use crate::errors::LBError;
use crate::math::safe_math::SafeMath;
use crate::math::weight_to_amounts::{to_amount_ask_side, to_amount_bid_side, to_amount_both_side};
use crate::ModifyLiquidity;
use anchor_lang::prelude::*;
const DEFAULT_MIN_WEIGHT: u16 = 200;
const DEFAULT_MAX_WEIGHT: u16 = 2000;
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Debug, Default)]
pub struct LiquidityParameterByStrategy {
pub amount_x: u64,
pub amount_y: u64,
pub active_id: i32,
pub max_active_bin_slippage: i32,
pub strategy_parameters: StrategyParameters,
}
impl LiquidityParameterByStrategy {
pub fn to_amounts_into_bin(
&self,
active_id: i32,
bin_step: u16,
amount_x_in_active_bin: u64,
amount_y_in_active_bin: u64,
) -> Result<Vec<(i32, u64, u64)>> {
let min_bin_id = self.strategy_parameters.min_bin_id;
let max_bin_id = self.strategy_parameters.max_bin_id;
match self.strategy_parameters.strategy_type {
StrategyType::SpotImBalanced => {
let mut amounts_in_bin = vec![];
if min_bin_id <= active_id {
let weights = to_weight_spot_balanced(min_bin_id, active_id);
let amounts_into_bid_side =
to_amount_bid_side(active_id, self.amount_y, &weights)?;
for &(bin_id, amount) in amounts_into_bid_side.iter() {
amounts_in_bin.push((bin_id, 0, amount))
}
}
if active_id < max_bin_id {
let weights = to_weight_spot_balanced(active_id + 1, max_bin_id);
let amounts_into_ask_side =
to_amount_ask_side(active_id, self.amount_x, bin_step, &weights)?;
for &(bin_id, amount) in amounts_into_ask_side.iter() {
amounts_in_bin.push((bin_id, amount, 0))
}
}
Ok(amounts_in_bin)
}
StrategyType::CurveImBalanced => {
let mut amounts_in_bin = vec![];
if min_bin_id <= active_id {
let weights = to_weight_ascending_order(min_bin_id, active_id);
let amounts_into_bid_side =
to_amount_bid_side(active_id, self.amount_y, &weights)?;
for &(bin_id, amount) in amounts_into_bid_side.iter() {
amounts_in_bin.push((bin_id, 0, amount))
}
}
if active_id < max_bin_id {
let weights = to_weight_descending_order(active_id + 1, max_bin_id);
let amounts_into_ask_side =
to_amount_ask_side(active_id, self.amount_x, bin_step, &weights)?;
for &(bin_id, amount) in amounts_into_ask_side.iter() {
amounts_in_bin.push((bin_id, amount, 0))
}
}
Ok(amounts_in_bin)
}
StrategyType::BidAskImBalanced => {
let mut amounts_in_bin = vec![];
if min_bin_id <= active_id {
let weights = to_weight_descending_order(min_bin_id, active_id);
let amounts_into_bid_side =
to_amount_bid_side(active_id, self.amount_y, &weights)?;
for &(bin_id, amount) in amounts_into_bid_side.iter() {
amounts_in_bin.push((bin_id, 0, amount))
}
}
if active_id < max_bin_id {
let weights = to_weight_ascending_order(active_id + 1, max_bin_id);
let amounts_into_ask_side =
to_amount_ask_side(active_id, self.amount_x, bin_step, &weights)?;
for &(bin_id, amount) in amounts_into_ask_side.iter() {
amounts_in_bin.push((bin_id, amount, 0))
}
}
Ok(amounts_in_bin)
}
StrategyType::SpotBalanced => {
let weights = to_weight_spot_balanced(min_bin_id, max_bin_id);
to_amount_both_side(
active_id,
bin_step,
amount_x_in_active_bin,
amount_y_in_active_bin,
self.amount_x,
self.amount_y,
&weights,
)
}
StrategyType::CurveBalanced => {
let weights = to_weight_curve(min_bin_id, max_bin_id, active_id)?;
to_amount_both_side(
active_id,
bin_step,
amount_x_in_active_bin,
amount_y_in_active_bin,
self.amount_x,
self.amount_y,
&weights,
)
}
StrategyType::BidAskBalanced => {
let weights = to_weight_bid_ask(min_bin_id, max_bin_id, active_id)?;
to_amount_both_side(
active_id,
bin_step,
amount_x_in_active_bin,
amount_y_in_active_bin,
self.amount_x,
self.amount_y,
&weights,
)
}
_ => Err(LBError::InvalidStrategyParameters.into()),
}
}
}
pub fn handle<'a, 'b, 'c, 'info>(
ctx: Context<'a, 'b, 'c, 'info, ModifyLiquidity<'info>>,
liquidity_parameter: &LiquidityParameterByStrategy,
) -> Result<()> {
Ok(())
}
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Debug)]
pub struct StrategyParameters {
pub min_bin_id: i32,
pub max_bin_id: i32,
pub strategy_type: StrategyType,
pub parameteres: [u8; 64],
}
#[derive(AnchorSerialize, AnchorDeserialize, Eq, PartialEq, Clone, Debug)]
pub enum StrategyType {
SpotOneSide,
CurveOneSide,
BidAskOneSide,
SpotBalanced,
CurveBalanced,
BidAskBalanced,
SpotImBalanced,
CurveImBalanced,
BidAskImBalanced,
}
pub fn to_weight_spot_balanced(min_bin_id: i32, max_bin_id: i32) -> Vec<(i32, u16)> {
let mut weights = vec![];
for i in min_bin_id..=max_bin_id {
weights.push((i, 1));
}
weights
}
pub fn to_weight_descending_order(min_bin_id: i32, max_bin_id: i32) -> Vec<(i32, u16)> {
let mut weights = vec![];
for i in min_bin_id..=max_bin_id {
weights.push((i, (max_bin_id - i + 1) as u16));
}
weights
}
pub fn to_weight_ascending_order(min_bin_id: i32, max_bin_id: i32) -> Vec<(i32, u16)> {
let mut weights = vec![];
for i in min_bin_id..=max_bin_id {
weights.push((i, (i - min_bin_id + 1) as u16));
}
weights
}
pub fn to_weight_curve(
min_bin_id: i32,
max_bin_id: i32,
active_id: i32,
) -> Result<Vec<(i32, u16)>> {
if active_id < min_bin_id || active_id > max_bin_id {
return Err(LBError::InvalidStrategyParameters.into());
}
let max_weight = DEFAULT_MAX_WEIGHT;
let min_weight = DEFAULT_MIN_WEIGHT;
let diff_weight = max_weight.safe_sub(min_weight)?;
let diff_min_weight = if active_id > min_bin_id {
diff_weight.safe_div(active_id.safe_sub(min_bin_id)? as u16)?
} else {
0
};
let diff_max_weight = if max_bin_id > active_id {
diff_weight.safe_div(max_bin_id.safe_sub(active_id)? as u16)?
} else {
0
};
let mut weights = vec![];
for i in min_bin_id..=max_bin_id {
if i < active_id {
let delta_bin = (active_id - i) as u16;
let weight = max_weight - delta_bin * diff_min_weight;
weights.push((i, weight));
} else if i > active_id {
let delta_bin = (i - active_id) as u16;
let weight = max_weight - delta_bin * diff_max_weight;
weights.push((i, weight));
} else {
weights.push((i, max_weight));
}
}
Ok(weights)
}
pub fn to_weight_bid_ask(
min_bin_id: i32,
max_bin_id: i32,
active_id: i32,
) -> Result<Vec<(i32, u16)>> {
if active_id < min_bin_id || active_id > max_bin_id {
return Err(LBError::InvalidStrategyParameters.into());
}
let max_weight = DEFAULT_MAX_WEIGHT;
let min_weight = DEFAULT_MIN_WEIGHT;
let diff_weight = max_weight.safe_sub(min_weight)?;
let diff_min_weight = if active_id > min_bin_id {
diff_weight.safe_div(active_id.safe_sub(min_bin_id)? as u16)?
} else {
0
};
let diff_max_weight = if max_bin_id > active_id {
diff_weight.safe_div(max_bin_id.safe_sub(active_id)? as u16)?
} else {
0
};
let mut weights = vec![];
for i in min_bin_id..=max_bin_id {
if i < active_id {
let delta_bin = (active_id - i) as u16;
let weight = min_weight + delta_bin * diff_min_weight;
weights.push((i, weight));
} else if i > active_id {
let delta_bin = (i - active_id) as u16;
let weight = min_weight + delta_bin * diff_max_weight;
weights.push((i, weight));
} else {
weights.push((i, min_weight));
}
}
Ok(weights)
}
impl StrategyParameters {
pub fn validate_both_side(&self, active_id: i32) -> Result<()> {
if active_id < self.min_bin_id || active_id > self.max_bin_id {
Err(LBError::InvalidStrategyParameters.into())
} else {
Ok(())
}
}
pub fn bin_count(&self) -> Result<usize> {
let bin_count = self.max_bin_id.safe_sub(self.min_bin_id)?;
Ok(bin_count as usize)
}
}
impl Default for StrategyParameters {
fn default() -> Self {
StrategyParameters {
min_bin_id: 0,
max_bin_id: 0,
strategy_type: StrategyType::SpotBalanced,
parameteres: [0; 64],
}
}
}