1
0
mirror of https://github.com/payden/libwsclient synced 2026-03-02 04:09:18 +00:00

feat : add strategy

This commit is contained in:
deniyuda348
2025-05-16 15:09:24 +09:00
parent d416fcb62c
commit 17c1e2d5e0
81 changed files with 7848 additions and 2858 deletions

17
strategies/arbitrage.rs Normal file
View File

@@ -0,0 +1,17 @@
use crate::models::market_conditions::MarketConditions;
use crate::models::mev_opportunity::MevOpportunity;
use crate::strategies::strategy::Strategy;
use async_trait::async_trait;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
pub struct ArbitrageStrategy {}
#[async_trait]
impl Strategy for ArbitrageStrategy {
fn update(&mut self, _market_conditions: &MarketConditions) {}
async fn find_opportunities(&self, _target_accounts: &HashMap<Pubkey, crate::AccountInfo>) -> Vec<MevOpportunity> {
Vec::new()
}
}

View File

@@ -0,0 +1,117 @@
use crate::dex::dex_manager::DexManager;
use crate::models::market_conditions::MarketConditions;
use crate::models::arbitrage_opportunity::ArbitrageOpportunity;
use crate::strategies::strategy::Strategy;
use crate::utils::math;
use async_trait::async_trait;
use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
pub struct ArbitrageStrategy {
pub rpc_client: RpcClient,
pub dex_manager: DexManager,
pub min_profit_threshold: f64,
}
impl ArbitrageStrategy {
pub fn new(rpc_client: RpcClient, dex_manager: DexManager, min_profit_threshold: f64) -> Self {
ArbitrageStrategy {
rpc_client,
dex_manager,
min_profit_threshold,
}
}
pub async fn find_arbitrage_opportunities(&self, market_conditions: &MarketConditions) -> Vec<ArbitrageOpportunity> {
let mut opportunities = Vec::new();
let token_prices = market_conditions.token_prices.clone();
let token_pairs = self.generate_token_pairs(&token_prices);
for (token_a, token_b) in token_pairs {
if let Some(opportunity) = self.find_arbitrage_opportunity(token_a, token_b, &token_prices).await {
if opportunity.expected_profit >= self.min_profit_threshold {
opportunities.push(opportunity);
}
}
}
opportunities
}
async fn find_arbitrage_opportunity(&self, token_a: &str, token_b: &str, token_prices: &HashMap<String, f64>) -> Option<ArbitrageOpportunity> {
let mut best_opportunity: Option<ArbitrageOpportunity> = None;
if let Some(price_a_b) = token_prices.get(&format!("{}/{}", token_a, token_b)) {
if let Some(price_b_a) = token_prices.get(&format!("{}/{}", token_b, token_a)) {
let forward_amount = 1.0;
let forward_price = price_a_b;
let backward_amount = forward_amount * forward_price;
let backward_price = price_b_a;
let forward_trade = self.dex_manager.get_best_trade_route(token_a, token_b, forward_amount).await;
let backward_trade = self.dex_manager.get_best_trade_route(token_b, token_a, backward_amount).await;
if let (Some(forward_trade), Some(backward_trade)) = (forward_trade, backward_trade) {
let forward_amount_received = math::checked_div(forward_amount, forward_trade.price).unwrap_or(0.0);
let backward_amount_received = math::checked_mul(backward_trade.received_amount, backward_price).unwrap_or(0.0);
let expected_profit = backward_amount_received - forward_amount;
if expected_profit > 0.0 {
best_opportunity = Some(ArbitrageOpportunity {
token_a: token_a.to_string(),
token_b: token_b.to_string(),
forward_trade,
backward_trade,
expected_profit,
});
}
}
}
}
best_opportunity
}
fn generate_token_pairs(&self, token_prices: &HashMap<String, f64>) -> Vec<(String, String)> {
let mut pairs = Vec::new();
for (token_a, _) in token_prices {
for (token_b, _) in token_prices {
if token_a != token_b {
pairs.push((token_a.clone(), token_b.clone()));
}
}
}
pairs
}
}
#[async_trait]
impl Strategy for ArbitrageStrategy {
async fn find_opportunities(&self, market_conditions: &MarketConditions) -> Vec<ArbitrageOpportunity> {
self.find_arbitrage_opportunities(market_conditions).await
}
async fn execute_opportunities(&self, opportunities: &[ArbitrageOpportunity]) {
for opportunity in opportunities {
let forward_trade = &opportunity.forward_trade;
let backward_trade = &opportunity.backward_trade;
let forward_result = self.dex_manager.execute_trade(forward_trade).await;
if forward_result.is_ok() {
let backward_result = self.dex_manager.execute_trade(backward_trade).await;
if backward_result.is_ok() {
// Log successful arbitrage execution
} else {
// Log backward trade failure
}
} else {
// Log forward trade failure
}
}
}
}

