1
0
mirror of https://github.com/payden/libwsclient synced 2025-06-13 12:53:52 +00:00
payden_libwsclient/strategies/copy_trade_strategy.rs
2025-05-16 15:09:24 +09:00

153 lines
5.1 KiB
Rust

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(())
}