refactor: restructure trading modules and add price calculation utilities
- Remove redundant pool.rs files from trading protocols - Consolidate protocol logic into common.rs files - Add comprehensive price calculation utilities for all protocols - Add decimals constants module - Restructure utils module for better organization - Update documentation with price utilities information Breaking changes: - Removed bonding_curve.rs from pumpfun module - Consolidated trading logic across protocols - Refactored utils module structure
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
pub mod price;
|
||||
|
||||
use crate::solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::PumpFunTradeEvent;
|
||||
use crate::trading;
|
||||
use crate::SolanaTrade;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::signature::Keypair;
|
||||
use solana_sdk::signer::Signer;
|
||||
|
||||
impl SolanaTrade {
|
||||
#[inline]
|
||||
pub async fn get_sol_balance(&self, payer: &Pubkey) -> Result<u64, anyhow::Error> {
|
||||
trading::common::utils::get_sol_balance(&self.rpc, payer).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_payer_sol_balance(&self) -> Result<u64, anyhow::Error> {
|
||||
trading::common::utils::get_sol_balance(&self.rpc, &self.payer.pubkey()).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_token_balance(
|
||||
&self,
|
||||
payer: &Pubkey,
|
||||
mint: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
trading::common::utils::get_token_balance(&self.rpc, payer, mint).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_payer_token_balance(&self, mint: &Pubkey) -> Result<u64, anyhow::Error> {
|
||||
trading::common::utils::get_token_balance(&self.rpc, &self.payer.pubkey(), mint).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_payer_pubkey(&self) -> Pubkey {
|
||||
self.payer.pubkey()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_payer(&self) -> &Keypair {
|
||||
self.payer.as_ref()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn transfer_sol(
|
||||
&self,
|
||||
payer: &Keypair,
|
||||
receive_wallet: &Pubkey,
|
||||
amount: u64,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
trading::common::utils::transfer_sol(&self.rpc, payer, receive_wallet, amount).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn close_token_account(&self, mint: &Pubkey) -> Result<(), anyhow::Error> {
|
||||
trading::common::utils::close_token_account(&self.rpc, self.payer.as_ref(), mint).await
|
||||
}
|
||||
|
||||
// -------------------------------- PumpFun --------------------------------
|
||||
|
||||
#[inline]
|
||||
pub fn get_pumpfun_token_buy_price(&self, amount: u64, trade_info: &PumpFunTradeEvent) -> u64 {
|
||||
trading::pumpfun::common::get_buy_price(amount, trade_info)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_pumpfun_token_current_price(
|
||||
&self,
|
||||
mint: &Pubkey,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let (bonding_curve, _) =
|
||||
trading::pumpfun::common::fetch_bonding_curve_account(&self.rpc, mint).await?;
|
||||
|
||||
let virtual_sol_reserves = bonding_curve.virtual_sol_reserves;
|
||||
let virtual_token_reserves = bonding_curve.virtual_token_reserves;
|
||||
|
||||
Ok(price::pumpfun::price_token_in_sol(virtual_sol_reserves, virtual_token_reserves))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_pumpfun_token_real_sol_reserves(
|
||||
&self,
|
||||
mint: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let (bonding_curve, _) =
|
||||
trading::pumpfun::common::fetch_bonding_curve_account(&self.rpc, mint).await?;
|
||||
|
||||
let actual_sol_reserves = bonding_curve.real_sol_reserves;
|
||||
|
||||
Ok(actual_sol_reserves)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_pumpfun_token_creator(&self, mint: &Pubkey) -> Result<Pubkey, anyhow::Error> {
|
||||
let (bonding_curve, _) =
|
||||
trading::pumpfun::common::fetch_bonding_curve_account(&self.rpc, mint).await?;
|
||||
|
||||
let creator = bonding_curve.creator;
|
||||
|
||||
Ok(creator)
|
||||
}
|
||||
|
||||
// -------------------------------- PumpSwap --------------------------------
|
||||
|
||||
#[inline]
|
||||
pub async fn get_pumpswap_token_current_price(
|
||||
&self,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<f64, anyhow::Error> {
|
||||
let pool = trading::pumpswap::common::fetch_pool(&self.rpc, pool_address).await?;
|
||||
|
||||
let (base_amount, quote_amount) =
|
||||
trading::pumpswap::common::get_token_balances(&pool, &self.rpc).await?;
|
||||
|
||||
// Calculate price using constant product formula (x * y = k)
|
||||
// Price = quote_amount / base_amount
|
||||
if base_amount == 0 {
|
||||
return Err(anyhow::anyhow!("Base amount is zero, cannot calculate price"));
|
||||
}
|
||||
|
||||
let price = quote_amount as f64 / base_amount as f64;
|
||||
|
||||
Ok(price)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_pumpswap_token_real_sol_reserves(
|
||||
&self,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let pool = trading::pumpswap::common::fetch_pool(&self.rpc, pool_address).await?;
|
||||
|
||||
let (_, quote_amount) =
|
||||
trading::pumpswap::common::get_token_balances(&pool, &self.rpc).await?;
|
||||
|
||||
Ok(quote_amount)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub async fn get_pumpswap_payer_token_balance(
|
||||
&self,
|
||||
pool_address: &Pubkey,
|
||||
) -> Result<u64, anyhow::Error> {
|
||||
let pool = trading::pumpswap::common::fetch_pool(&self.rpc, pool_address).await?;
|
||||
|
||||
let (base_amount, _) =
|
||||
trading::pumpswap::common::get_token_balances(&pool, &self.rpc).await?;
|
||||
|
||||
Ok(base_amount)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::bonk::types::PoolState;
|
||||
|
||||
use crate::constants::{
|
||||
bonk::accounts::WSOL_TOKEN_ACCOUNT,
|
||||
decimals::{DEFAULT_TOKEN_DECIMALS, SOL_DECIMALS},
|
||||
};
|
||||
|
||||
/// Calculate the token price in WSOL based on pool state
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `pool_state` - Pool state
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in WSOL as f64
|
||||
pub fn price_token_in_wsol_with_pool_state(pool_state: &PoolState) -> f64 {
|
||||
if pool_state.quote_mint != WSOL_TOKEN_ACCOUNT {
|
||||
log::error!("Quote mint is not WSOL: {:?}", pool_state.quote_mint);
|
||||
return 0.0;
|
||||
}
|
||||
price_base_in_quote(
|
||||
pool_state.virtual_base,
|
||||
pool_state.virtual_quote,
|
||||
pool_state.real_base,
|
||||
pool_state.real_quote,
|
||||
pool_state.base_decimals,
|
||||
pool_state.quote_decimals,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the price of base in quote based on pool state
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `pool_state` - Pool state
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of base in quote
|
||||
pub fn price_base_in_quote_with_pool_state(pool_state: &PoolState) -> f64 {
|
||||
price_base_in_quote(
|
||||
pool_state.virtual_base,
|
||||
pool_state.virtual_quote,
|
||||
pool_state.real_base,
|
||||
pool_state.real_quote,
|
||||
pool_state.base_decimals,
|
||||
pool_state.quote_decimals,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the price of token in WSOL
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `virtual_base` - Virtual base reserves
|
||||
/// * `virtual_quote` - Virtual quote reserves
|
||||
/// * `real_base` - Real base reserves
|
||||
/// * `real_quote` - Real quote reserves
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of token in WSOL
|
||||
pub fn price_token_in_wsol(
|
||||
virtual_base: u64,
|
||||
virtual_quote: u64,
|
||||
real_base: u64,
|
||||
real_quote: u64,
|
||||
) -> f64 {
|
||||
price_base_in_quote(
|
||||
virtual_base,
|
||||
virtual_quote,
|
||||
real_base,
|
||||
real_quote,
|
||||
DEFAULT_TOKEN_DECIMALS,
|
||||
SOL_DECIMALS,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the price of base in quote
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `virtual_base` - Virtual base reserves
|
||||
/// * `virtual_quote` - Virtual quote reserves
|
||||
/// * `real_base` - Real base reserves
|
||||
/// * `real_quote` - Real quote reserves
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of base in quote
|
||||
pub fn price_base_in_quote(
|
||||
virtual_base: u64,
|
||||
virtual_quote: u64,
|
||||
real_base: u64,
|
||||
real_quote: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
// Calculate decimal places difference
|
||||
let decimal_diff = quote_decimals as i32 - base_decimals as i32;
|
||||
let decimal_factor = if decimal_diff >= 0 {
|
||||
10_f64.powi(decimal_diff)
|
||||
} else {
|
||||
1.0 / 10_f64.powi(-decimal_diff)
|
||||
};
|
||||
// Calculate reserves state before price calculation
|
||||
let quote_reserves = virtual_quote.checked_add(real_quote).unwrap_or(0);
|
||||
let base_reserves = virtual_base.checked_sub(real_base).unwrap_or(0);
|
||||
|
||||
if base_reserves == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if decimal_factor == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Use floating point calculation to avoid precision loss from integer division
|
||||
let price = (quote_reserves as f64) / (base_reserves as f64) / decimal_factor;
|
||||
|
||||
price
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/// Calculate the token price in quote based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in quote as f64
|
||||
pub fn price_base_in_quote(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
let base = base_reserve as f64 / 10f64.powi(base_decimals as i32);
|
||||
let quote = quote_reserve as f64 / 10f64.powi(quote_decimals as i32);
|
||||
if base == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
quote / base
|
||||
}
|
||||
|
||||
/// Calculate the token price in base based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in base as f64
|
||||
pub fn price_quote_in_base(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
let base = base_reserve as f64 / 10f64.powi(base_decimals as i32);
|
||||
let quote = quote_reserve as f64 / 10f64.powi(quote_decimals as i32);
|
||||
if quote == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
base / quote
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
pub mod bonk;
|
||||
pub mod pumpfun;
|
||||
pub mod pumpswap;
|
||||
pub mod raydium_amm_v4;
|
||||
pub mod raydium_clmm;
|
||||
pub mod raydium_cpmm;
|
||||
pub mod common;
|
||||
@@ -0,0 +1,31 @@
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::pumpfun::types::BondingCurve;
|
||||
|
||||
use crate::constants::pumpfun::global_constants::{LAMPORTS_PER_SOL, SCALE};
|
||||
|
||||
/// Calculate the token price in SOL based on virtual reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `bonding_curve` - Bonding curve account
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in SOL as f64
|
||||
pub fn price_token_in_sol_with_bonding_curve(bonding_curve: &BondingCurve) -> f64 {
|
||||
price_token_in_sol(bonding_curve.virtual_sol_reserves, bonding_curve.virtual_token_reserves)
|
||||
}
|
||||
|
||||
/// Calculate the token price in SOL based on virtual reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `virtual_sol_reserves` - Virtual SOL reserves in the bonding curve
|
||||
/// * `virtual_token_reserves` - Virtual token reserves in the bonding curve
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in SOL as f64
|
||||
pub fn price_token_in_sol(virtual_sol_reserves: u64, virtual_token_reserves: u64) -> f64 {
|
||||
let v_sol = virtual_sol_reserves as f64 / LAMPORTS_PER_SOL as f64;
|
||||
let v_tokens = virtual_token_reserves as f64 / SCALE as f64;
|
||||
if v_tokens == 0.0 {
|
||||
return 0.0;
|
||||
}
|
||||
v_sol / v_tokens
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/// Calculate the token price in quote based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in quote as f64
|
||||
pub fn price_base_in_quote(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
crate::utils::price::common::price_base_in_quote(
|
||||
base_reserve,
|
||||
quote_reserve,
|
||||
base_decimals,
|
||||
quote_decimals,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the token price in base based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in base as f64
|
||||
pub fn price_quote_in_base(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
crate::utils::price::common::price_quote_in_base(
|
||||
base_reserve,
|
||||
quote_reserve,
|
||||
base_decimals,
|
||||
quote_decimals,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/// Calculate the token price in quote based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in quote as f64
|
||||
pub fn price_base_in_quote(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
crate::utils::price::common::price_base_in_quote(
|
||||
base_reserve,
|
||||
quote_reserve,
|
||||
base_decimals,
|
||||
quote_decimals,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the token price in base based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in base as f64
|
||||
pub fn price_quote_in_base(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
crate::utils::price::common::price_quote_in_base(
|
||||
base_reserve,
|
||||
quote_reserve,
|
||||
base_decimals,
|
||||
quote_decimals,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use solana_streamer_sdk::streaming::event_parser::protocols::raydium_clmm::types::PoolState;
|
||||
|
||||
/// Calculate the price of token0 in token1
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `sqrt_price_x64` - The sqrt price of the pool
|
||||
/// * `decimals_token0` - The decimals of token0
|
||||
/// * `decimals_token1` - The decimals of token1
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of token0 in token1
|
||||
pub fn price_token0_in_token1(
|
||||
sqrt_price_x64: u128,
|
||||
decimals_token0: u8,
|
||||
decimals_token1: u8,
|
||||
) -> f64 {
|
||||
let sqrt_price = sqrt_price_x64 as f64 / (1u128 << 64) as f64; // Q64.64 转浮点
|
||||
let price_raw = sqrt_price * sqrt_price; // 未调整小数位的价格
|
||||
let scale = 10f64.powi((decimals_token0 as i32) - (decimals_token1 as i32));
|
||||
price_raw * scale
|
||||
}
|
||||
|
||||
/// Calculate the price of token1 in token0
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `sqrt_price_x64` - The sqrt price of the pool
|
||||
/// * `decimals_token0` - The decimals of token0
|
||||
/// * `decimals_token1` - The decimals of token1
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of token1 in token0
|
||||
pub fn price_token1_in_token0(
|
||||
sqrt_price_x64: u128,
|
||||
decimals_token0: u8,
|
||||
decimals_token1: u8,
|
||||
) -> f64 {
|
||||
1.0 / price_token0_in_token1(sqrt_price_x64, decimals_token0, decimals_token1)
|
||||
}
|
||||
|
||||
/// Calculate the price of token0 in token1 based on pool state
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `pool_state` - The pool state
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of token0 in token1
|
||||
pub fn price_token0_in_token1_with_pool_state(pool_state: &PoolState) -> f64 {
|
||||
price_token0_in_token1(
|
||||
pool_state.sqrt_price_x64,
|
||||
pool_state.mint_decimals0,
|
||||
pool_state.mint_decimals1,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the price of token1 in token0 based on pool state
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `pool_state` - The pool state
|
||||
///
|
||||
/// # Returns
|
||||
/// The price of token1 in token0
|
||||
pub fn price_token1_in_token0_with_pool_state(pool_state: &PoolState) -> f64 {
|
||||
price_token1_in_token0(
|
||||
pool_state.sqrt_price_x64,
|
||||
pool_state.mint_decimals0,
|
||||
pool_state.mint_decimals1,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/// Calculate the token price in quote based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in quote as f64
|
||||
pub fn price_base_in_quote(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
crate::utils::price::common::price_base_in_quote(
|
||||
base_reserve,
|
||||
quote_reserve,
|
||||
base_decimals,
|
||||
quote_decimals,
|
||||
)
|
||||
}
|
||||
|
||||
/// Calculate the token price in base based on base and quote reserves
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `base_reserve` - Base reserve in the pool
|
||||
/// * `quote_reserve` - Quote reserve in the pool
|
||||
/// * `base_decimals` - Base decimals
|
||||
/// * `quote_decimals` - Quote decimals
|
||||
///
|
||||
/// # Returns
|
||||
/// Token price in base as f64
|
||||
pub fn price_quote_in_base(
|
||||
base_reserve: u64,
|
||||
quote_reserve: u64,
|
||||
base_decimals: u8,
|
||||
quote_decimals: u8,
|
||||
) -> f64 {
|
||||
crate::utils::price::common::price_quote_in_base(
|
||||
base_reserve,
|
||||
quote_reserve,
|
||||
base_decimals,
|
||||
quote_decimals,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user