-
Notifications
You must be signed in to change notification settings - Fork 199
/
Copy pathposition_manager.rs
167 lines (152 loc) · 5.49 KB
/
position_manager.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use crate::{
cli::Args,
utils::{
display_position_balances, display_wallet_balances, fetch_position, fetch_whirlpool,
send_transaction,
},
};
use colored::Colorize;
use orca_whirlpools::{
close_position_instructions, open_position_instructions, IncreaseLiquidityParam,
};
use orca_whirlpools_client::{get_position_address, Position};
use orca_whirlpools_core::{sqrt_price_to_price, tick_index_to_price, tick_index_to_sqrt_price};
use solana_client::nonblocking::rpc_client::RpcClient;
use solana_sdk::signer::Signer;
use spl_token_2022::state::Mint;
pub async fn run_position_manager(
rpc: &RpcClient,
args: &Args,
wallet: &Box<dyn Signer>,
position: &mut Position,
token_mint_a: &Mint,
token_mint_b: &Mint,
) -> Result<(), Box<dyn std::error::Error>> {
println!("Checking position.");
let whirlpool_address = position.whirlpool;
let whirlpool = fetch_whirlpool(rpc, &whirlpool_address)
.await
.map_err(|_| "Failed to fetch Whirlpool data.")?;
let current_price = sqrt_price_to_price(
whirlpool.sqrt_price,
token_mint_a.decimals,
token_mint_b.decimals,
);
let position_lower_price = tick_index_to_price(
position.tick_lower_index,
token_mint_a.decimals,
token_mint_b.decimals,
);
let position_upper_price = tick_index_to_price(
position.tick_upper_index,
token_mint_a.decimals,
token_mint_b.decimals,
);
let position_center_price = (position_lower_price + position_upper_price) / 2.0;
let deviation_amount = if current_price > position_center_price {
current_price - position_center_price
} else {
position_center_price - current_price
};
let deviation_bps = (deviation_amount * 10000.0) / (position_center_price);
println!("Current pool price: {:.6}", current_price);
println!(
"Position price range: [{:.6}, {:.6}]",
position_lower_price, position_upper_price
);
println!("Position center price: {:.6}", position_center_price);
println!("Price deviation from center: {:.2} bps", deviation_bps);
if deviation_bps as u16 >= args.threshold {
println!(
"{}",
"Deviation exceeds threshold. Rebalancing position."
.to_string()
.yellow()
);
let close_position_instructions = close_position_instructions(
rpc,
position.position_mint,
Some(args.slippage_tolerance_bps),
None,
)
.await
.map_err(|_| "Failed to generate close position instructions.")?;
let new_lower_price = current_price - (position_upper_price - position_lower_price) / 2.0;
let new_upper_price = current_price + (position_upper_price - position_lower_price) / 2.0;
let increase_liquidity_param =
IncreaseLiquidityParam::Liquidity(close_position_instructions.quote.liquidity_delta);
let open_position_instructions = open_position_instructions(
rpc,
whirlpool_address,
new_lower_price,
new_upper_price,
increase_liquidity_param,
Some(100),
None,
)
.await
.map_err(|_| "Failed to generate open position instructions.")?;
let mut all_instructions = vec![];
all_instructions.extend(close_position_instructions.instructions);
all_instructions.extend(open_position_instructions.instructions);
let mut signers: Vec<&dyn Signer> = vec![wallet.as_ref()];
signers.extend(
open_position_instructions
.additional_signers
.iter()
.map(|kp| kp as &dyn Signer),
);
signers.extend(
close_position_instructions
.additional_signers
.iter()
.map(|kp| kp as &dyn Signer),
);
let signature = send_transaction(
rpc,
wallet.as_ref(),
&whirlpool_address,
all_instructions,
signers,
args.priority_fee_tier,
args.max_priority_fee_lamports,
)
.await
.map_err(|_| "Failed to send rebalancing transaction.")?;
println!("Rebalancing transaction signature: {}", signature);
let position_mint_address = open_position_instructions.position_mint;
println!("New position mint address: {}", position_mint_address);
let (position_address, _) = get_position_address(&position_mint_address)
.map_err(|_| "Failed to derive new position address.")?;
*position = fetch_position(rpc, &position_address)
.await
.map_err(|_| "Failed to fetch new position data.")?;
display_wallet_balances(
rpc,
&wallet.pubkey(),
&whirlpool.token_mint_a,
&whirlpool.token_mint_b,
)
.await
.map_err(|_| "Failed to display wallet balances.")?;
display_position_balances(
rpc,
position,
&whirlpool.token_mint_a,
&whirlpool.token_mint_b,
token_mint_a.decimals,
token_mint_b.decimals,
args.slippage_tolerance_bps,
)
.await
.map_err(|_| "Failed to display position balances.")?;
} else {
println!(
"{}",
"Current price is within range. No repositioning needed."
.to_string()
.green()
);
}
Ok(())
}