copy_trading/position/close.rs
1use crate::types::position::Position;
2use crate::types::position_close_reason::PositionCloseReason;
3use crate::types::position_manager::PositionManager;
4use crate::types::position_status::PositionStatus;
5use solana_central::SwapDirection;
6use solana_central::constants::TOKENS;
7use solana_exec::SwapConfig;
8use solana_exec::create_tx_from_swap_configs;
9// use solana_client::rpc_config::RpcSendTransactionConfig;
10use solana_sdk::signature::Signer;
11use std::sync::{Arc, Mutex};
12use std::time::{SystemTime, UNIX_EPOCH};
13
14impl Position {
15 /// Close a position by creating and sending a sell transaction to the blockchain. Handles both
16 /// stop-loss and hold-time closures. Creates a sell swap transaction and sends it via Jito
17 /// bundles, retrying until confirmation is received.
18 pub fn close_position(
19 position: Arc<Mutex<Position>>,
20 reason: PositionCloseReason,
21 position_manager: Arc<PositionManager>,
22 ) {
23 let (swap_config, jito_tip_lp, wallet_keypair) = {
24 let mut position = position.lock().unwrap();
25 if position.status != PositionStatus::Open {
26 println!(
27 "POSITION: Attempted to close position not in Open state. Market: {}, Status: {:?}",
28 position.pool_address, position.status
29 );
30 return;
31 }
32
33 // Update status based on close reason
34 position.status = PositionStatus::Closing;
35 position.close_reason = reason.clone();
36
37 position.close_time = SystemTime::now()
38 .duration_since(UNIX_EPOCH)
39 .unwrap()
40 .as_millis() as u64;
41
42 // Determine sell direction (opposite of buy)
43 let sell_direction;
44 if *position.pool.read().unwrap().token_b_address() == TOKENS.wsol {
45 sell_direction = SwapDirection::AToB; // Sell token for SOL
46 } else {
47 sell_direction = SwapDirection::BToA; // Sell token for SOL
48 }
49
50 // Create sell swap config
51 // Note: amount_in should be the amount of tokens we want to sell
52 // For now, we'll use a placeholder amount - this should be calculated based on
53 // actual token balance
54 (
55 SwapConfig {
56 pool: position.pool.clone(),
57 direction: sell_direction,
58 amount_in: position.tokens_purchased,
59 slippage_lp: position.config.slippage_lps,
60 open_token_account: false,
61 close_token_account: true, // Close token account after selling
62 wallet: position.wallet.pubkey(),
63 },
64 position.config.jito_tip_sell_lp,
65 position.wallet.clone(),
66 )
67 // println!("Swap direction: {:?}", sell_swap_config.direction);
68 };
69
70 let e = position_manager.execution_context.clone();
71 // Resell logic runs on a separate thread
72 tokio::spawn(async move {
73 /*
74 We're going to keep retrying as long as the position is not closed out completely. The status
75 was set to closing by the initial call of this function.
76 */
77 while position.lock().unwrap().status == PositionStatus::Closing {
78 // Make a fresh tx from newest market state, newest block hash
79 let tx = create_tx_from_swap_configs(
80 &vec![swap_config.clone()],
81 &position_manager.central_context,
82 true,
83 jito_tip_lp,
84 &wallet_keypair,
85 );
86
87 // Send tx to Jito
88 e.jito_be_client.send_bundle(vec![tx]).await.unwrap();
89
90 // position_manager
91 // .central_context
92 // .json_rpc_client
93 // .send_transaction_with_config(
94 // &tx,
95 // RpcSendTransactionConfig {
96 // skip_preflight: true,
97 // preflight_commitment: Some(solana_sdk::commitment_config::CommitmentLevel::Confirmed),
98 // ..RpcSendTransactionConfig::default()
99 // },
100 // )
101 // .unwrap();
102 // println!(
103 // "POSITION: Sell transaction sent to Jito for token: {}. Reason: {:?}",
104 // token_address, reason
105 // );
106 /*
107 Sleep for 5 seconds to allow enough time for tx to land. When the tx lands on chain, the
108 process sell tx confirmation will pick it up and switch the position status to closed and
109 remove it from the map of open positions. When this loop rechecks it, it will say closed
110 and the loop will end the position will be cleared up.
111 */
112 tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
113 }
114 });
115 }
116}