View File

@@ -0,0 +1,153 @@
use crate::dex::dex_manager::DexManager;
use crate::error::Result;
use crate::models::copy_trade_opportunity::CopyTradeOpportunity;
use crate::models::market::Market;
use crate::models::order::Order;
use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct CopyTradeStrategy {
pub rpc_client: Arc<RpcClient>,
pub dex_manager: Arc<Mutex<DexManager>>,
pub tracked_traders: Vec<Pubkey>,
pub trade_threshold: f64,
pub max_trade_amount: f64,
}
impl CopyTradeStrategy {
pub fn new(
rpc_client: Arc<RpcClient>,
dex_manager: Arc<Mutex<DexManager>>,
tracked_traders: Vec<Pubkey>,
trade_threshold: f64,
max_trade_amount: f64,
) -> Self {
CopyTradeStrategy {
rpc_client,
dex_manager,
tracked_traders,
trade_threshold,
max_trade_amount,
}
}
pub async fn run(&self) -> Result<()> {
loop {
let opportunities = self.find_opportunities().await?;
for opportunity in opportunities {
self.execute_copy_trade(&opportunity).await?;
}
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
}
async fn find_opportunities(&self) -> Result<Vec<CopyTradeOpportunity>> {
let mut opportunities = Vec::new();
for trader in &self.tracked_traders {
let trades = self.get_recent_trades(trader).await?;
for trade in trades {
if trade.quantity >= self.trade_threshold && trade.quantity <= self.max_trade_amount {
let market = self.dex_manager.lock().await.get_market(&trade.market).await?;
let opportunity = CopyTradeOpportunity {
trader: *trader,
market,
trade,
};
opportunities.push(opportunity);
}
}
}
Ok(opportunities)
}
async fn get_recent_trades(&self, trader: &Pubkey) -> Result<Vec<Order>> {
let signature_infos = self.rpc_client.get_signatures_for_address(trader)?;
let mut trades = Vec::new();
for signature_info in signature_infos {
if let Some(signature) = signature_info.signature {
let transaction = self.rpc_client.get_transaction(&signature)?;
if let Some(transaction) = transaction {
for instruction in transaction.transaction.message.instructions {
if let Some(dex_instruction) = DexInstruction::unpack(instruction) {
match dex_instruction {
DexInstruction::NewOrder { .. } => {
let order = self.parse_order(&transaction, &instruction)?;
trades.push(order);
}
_ => {}
}
}
}
}
}
}
Ok(trades)
}
fn parse_order(&self, transaction: &TransactionInfo, instruction: &CompiledInstruction) -> Result<Order> {
let market_address = instruction.accounts[0];
let market = self.dex_manager.lock().await.get_market(&market_address).await?;
let side = match instruction.data[0] {
0 => OrderSide::Bid,
1 => OrderSide::Ask,
_ => return Err(anyhow!("Invalid order side")),
};
let order_type = match instruction.data[1] {
0 => OrderType::Limit,
1 => OrderType::ImmediateOrCancel,
2 => OrderType::PostOnly,
_ => return Err(anyhow!("Invalid order type")),
};
let price = f64::from_le_bytes(instruction.data[2..10].try_into()?);
let quantity = f64::from_le_bytes(instruction.data[10..18].try_into()?);
Ok(Order {
id: transaction.transaction.signatures[0].to_string(),
market,
side,
order_type,
price,
quantity,
status: OrderStatus::Filled,
})
}
async fn execute_copy_trade(&self, opportunity: &CopyTradeOpportunity) -> Result<()> {
let market = &opportunity.market;
let trade = &opportunity.trade;
let order = self
.dex_manager
.lock()
.await
.place_order(market, trade.order_type, trade.side, trade
async fn execute_copy_trade(&self, opportunity: &CopyTradeOpportunity) -> Result<()> {
let market = &opportunity.market;
let trade = &opportunity.trade;
let order = self
.dex_manager
.lock()
.await
.place_order(market, trade.order_type, trade.side, trade.price, trade.quantity)
.await?;
println!("Placed copy trade order: {:?}", order);
Ok(())
}

17
strategies/liquidation.rs Normal file
View File

@@ -0,0 +1,17 @@
use crate::models::market_conditions::MarketConditions;
use crate::models::mev_opportunity::MevOpportunity;
use crate::strategies::strategy::Strategy;
use async_trait::async_trait;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
pub struct LiquidationStrategy {}
#[async_trait]
impl Strategy for LiquidationStrategy {
fn update(&mut self, _market_conditions: &MarketConditions) {}
async fn find_opportunities(&self, _target_accounts: &HashMap<Pubkey, crate::AccountInfo>) -> Vec<MevOpportunity> {
Vec::new()
}
}

4
strategies/mod.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod strategy;
pub mod arbitrage;
pub mod liquidation;
pub mod sandwich;

17
strategies/sandwich.rs Normal file
View File

@@ -0,0 +1,17 @@
use crate::models::market_conditions::MarketConditions;
use crate::models::mev_opportunity::MevOpportunity;
use crate::strategies::strategy::Strategy;
use async_trait::async_trait;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
pub struct SandwichStrategy {}
#[async_trait]
impl Strategy for SandwichStrategy {
fn update(&mut self, _market_conditions: &MarketConditions) {}
async fn find_opportunities(&self, _target_accounts: &HashMap<Pubkey, crate::AccountInfo>) -> Vec<MevOpportunity> {
Vec::new()
}
}

View File

@@ -0,0 +1,119 @@
use crate::dex::dex_manager::DexManager;
use crate::error::Result;
use crate::models::market::Market;
use crate::models::sniping_opportunity::SnipingOpportunity;
use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;
use std::sync::Arc;
use tokio::sync::Mutex;
pub struct SnipingStrategy {
pub rpc_client: Arc<RpcClient>,
pub dex_manager: Arc<Mutex<DexManager>>,
pub target_markets: Vec<Pubkey>,
pub max_price: f64,
pub min_liquidity: f64,
}
impl SnipingStrategy {
pub fn new(
rpc_client: Arc<RpcClient>,
dex_manager: Arc<Mutex<DexManager>>,
target_markets: Vec<Pubkey>,
max_price: f64,
min_liquidity: f64,
) -> Self {
SnipingStrategy {
rpc_client,
dex_manager,
target_markets,
max_price,
min_liquidity,
}
}
pub async fn run(&self) -> Result<()> {
loop {
let markets = self.get_target_markets().await?;
let opportunities = self.find_opportunities(&markets).await?;
for opportunity in opportunities {
self.execute_sniping(&opportunity).await?;
}
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
}
}
async fn get_target_markets(&self) -> Result<Vec<Market>> {
let mut markets = Vec::new();
for market_address in &self.target_markets {
let market = self.dex_manager.lock().await.get_market(market_address).await?;
markets.push(market);
}
Ok(markets)
}
async fn find_opportunities(&self, markets: &[Market]) -> Result<Vec<SnipingOpportunity>> {
let mut opportunities = Vec::new();
for market in markets {
let orderbook = self.dex_manager.lock().await.get_orderbook(market).await?;
if let Some(best_bid) = orderbook.bids.first() {
if best_bid.price <= self.max_price {
let liquidity = self.calculate_liquidity(market, &orderbook).await?;
if liquidity >= self.min_liquidity {
let opportunity = SnipingOpportunity {
market: market.clone(),
price: best_bid.price,
liquidity,
};
opportunities.push(opportunity);
}
}
}
}
Ok(opportunities)
}
async fn calculate_liquidity(&self, market: &Market, orderbook: &Orderbook) -> Result<f64> {
let bids_volume = orderbook.bids.iter().map(|order| order.quantity).sum();
let asks_volume = orderbook.asks.iter().map(|order| order.quantity).sum();
let mid_price = (orderbook.bids[0].price + orderbook.asks[0].price) / 2.0;
let base_volume = bids_volume + asks_volume;
let quote_volume = base_volume * mid_price;
let base_decimals = market.base_decimals;
let quote_decimals = market.quote_decimals;
let liquidity = base_volume / 10_usize.pow(base_decimals as u32) as f64
+ quote_volume / 10_usize.pow(quote_decimals as u32) as f64;
Ok(liquidity)
}
async fn execute_sniping(&self, opportunity: &SnipingOpportunity) -> Result<()> {
let market = &opportunity.market;
let price = opportunity.price;
let quantity = opportunity.liquidity / price;
let order = self
.dex_manager
.lock()
.await
.place_order(market, OrderType::Limit, OrderSide::Bid, price, quantity)
.await?;
println!("Placed sniping order: {:?}", order);
Ok(())
}
}

11
strategies/strategy.rs Normal file
View File

@@ -0,0 +1,11 @@
use crate::models::market_conditions::MarketConditions;
use crate::models::mev_opportunity::MevOpportunity;
use async_trait::async_trait;
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;
#[async_trait]
pub trait Strategy {
fn update(&mut self, market_conditions: &MarketConditions);
async fn find_opportunities(&self, target_accounts: &HashMap<Pubkey, crate::AccountInfo>) -> Vec<MevOpportunity>;
}