-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathspread_math.cairo
129 lines (115 loc) · 4.56 KB
/
spread_math.cairo
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
// Core lib imports.
use core::cmp::{min, max};
// Local imports.
use haiko_solver_replicating::libraries::swap_lib;
use haiko_solver_core::types::{MarketState, PositionInfo, SwapParams};
use haiko_solver_replicating::types::MarketParams;
// Haiko imports.
use haiko_lib::math::{price_math, liquidity_math, math};
use haiko_lib::constants::{ONE, MAX_LIMIT_SHIFTED};
use haiko_lib::types::i32::{i32, I32Trait};
////////////////////////////////
// CONSTANTS
///////////////////////////////
pub const DENOMINATOR: u256 = 10000;
////////////////////////////////
// FUNCTIONS
///////////////////////////////
// Calculate virtual bid or ask position to execute swap over.
//
// # Arguments
// `is_bid` - whether to calculate bid or ask position
// `lower_limit` - virtual position lower limit
// `upper_limit` - virtual position upper limit
// `amount` - token amount in reserve
//
// # Returns
// `position` - virtual liquidity position to execute swap over
pub fn get_virtual_position(
is_bid: bool, lower_limit: u32, upper_limit: u32, amount: u256,
) -> PositionInfo {
// Convert position range to sqrt prices.
let lower_sqrt_price = price_math::limit_to_sqrt_price(lower_limit, 1);
let upper_sqrt_price = price_math::limit_to_sqrt_price(upper_limit, 1);
// Calculate liquidity.
let liquidity = if is_bid {
liquidity_math::quote_to_liquidity(lower_sqrt_price, upper_sqrt_price, amount, false)
} else {
liquidity_math::base_to_liquidity(lower_sqrt_price, upper_sqrt_price, amount, false)
};
// Return position.
PositionInfo { lower_sqrt_price, upper_sqrt_price, liquidity }
}
// Calculate virtual bid or ask position to execute swap over.
//
// # Arguments
// `is_bid` - whether to calculate bid or ask position
// `delta` - inventory delta (+ve if ask spread, -ve if bid spread)
// note `delta` uses a custom i32 implementation that is [-4294967295, 4294967295]
// `range` - position range
// `oracle_price` - current oracle price (base 10e28)
//
// # Returns
// `lower_limit` - virtual position lower limit
// `upper_limit` - virtual position upper limit
pub fn get_virtual_position_range(
is_bid: bool, delta: i32, range: u32, oracle_price: u256
) -> (u32, u32) {
// Start with the oracle price, and convert it to limits.
assert(oracle_price != 0, 'OraclePriceZero');
let mut limit = price_math::price_to_limit(oracle_price, 1, !is_bid);
// Apply delta.
if delta.sign {
assert(limit >= delta.val, 'LimitUF');
limit -= delta.val;
} else {
limit += delta.val;
}
// Calculate position range.
if is_bid {
assert(limit >= range, 'LimitUF');
(limit - range, limit)
} else {
assert(limit + range <= MAX_LIMIT_SHIFTED, 'LimitOF');
(limit, limit + range)
}
}
// Calculate the single-sided spread to add to either the bid or ask positions based on delta,
// i.e. the portfolio imbalance factor.
//
// # Arguments
// `max_delta` - maximum allowed delta
// `base_reserves` - amount of base assets held in reserve by solver market
// `quote_reserves` - amount of quote assets held in reserve by solver market
// `price` - current price (base 10 ** 28)
//
// # Returns
// `delta` - inventory delta (+ve if ask spread, -ve if bid spread)
// note `delta` uses a custom i32 implementation that is [-4294967295, 4294967295]
pub fn get_delta(max_delta: u32, base_reserves: u256, quote_reserves: u256, price: u256) -> i32 {
let (skew, is_skew_bid) = get_skew(base_reserves, quote_reserves, price);
let spread: u32 = math::mul_div(max_delta.into(), skew, DENOMINATOR, false).try_into().unwrap();
// Constrain to width and return.
I32Trait::new(spread, is_skew_bid)
}
// Calculate the portfolio skew of reserves in the market.
//
// # Arguments
// `base_reserves` - amount of base assets held in reserve by solver market
// `quote_reserves` - amount of quote assets held in reserve by solver market
// `price` - current price (base 10 ** 28)
//
// # Returns
// `skew` - portfolio skew, ranging from 0 (50:50) to 10000 (100:0)
// `is_skew_bid` - whether the skew is in the bid or ask direction
pub fn get_skew(base_reserves: u256, quote_reserves: u256, price: u256,) -> (u256, bool) {
let base_in_quote = math::mul_div(base_reserves, price, ONE, false);
let diff = max(base_in_quote, quote_reserves) - min(base_in_quote, quote_reserves);
let sum = base_in_quote + quote_reserves;
if sum == 0 {
return (0, false);
}
let skew = math::mul_div(DENOMINATOR, diff, sum, false);
let is_skew_bid = base_in_quote > quote_reserves;
(skew, is_skew_bid)
}