| 1 | [ |
| 2 | "autonomous agent", |
| 3 | { |
| 4 | "getters": "{ |
| 5 | |
| 6 | $get_leverages = () => [2, 5, 10, 20, 50, 100]; |
| 7 | |
| 8 | $singularity_threshold = 0.1; |
| 9 | |
| 10 | $trade_merge_period = 1; |
| 11 | |
| 12 | $update_recent_data = ($recent, $p, $final_p, $trigger_initial_address, $tax_token, $traded_amount, $paid_tax, $period_length) => { |
| 13 | $period_start_ts = floor(timestamp / $period_length) * $period_length; |
| 14 | $pmin = min($p, $final_p); |
| 15 | $pmax = max($p, $final_p); |
| 16 | if (+$recent.current.start_ts < $period_start_ts){ |
| 17 | $recent.prev = $recent.current; |
| 18 | $recent.current = {start_ts: $period_start_ts, pmin: $pmin, pmax: $pmax}; |
| 19 | } |
| 20 | else{ |
| 21 | $recent.current.pmin = min($recent.current.pmin, $pmin); |
| 22 | $recent.current.pmax = max($recent.current.pmax, $pmax); |
| 23 | } |
| 24 | if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ |
| 25 | $recent.last_trade.pmin = min($recent.last_trade.pmin, $pmin); |
| 26 | $recent.last_trade.pmax = max($recent.last_trade.pmax, $pmax); |
| 27 | $recent.last_trade.amounts[$tax_token] = $recent.last_trade.amounts[$tax_token] + $traded_amount; |
| 28 | $recent.last_trade.paid_taxes[$tax_token] = $recent.last_trade.paid_taxes[$tax_token] + $paid_tax; |
| 29 | } |
| 30 | else{ |
| 31 | $amounts = {x:0, y:0}; |
| 32 | $paid_taxes = {x:0, y:0}; |
| 33 | $amounts[$tax_token] = $traded_amount; |
| 34 | $paid_taxes[$tax_token] = $paid_tax; |
| 35 | $recent.last_trade = { |
| 36 | address: $trigger_initial_address, |
| 37 | pmin: $pmin, |
| 38 | pmax: $pmax, |
| 39 | amounts: $amounts, |
| 40 | paid_taxes: $paid_taxes, |
| 41 | }; |
| 42 | } |
| 43 | $recent.last_ts = timestamp; |
| 44 | }; |
| 45 | |
| 46 | $get_utilization_ratio = ($balances, $l_balances, $x0, $y0, $alpha) => { |
| 47 | $beta = 1 - $alpha; |
| 48 | $ratio = reduce($get_leverages(), 6, ($acc, $L) => $acc + $l_balances[$L||'x'].balance/($balances.x+$x0)*($L-1)/$beta + $l_balances[-$L||'x'].balance/($balances.y+$y0)*($L-1)/$alpha, 0); |
| 49 | $ratio |
| 50 | }; |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | $get_final_x = ($X, $Y, $final_Y, $X0, $Y0, $pool_props, $inverted) => { |
| 56 | require($final_Y >= $Y, "not selling Y"); |
| 57 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 58 | $b = 1 - $a; |
| 59 | $final_X = ($X + $X0) * (($Y + $Y0)/($final_Y + $Y0))^($b/$a) - $X0; |
| 60 | require($final_X >= 0, "bad final_X " || $final_X); |
| 61 | $deltaX = $X - $final_X; |
| 62 | require($deltaX >= 0, "bad deltaX " || $deltaX); |
| 63 | $final_X |
| 64 | }; |
| 65 | |
| 66 | |
| 67 | $get_final_x_along_x = ($X, $Y, $final_Y, $pool_props, $inverted) => { |
| 68 | log('get_final_x_along_x', $X, $Y, $final_Y); |
| 69 | $b = $inverted ? $pool_props.alpha : $pool_props.beta; |
| 70 | $X * ($final_Y/$Y)^($b * $pool_props.Lambda/($b * $pool_props.Lambda - 1)) |
| 71 | }; |
| 72 | |
| 73 | |
| 74 | $get_final_x_along_y = ($X, $Y, $final_Y, $pool_props, $inverted) => { |
| 75 | log('get_final_x_along_y', $X, $Y, $final_Y); |
| 76 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 77 | $X * ($final_Y/$Y)^(1-1/$a/$pool_props.Lambda) |
| 78 | }; |
| 79 | |
| 80 | |
| 81 | |
| 82 | |
| 83 | |
| 84 | $get_final_y = ($X, $Y, $final_X, $X0, $Y0, $pool_props, $inverted) => { |
| 85 | |
| 86 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 87 | $b = 1 - $a; |
| 88 | $final_Y = ($Y + $Y0) * (($X + $X0)/($final_X + $X0))^($a/$b) - $Y0; |
| 89 | require($final_Y >= 0, "bad final_Y " || $final_Y); |
| 90 | |
| 91 | |
| 92 | $final_Y |
| 93 | }; |
| 94 | |
| 95 | |
| 96 | $get_final_y_along_x = ($X, $Y, $final_X, $pool_props, $inverted) => { |
| 97 | $b = $inverted ? $pool_props.alpha : $pool_props.beta; |
| 98 | $Y * ($final_X/$X)^(1 - 1/$b/$pool_props.Lambda) |
| 99 | }; |
| 100 | |
| 101 | |
| 102 | $get_final_y_along_y = ($X, $Y, $final_X, $pool_props, $inverted) => { |
| 103 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 104 | $Y * ($final_X/$X)^($a*$pool_props.Lambda/($a*$pool_props.Lambda - 1)) |
| 105 | }; |
| 106 | |
| 107 | |
| 108 | |
| 109 | $add_net_balance_without_changing_price = ($balances, $profits, $side, $amount, $Lambda) => { |
| 110 | if (!$amount) |
| 111 | return; |
| 112 | if ($Lambda == 1){ |
| 113 | $profits[$side] = $profits[$side] + $amount; |
| 114 | return; |
| 115 | } |
| 116 | |
| 117 | $opposite = $side == 'x' ? 'y' : 'x'; |
| 118 | $side_n = $side || 'n'; |
| 119 | $opposite_n = $opposite || 'n'; |
| 120 | |
| 121 | $Xn = $balances[$side_n]; |
| 122 | $Yn = $balances[$opposite_n]; |
| 123 | $X = $balances[$side]; |
| 124 | $Y = $balances[$opposite]; |
| 125 | |
| 126 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 127 | $delta_Xn = $amount; |
| 128 | |
| 129 | |
| 130 | if (!$underleveraged){ |
| 131 | |
| 132 | $full_delta_Y = $Y * $delta_Xn/$Xn; |
| 133 | if ($Y + $full_delta_Y > $Yn * $Lambda){ |
| 134 | $ratio = $Yn * $Lambda / $Y - 1; |
| 135 | $delta_X = $ratio * $X; |
| 136 | $delta_Y = $ratio * $Y; |
| 137 | } |
| 138 | else{ |
| 139 | $delta_X = $delta_Xn * $Lambda; |
| 140 | $delta_Y = $full_delta_Y; |
| 141 | } |
| 142 | } |
| 143 | else{ |
| 144 | $delta_X = 0; |
| 145 | $delta_Y = 0; |
| 146 | } |
| 147 | |
| 148 | $balances[$side_n] = $balances[$side_n] + $delta_Xn; |
| 149 | |
| 150 | $balances[$side] = $balances[$side] + $delta_X; |
| 151 | $balances[$opposite] = $balances[$opposite] + $delta_Y; |
| 152 | }; |
| 153 | |
| 154 | |
| 155 | $pow = ($precomputed, $power) => { |
| 156 | require($precomputed[$power], "no precomputed power " || $power); |
| 157 | $precomputed[$power] |
| 158 | }; |
| 159 | $precompute = $v => { |
| 160 | $pre = {}; |
| 161 | $pre['2'] = $v * $v; |
| 162 | $pre['5'] = $pre['2'] * $pre['2'] * $v; |
| 163 | $pre['10'] = $pre['5'] * $pre['5']; |
| 164 | $pre['20'] = $pre['10'] * $pre['10']; |
| 165 | $pre['50'] = $pre['20'] * $pre['20'] * $pre['10']; |
| 166 | $pre['100'] = $pre['50'] * $pre['50']; |
| 167 | $pre |
| 168 | }; |
| 169 | |
| 170 | |
| 171 | $charge_interest = ($balances, $l_balances, $profits, $x0, $y0, $last_ts, $i, $alpha, $Lambda) => { |
| 172 | require($last_ts, "no last ts"); |
| 173 | if (length($l_balances) == 0 OR $i == 0 OR timestamp == $last_ts) |
| 174 | return; |
| 175 | $beta = 1 - $alpha; |
| 176 | $x = $balances.x; |
| 177 | $y = $balances.y; |
| 178 | |
| 179 | $accrued_rate = (timestamp - $last_ts)/3600/24/360 * $i; |
| 180 | $factor = max(1 - $accrued_rate, 0); |
| 181 | $factor_powers = $precompute($factor); |
| 182 | |
| 183 | $y2x = ($y + $y0) / ($x + $x0); |
| 184 | $p = $alpha/$beta * $y2x; |
| 185 | $n_deltas = {dxn:0, dyn:0}; |
| 186 | |
| 187 | foreach($get_leverages(), 6, ($L) => { |
| 188 | $xL = $l_balances[$L||'x'].balance; |
| 189 | $yL = $l_balances[-$L||'x'].balance; |
| 190 | if (!$xL AND !$yL) |
| 191 | return; |
| 192 | $factorL1 = $pow($factor_powers, $L) / $factor; |
| 193 | if ($xL){ |
| 194 | $dxL = -$xL * (1 - $factorL1); |
| 195 | $l_balances[$L||'x'].balance = $xL + $dxL; |
| 196 | |
| 197 | $delta_yn = $dxL * $p * ($L-1)/$L; |
| 198 | $n_deltas.dyn = $n_deltas.dyn + $delta_yn; |
| 199 | if ($Lambda == 1){ |
| 200 | $n_deltas.dxn = $n_deltas.dxn + $delta_yn / $y2x; |
| 201 | $profits.x = $profits.x - $dxL - $delta_yn / $y2x; |
| 202 | } |
| 203 | else |
| 204 | $n_deltas.dxn = $n_deltas.dxn - $dxL; |
| 205 | } |
| 206 | if ($yL){ |
| 207 | $dyL = -$yL * (1 - $factorL1); |
| 208 | $l_balances[-$L||'x'].balance = $yL + $dyL; |
| 209 | |
| 210 | $delta_xn = $dyL / $p * ($L-1)/$L; |
| 211 | $n_deltas.dxn = $n_deltas.dxn + $delta_xn; |
| 212 | if ($Lambda == 1){ |
| 213 | $n_deltas.dyn = $n_deltas.dyn + $delta_xn * $y2x; |
| 214 | $profits.y = $profits.y - $dyL - $delta_xn * $y2x; |
| 215 | } |
| 216 | else |
| 217 | $n_deltas.dyn = $n_deltas.dyn - $dyL; |
| 218 | } |
| 219 | }); |
| 220 | |
| 221 | |
| 222 | $dxn = $n_deltas.dxn; |
| 223 | $dyn = $n_deltas.dyn; |
| 224 | |
| 225 | if ($Lambda == 1){ |
| 226 | $balances.xn = $balances.xn + $dxn; |
| 227 | $balances.yn = $balances.yn + $dyn; |
| 228 | $balances.x = $balances.x + $dxn; |
| 229 | $balances.y = $balances.y + $dyn; |
| 230 | } |
| 231 | else{ |
| 232 | $add_net_balance_without_changing_price($balances, $profits, 'x', $dxn, $Lambda); |
| 233 | $add_net_balance_without_changing_price($balances, $profits, 'y', $dyn, $Lambda); |
| 234 | } |
| 235 | }; |
| 236 | |
| 237 | |
| 238 | $update_leveraged_balances = ($l_balances, $P, $final_P, $inverted) => { |
| 239 | $ratio = $final_P/$P; |
| 240 | $ratio_powers = $precompute($ratio); |
| 241 | |
| 242 | $totals = { |
| 243 | delta_XL: 0, |
| 244 | delta_YL: 0, |
| 245 | XL_denom: 0, |
| 246 | YL_denom: 0, |
| 247 | }; |
| 248 | foreach($get_leverages(), 6, ($L) => { |
| 249 | $allyL = $inverted ? -$L : $L; |
| 250 | $balance = $l_balances[$allyL||'x'].balance; |
| 251 | $obalance = $l_balances[-$allyL||'x'].balance; |
| 252 | if (!$balance AND !$obalance) |
| 253 | return; |
| 254 | $ratio_L1 = $pow($ratio_powers, $L) / $ratio; |
| 255 | $debt_ratio = ($L-1)/$L; |
| 256 | if ($balance) { |
| 257 | $delta_XL_balance = $balance * ($ratio_L1 - 1); |
| 258 | $new_XL_balance = $balance + $delta_XL_balance; |
| 259 | $l_balances[$allyL||'x'].balance = $new_XL_balance; |
| 260 | $delta_YL_balance = -(($new_XL_balance * $final_P - $balance * $P) * $debt_ratio); |
| 261 | $totals.delta_XL = $totals.delta_XL + $delta_XL_balance; |
| 262 | $totals.delta_YL = $totals.delta_YL + $delta_YL_balance; |
| 263 | $totals.XL_denom = $totals.XL_denom + $new_XL_balance * ($L-1); |
| 264 | } |
| 265 | if ($obalance) { |
| 266 | $delta_YL_obalance = $obalance * (1/$ratio_L1 - 1); |
| 267 | $new_YL_obalance = $obalance + $delta_YL_obalance; |
| 268 | $l_balances[-$allyL||'x'].balance = $new_YL_obalance; |
| 269 | $delta_XL_obalance = -(($new_YL_obalance / $final_P - $obalance / $P) * $debt_ratio); |
| 270 | $totals.delta_YL = $totals.delta_YL + $delta_YL_obalance; |
| 271 | $totals.delta_XL = $totals.delta_XL + $delta_XL_obalance; |
| 272 | $totals.YL_denom = $totals.YL_denom + $new_YL_obalance * ($L-1); |
| 273 | } |
| 274 | }); |
| 275 | $totals |
| 276 | }; |
| 277 | |
| 278 | |
| 279 | |
| 280 | $swap = ($balances, $l_balances, $profits, $recent, $x0, $y0, $y_in, $delta_Yn, $in_final_P, $received_amount_Y, $min_amount_out, $trigger_initial_address, $pool_props) => { |
| 281 | |
| 282 | require(!$in_final_P, "no final price please, this is swap by Y"); |
| 283 | |
| 284 | $alpha = $pool_props.alpha; |
| 285 | $beta = $pool_props.beta; |
| 286 | $Lambda = $pool_props.Lambda; |
| 287 | |
| 288 | $xn = $balances.xn; |
| 289 | $yn = $balances.yn; |
| 290 | $x = $balances.x; |
| 291 | $y = $balances.y; |
| 292 | |
| 293 | if ($y_in){ |
| 294 | $inverted = false; |
| 295 | $X = $x; |
| 296 | $Y = $y; |
| 297 | $Xn = $xn; |
| 298 | $Yn = $yn; |
| 299 | $X0 = $x0; |
| 300 | $Y0 = $y0; |
| 301 | $a = $alpha; |
| 302 | $b = $beta; |
| 303 | $in_token = 'y'; |
| 304 | $out_token = 'x'; |
| 305 | } |
| 306 | else{ |
| 307 | $inverted = true; |
| 308 | $X = $y; |
| 309 | $Y = $x; |
| 310 | $Xn = $yn; |
| 311 | $Yn = $xn; |
| 312 | $X0 = $y0; |
| 313 | $Y0 = $x0; |
| 314 | $a = $beta; |
| 315 | $b = $alpha; |
| 316 | $in_token = 'x'; |
| 317 | $out_token = 'y'; |
| 318 | } |
| 319 | |
| 320 | require($delta_Yn > 0, "bad delta " || $delta_Yn); |
| 321 | |
| 322 | if ($Lambda > 1){ |
| 323 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 324 | } |
| 325 | |
| 326 | $final_Yn = $Yn + $delta_Yn; |
| 327 | |
| 328 | if ($Lambda == 1){ |
| 329 | $final_Xn = $get_final_x($X, $Y, $final_Yn, $X0, $Y0, $pool_props, $inverted); |
| 330 | $final_X = $final_Xn; |
| 331 | $final_Y = $final_Yn; |
| 332 | } |
| 333 | else if (!$underleveraged){ |
| 334 | $delta_Y = -($b*$Lambda-1)/$a*$delta_Yn; |
| 335 | $final_Y = $Y + $delta_Y; |
| 336 | require($final_Y > 0, "fully leveraged: negative final_Y="||$final_Y); |
| 337 | $final_X = $get_final_x_along_x($X, $Y, $final_Y, $pool_props, $inverted); |
| 338 | $final_Xn = $final_X/$Lambda; |
| 339 | } |
| 340 | else if ($underleveraged){ |
| 341 | $delta_Yn_inflection = $Y * (( $Lambda/($Lambda-1) * ($b + ($a * $Lambda - 1) * $Xn/$X) )^($a * $Lambda/($a*$Lambda-1)) - 1) / $Lambda; |
| 342 | require($delta_Yn_inflection > 0, "negative delta_Yn_inflection="||$delta_Yn_inflection); |
| 343 | $inflected = $delta_Yn > $delta_Yn_inflection; |
| 344 | |
| 345 | $inflection_Yn = $Yn + $delta_Yn_inflection; |
| 346 | $final_Yn1 = $inflected ? $inflection_Yn : $final_Yn; |
| 347 | $final_Y1 = $final_Yn1 * $Lambda; |
| 348 | $final_X1 = $get_final_x_along_y($X, $Y, $final_Y1, $pool_props, $inverted); |
| 349 | $delta_X1 = $final_X1 - $X; |
| 350 | $delta_Xn1 = -$b/($a*$Lambda-1) * $delta_X1; |
| 351 | $final_Xn1 = $Xn + $delta_Xn1; |
| 352 | require($final_Xn1 > 0, "negative final_Xn1="||$final_Xn1); |
| 353 | if ($inflected){ |
| 354 | |
| 355 | log('inflected at ', $delta_Yn_inflection); |
| 356 | $delta_Yn2 = $final_Yn - $final_Yn1; |
| 357 | $delta_Y2 = -($b*$Lambda-1)/$a*$delta_Yn2; |
| 358 | $final_Y = $final_Y1 + $delta_Y2; |
| 359 | require($final_Y > 0, "inflected: negative final_Y="||$final_Y); |
| 360 | $final_X = $get_final_x_along_x($final_X1, $final_Y1, $final_Y, $pool_props, $inverted); |
| 361 | $final_Xn = $final_X/$Lambda; |
| 362 | require($final_Xn <= $final_Xn1, "Xn didn't decrease"); |
| 363 | } |
| 364 | else{ |
| 365 | $final_X = $final_X1; |
| 366 | $final_Xn = $final_Xn1; |
| 367 | $final_Y = $final_Y1; |
| 368 | } |
| 369 | } |
| 370 | else |
| 371 | bounce("???"); |
| 372 | |
| 373 | $balances.x = $y_in ? $final_X : $final_Y; |
| 374 | $balances.y = $y_in ? $final_Y : $final_X; |
| 375 | $balances.xn = $y_in ? $final_Xn : $final_Yn; |
| 376 | $balances.yn = $y_in ? $final_Yn : $final_Xn; |
| 377 | |
| 378 | $final_y = $balances.y; |
| 379 | $final_x = $balances.x; |
| 380 | |
| 381 | $p = $alpha/$beta * ($y + $y0) / ($x + $x0); |
| 382 | $P = $inverted ? 1/$p : $p; |
| 383 | $final_p = $alpha/$beta * ($final_y + $y0) / ($final_x + $x0); |
| 384 | $final_P = $inverted ? 1/$final_p : $final_p; |
| 385 | require($final_P > $P, "price should have risen but hasn't, old " || $P || ", new " || $final_P); |
| 386 | |
| 387 | |
| 388 | $totals = $update_leveraged_balances($l_balances, $P, $final_P, $inverted); |
| 389 | |
| 390 | $amount_X_exact = -($final_Xn - $Xn + $totals.delta_XL); |
| 391 | $amount_Y_exact = $final_Yn - $Yn + $totals.delta_YL; |
| 392 | $amount_Y = ceil($amount_Y_exact); |
| 393 | if ($received_amount_Y >= 0) |
| 394 | require($received_amount_Y >= $amount_Y, "expected " || $amount_Y || ", received " || $received_amount_Y); |
| 395 | require($amount_X_exact >= 0, "to pay " || $amount_X_exact); |
| 396 | $change = $received_amount_Y - $amount_Y; |
| 397 | |
| 398 | $denom = 1 - $totals.XL_denom/$b/($final_X+$X0) - $totals.YL_denom/$a/($final_Y+$Y0); |
| 399 | log('denom after swap by delta', $denom); |
| 400 | require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to swap this amount"); |
| 401 | |
| 402 | |
| 403 | if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ |
| 404 | $min_P = min($P, $y_in ? $recent.last_trade.pmin : 1/$recent.last_trade.pmax); |
| 405 | $max_P = max($final_P, $y_in ? $recent.last_trade.pmax : 1/$recent.last_trade.pmin); |
| 406 | $recent_traded_amount = $recent.last_trade.amounts[$out_token]; |
| 407 | $recent_paid_tax = $recent.last_trade.paid_taxes[$out_token]; |
| 408 | } |
| 409 | else{ |
| 410 | $min_P = $P; |
| 411 | $max_P = $final_P; |
| 412 | } |
| 413 | $arb_profit_in_Y = ($max_P - $min_P) * ($recent_traded_amount + $amount_X_exact) / 2; |
| 414 | $arb_profit_in_X = $arb_profit_in_Y / $min_P; |
| 415 | $arb_profit_tax = $arb_profit_in_X * $pool_props.arb_profit_tax - $recent_paid_tax; |
| 416 | require($arb_profit_tax >= 0, "negative arb profit tax"); |
| 417 | |
| 418 | $swap_fee = $amount_X_exact * $pool_props.swap_fee; |
| 419 | $fee = $arb_profit_tax + $swap_fee; |
| 420 | |
| 421 | $net_amount_X_exact = $amount_X_exact - $fee; |
| 422 | $net_amount_X = floor($net_amount_X_exact); |
| 423 | $rounding_fee_X = $net_amount_X_exact - $net_amount_X; |
| 424 | $rounding_fee_Y = $amount_Y - $amount_Y_exact; |
| 425 | $total_fee = $fee + $rounding_fee_X; |
| 426 | |
| 427 | if ($min_amount_out) |
| 428 | require($net_amount_X >= $min_amount_out, "output amount " || $net_amount_X || " would be less than the expected minimum " || $min_amount_out); |
| 429 | |
| 430 | |
| 431 | $fees = { |
| 432 | X: $total_fee, |
| 433 | Y: $rounding_fee_Y, |
| 434 | }; |
| 435 | |
| 436 | |
| 437 | $add_net_balance_without_changing_price($balances, $profits, $out_token, $fees.X, $Lambda); |
| 438 | $add_net_balance_without_changing_price($balances, $profits, $in_token, $fees.Y, $Lambda); |
| 439 | log({fees: $fees, profits: $profits}); |
| 440 | |
| 441 | $update_recent_data($recent, $p, $final_p, $trigger_initial_address, $out_token, $amount_X_exact, $arb_profit_tax, $pool_props.period_length); |
| 442 | |
| 443 | $event = json_stringify({ |
| 444 | type: 'swap', |
| 445 | direction: $y_in ? 'y2x' : 'x2y', |
| 446 | in: $amount_Y, |
| 447 | out: $net_amount_X, |
| 448 | swap_fee: $swap_fee, |
| 449 | arb_profit_tax: $arb_profit_tax, |
| 450 | total_fee: $total_fee, |
| 451 | }); |
| 452 | |
| 453 | { |
| 454 | net_amount_X: $net_amount_X, |
| 455 | amount_Y: $amount_Y, |
| 456 | swap_fee: $swap_fee, |
| 457 | arb_profit_tax: $arb_profit_tax, |
| 458 | total_fee: $total_fee, |
| 459 | fees: $fees, |
| 460 | change: $change, |
| 461 | initial_price: $P, |
| 462 | final_price: $final_P, |
| 463 | event: $event, |
| 464 | } |
| 465 | }; |
| 466 | |
| 467 | |
| 468 | |
| 469 | $buy_shares = ($s, $balances, $profits, $recent, $x0, $y0, $received_amount_x, $received_amount_y, $pool_props) => { |
| 470 | |
| 471 | $Lambda = $pool_props.Lambda; |
| 472 | $alpha = $pool_props.alpha; |
| 473 | $beta = $pool_props.beta; |
| 474 | |
| 475 | |
| 476 | $get_shares = ($x_balance, $y_balance) => round(($x_balance/$y_balance)^$alpha * $y_balance); |
| 477 | |
| 478 | $xn = $balances.xn; |
| 479 | $yn = $balances.yn; |
| 480 | $x = $balances.x; |
| 481 | $y = $balances.y; |
| 482 | |
| 483 | $recent.last_ts = timestamp; |
| 484 | |
| 485 | if (!$s){ |
| 486 | require($received_amount_x > 0 AND $received_amount_y > 0, "send both assets for the first issue"); |
| 487 | $mid_price = $pool_props.mid_price; |
| 488 | if ($mid_price){ |
| 489 | |
| 490 | require($received_amount_y == round($mid_price * $received_amount_x), "first issue must be at mid price "||$mid_price); |
| 491 | $gamma = $pool_props.gamma; |
| 492 | $shares_amount = round($received_amount_x * $pool_props.mid_price_beta * $gamma / ($gamma - 1)); |
| 493 | } |
| 494 | else{ |
| 495 | |
| 496 | $shares_amount = $get_shares($received_amount_x, $received_amount_y); |
| 497 | } |
| 498 | $balances.xn = $balances.xn + $received_amount_x; |
| 499 | $balances.yn = $balances.yn + $received_amount_y; |
| 500 | $balances.x = $balances.x + $received_amount_x * $Lambda; |
| 501 | $balances.y = $balances.y + $received_amount_y * $Lambda; |
| 502 | |
| 503 | $event = json_stringify({ |
| 504 | type: 'add', |
| 505 | x: $received_amount_x, |
| 506 | y: $received_amount_y, |
| 507 | shares: $shares_amount, |
| 508 | }); |
| 509 | |
| 510 | return { |
| 511 | shares_amount: $shares_amount, |
| 512 | coef: 1, |
| 513 | change_x: 0, |
| 514 | change_y: 0, |
| 515 | event: $event, |
| 516 | }; |
| 517 | } |
| 518 | |
| 519 | $p = $alpha/$beta * ($y + $y0) / ($x + $x0); |
| 520 | if ($Lambda > 1 AND $recent.prev){ |
| 521 | $target_xn = $x/$Lambda; |
| 522 | if ($xn > ceil($target_xn)){ |
| 523 | |
| 524 | |
| 525 | $share_price_in_y = ($yn + max($recent.current.pmax, $recent.prev.pmax) * $xn) / $s; |
| 526 | $max_delta_yn = ($xn/$target_xn-1)*$yn; |
| 527 | $delta_yn1 = min($max_delta_yn, $received_amount_y); |
| 528 | $delta_y1 = $delta_yn1 * $Lambda; |
| 529 | $delta_x1 = $x * $delta_yn1/$yn; |
| 530 | $shares1 = $delta_yn1/$share_price_in_y; |
| 531 | } |
| 532 | else{ |
| 533 | $target_yn = $y/$Lambda; |
| 534 | if ($yn > ceil($target_yn)){ |
| 535 | |
| 536 | |
| 537 | $share_price_in_x = ($xn + 1/min($recent.current.pmin, $recent.prev.pmin) * $yn) / $s; |
| 538 | $max_delta_xn = ($yn/$target_yn-1)*$xn; |
| 539 | $delta_xn1 = min($max_delta_xn, $received_amount_x); |
| 540 | $delta_x1 = $delta_xn1 * $Lambda; |
| 541 | $delta_y1 = $y * $delta_xn1/$xn; |
| 542 | $shares1 = $delta_xn1/$share_price_in_x; |
| 543 | log({delta_xn1:$delta_xn1, shares1:$shares1, share_price_in_x:$share_price_in_x, p:$p, recent:$recent}); |
| 544 | } |
| 545 | } |
| 546 | $balances.xn = $balances.xn + $delta_xn1; |
| 547 | $balances.yn = $balances.yn + $delta_yn1; |
| 548 | $balances.x = $balances.x + $delta_x1; |
| 549 | $balances.y = $balances.y + $delta_y1; |
| 550 | } |
| 551 | else{ |
| 552 | $delta_xn1 = 0; |
| 553 | $delta_yn1 = 0; |
| 554 | $shares1 = 0; |
| 555 | } |
| 556 | $remaining = { |
| 557 | x: $received_amount_x - $delta_xn1, |
| 558 | y: $received_amount_y - $delta_yn1, |
| 559 | }; |
| 560 | |
| 561 | $y_to_x = ($yn+$delta_yn1)/($xn+$delta_xn1); |
| 562 | |
| 563 | |
| 564 | if ($profits.x OR $profits.y){ |
| 565 | require($Lambda == 1, "have profits while Lambda is " || $Lambda); |
| 566 | require($profits.x >= 0 AND $profits.y >= 0, "negative profits?"); |
| 567 | |
| 568 | |
| 569 | $share_price_in_y = ($yn + $p * $xn) / $s; |
| 570 | $share_price_in_x = ($xn + 1/$p * $yn) / $s; |
| 571 | |
| 572 | |
| 573 | $profits_proportional_y = $y_to_x * $profits.x; |
| 574 | if ($profits.y > $profits_proportional_y){ |
| 575 | $delta_profit_x = $profits.x; |
| 576 | $delta_profit_y = $profits_proportional_y; |
| 577 | $symmetric_moved_profit_shares = $delta_profit_x/$xn * $s; |
| 578 | } |
| 579 | else{ |
| 580 | $profits_proportional_x = $profits.y / $y_to_x; |
| 581 | require($profits_proportional_x <= $profits.x, "profits x " || $profits.x || ", proportional " || $profits_proportional_x); |
| 582 | $delta_profit_x = $profits_proportional_x; |
| 583 | $delta_profit_y = $profits.y; |
| 584 | $symmetric_moved_profit_shares = $delta_profit_y/$yn * $s; |
| 585 | } |
| 586 | $profits.x = $profits.x - $delta_profit_x; |
| 587 | $profits.y = $profits.y - $delta_profit_y; |
| 588 | $balances.xn = $balances.xn + $delta_profit_x; |
| 589 | $balances.yn = $balances.yn + $delta_profit_y; |
| 590 | $balances.x = $balances.x + $delta_profit_x; |
| 591 | $balances.y = $balances.y + $delta_profit_y; |
| 592 | log('after proportional profits: delta_profit_x', $delta_profit_x, 'delta_profit_y', $delta_profit_y, 'remaining profits', $profits, 'symmetric_moved_profit_shares', $symmetric_moved_profit_shares); |
| 593 | |
| 594 | |
| 595 | $moved_profit_x = min($profits.x, $remaining.y / $y_to_x); |
| 596 | $moved_profit_y = min($profits.y, $remaining.x * $y_to_x); |
| 597 | |
| 598 | $moved_profit_shares = $moved_profit_x/$share_price_in_x + $moved_profit_y/$share_price_in_y + $symmetric_moved_profit_shares; |
| 599 | log('share_price_in_x', $share_price_in_x, 'share_price_in_y', $share_price_in_y, 'moved_profit_shares', $moved_profit_shares, 'moved_profit_x', $moved_profit_x, 'moved_profit_y', $moved_profit_y); |
| 600 | |
| 601 | $profits.x = $profits.x - $moved_profit_x; |
| 602 | $profits.y = $profits.y - $moved_profit_y; |
| 603 | |
| 604 | $remaining.x = $remaining.x + $moved_profit_x; |
| 605 | $remaining.y = $remaining.y + $moved_profit_y; |
| 606 | } |
| 607 | |
| 608 | |
| 609 | log('before proportional buying: remaining', $remaining, 'y_to_x', $y_to_x); |
| 610 | $remaining_proportional_y = $y_to_x * $remaining.x; |
| 611 | if ($remaining.y > $remaining_proportional_y){ |
| 612 | $proportional_delta_xn = $remaining.x; |
| 613 | $proportional_delta_yn = $remaining_proportional_y; |
| 614 | $exact_change_x = 0; |
| 615 | $exact_change_y = $remaining.y - $remaining_proportional_y; |
| 616 | log({proportional_delta_yn:$proportional_delta_yn, exact_change_y:$exact_change_y, change_y_exact: $remaining.y - $remaining_proportional_y, remaining_y:$remaining.y, remaining_proportional_y:$remaining_proportional_y}); |
| 617 | $shares_proportional = $remaining.x / ($xn + $delta_xn1) * ($s + $shares1); |
| 618 | } |
| 619 | else{ |
| 620 | $remaining_proportional_x = $remaining.y / $y_to_x; |
| 621 | $proportional_delta_xn = $remaining_proportional_x; |
| 622 | $proportional_delta_yn = $remaining.y; |
| 623 | $exact_change_x = $remaining.x - $remaining_proportional_x; |
| 624 | log({proportional_delta_xn:$proportional_delta_xn, exact_change_x:$exact_change_x, remaining_x:$remaining.x, remaining_proportional_x:$remaining_proportional_x}); |
| 625 | require($exact_change_x >= 0, "received x " || $remaining.x || ", proportional " || $remaining_proportional_x); |
| 626 | $exact_change_y = 0; |
| 627 | $shares_proportional = $remaining.y / ($yn + $delta_yn1) * ($s + $shares1); |
| 628 | } |
| 629 | |
| 630 | $gross_shares_amount = $shares1 + $symmetric_moved_profit_shares + $shares_proportional; |
| 631 | $shares_amount = floor($gross_shares_amount - $moved_profit_shares); |
| 632 | $coef = $Lambda == 1 ? ($s + $gross_shares_amount) / ($s + $shares_amount) : 1; |
| 633 | log({shares_proportional:$shares_proportional, moved_profit_shares:$moved_profit_shares, shares_amount:$shares_amount}); |
| 634 | |
| 635 | $balances.xn = $balances.xn + $proportional_delta_xn; |
| 636 | $balances.yn = $balances.yn + $proportional_delta_yn; |
| 637 | $balances.x = $balances.x + $proportional_delta_xn * $Lambda; |
| 638 | $balances.y = $balances.y + $proportional_delta_yn * $Lambda; |
| 639 | |
| 640 | $change_x = floor($exact_change_x); |
| 641 | $change_y = floor($exact_change_y); |
| 642 | $rounding_fee_x = $exact_change_x - $change_x; |
| 643 | $rounding_fee_y = $exact_change_y - $change_y; |
| 644 | |
| 645 | $add_net_balance_without_changing_price($balances, $profits, 'x', $rounding_fee_x, $Lambda); |
| 646 | $add_net_balance_without_changing_price($balances, $profits, 'y', $rounding_fee_y, $Lambda); |
| 647 | |
| 648 | $event = json_stringify({ |
| 649 | type: 'add', |
| 650 | x: $received_amount_x - $change_x, |
| 651 | y: $received_amount_y - $change_y, |
| 652 | shares: $shares_amount, |
| 653 | }); |
| 654 | |
| 655 | { |
| 656 | shares_amount: $shares_amount, |
| 657 | coef: $coef, |
| 658 | change_x: $change_x, |
| 659 | change_y: $change_y, |
| 660 | event: $event, |
| 661 | } |
| 662 | |
| 663 | }; |
| 664 | |
| 665 | |
| 666 | $redeem_shares = ($s, $balances, $l_balances, $profits, $recent, $x0, $y0, $received_shares_amount, $asset, $pool_props) => { |
| 667 | $xn = $balances.xn; |
| 668 | $yn = $balances.yn; |
| 669 | $x = $balances.x; |
| 670 | $y = $balances.y; |
| 671 | $exit_fee = ($received_shares_amount < $s) ? $pool_props.exit_fee : 0; |
| 672 | $net_of_exit_fee = 1 - $exit_fee; |
| 673 | $x_asset = $pool_props.x_asset; |
| 674 | $y_asset = $pool_props.y_asset; |
| 675 | $Lambda = $pool_props.Lambda; |
| 676 | $alpha = $pool_props.alpha; |
| 677 | |
| 678 | if ($asset){ |
| 679 | require($asset == $x_asset OR $asset == $y_asset, "wrong preferred asset"); |
| 680 | require($Lambda > 1, "only proportional withdrawals allowed"); |
| 681 | $asset_label = $asset == $x_asset ? 'x' : 'y'; |
| 682 | $net_balance = $asset == $x_asset ? $xn : $yn; |
| 683 | $effective_balance = $asset == $x_asset ? $x : $y; |
| 684 | $target_net_balance = $effective_balance / $Lambda; |
| 685 | |
| 686 | |
| 687 | $excess_net_balance = max($net_balance - $target_net_balance, 0); |
| 688 | |
| 689 | require($recent.prev, "too early, prev price not known yet"); |
| 690 | |
| 691 | $share_price_in_asset = (($asset_label == 'y') ? ($yn + min($recent.current.pmin, $recent.prev.pmin) * $xn) / $s : ($xn + 1/max($recent.current.pmax, $recent.prev.pmax) * $yn) / $s) * $net_of_exit_fee; |
| 692 | $max_asset = $received_shares_amount * $share_price_in_asset; |
| 693 | $one_sided_amount = min($max_asset, $excess_net_balance); |
| 694 | $one_sided_fee = $one_sided_amount / $net_of_exit_fee * $exit_fee; |
| 695 | if ($asset_label == 'y'){ |
| 696 | $yn_amount1 = $one_sided_amount; |
| 697 | $xn_amount1 = 0; |
| 698 | $one_sided_y_fee = $one_sided_fee; |
| 699 | } |
| 700 | else{ |
| 701 | $xn_amount1 = $one_sided_amount; |
| 702 | $yn_amount1 = 0; |
| 703 | $one_sided_x_fee = $one_sided_fee; |
| 704 | } |
| 705 | $remaining_received_shares = max($received_shares_amount - ($one_sided_amount / $share_price_in_asset), 0); |
| 706 | } |
| 707 | else{ |
| 708 | $remaining_received_shares = $received_shares_amount; |
| 709 | $xn_amount1 = 0; |
| 710 | $yn_amount1 = 0; |
| 711 | } |
| 712 | $share_of_shares = $remaining_received_shares / $s; |
| 713 | $remaining_share_of_shares = 1 - $share_of_shares; |
| 714 | $remaining_share_of_assets = $remaining_share_of_shares; |
| 715 | $share_of_assets = 1 - $remaining_share_of_assets; |
| 716 | $x_amount = $share_of_assets * $x * $net_of_exit_fee; |
| 717 | $y_amount = $share_of_assets * $y * $net_of_exit_fee; |
| 718 | $xn_amount = $share_of_assets * ($xn - $xn_amount1) * $net_of_exit_fee + $xn_amount1; |
| 719 | $yn_amount = $share_of_assets * ($yn - $yn_amount1) * $net_of_exit_fee + $yn_amount1; |
| 720 | if ($asset) |
| 721 | log({xn_amount1:$xn_amount1, yn_amount1:$yn_amount1, shares_for_excess: $one_sided_amount / $share_price_in_asset, remaining_received_shares:$remaining_received_shares, xn_amount:$xn_amount, yn_amount:$yn_amount, asset_label:$asset_label, share_price_in_asset:$share_price_in_asset, max_asset: $max_asset, excess_net_balance:$excess_net_balance, recent:$recent, pmin:min($recent.current.pmin, $recent.prev.pmin), pmax:max($recent.current.pmax, $recent.prev.pmax)}); |
| 722 | log({ remaining_received_shares:$remaining_received_shares, xn_amount:$xn_amount, yn_amount:$yn_amount, x_amount:$x_amount, y_amount:$y_amount}); |
| 723 | |
| 724 | log('balances before', $balances); |
| 725 | $balances.x = $balances.x - $x_amount; |
| 726 | $balances.y = $balances.y - $y_amount; |
| 727 | $balances.xn = $balances.xn - $xn_amount; |
| 728 | $balances.yn = $balances.yn - $yn_amount; |
| 729 | log('balances after', $balances); |
| 730 | |
| 731 | $coef = $Lambda == 1 AND $received_shares_amount < $s ? ($s - $received_shares_amount * $net_of_exit_fee) / ($s - $received_shares_amount) : 1; |
| 732 | |
| 733 | $new_x0 = $x0 * ($s-$received_shares_amount)/$s; |
| 734 | $new_y0 = $y0 * ($s-$received_shares_amount)/$s; |
| 735 | $denom = 1 - $get_utilization_ratio($balances, $l_balances, $new_x0, $new_y0, $alpha); |
| 736 | require($denom >= $singularity_threshold, "redemption amount too large, it would bring us too close to the singularity point, denom="||$denom); |
| 737 | |
| 738 | $int_xn_amount = floor($xn_amount); |
| 739 | $int_yn_amount = floor($yn_amount); |
| 740 | $rounding_fee_x = $xn_amount - $int_xn_amount; |
| 741 | $rounding_fee_y = $yn_amount - $int_yn_amount; |
| 742 | |
| 743 | $add_net_balance_without_changing_price($balances, $profits, 'x', $rounding_fee_x, $Lambda); |
| 744 | $add_net_balance_without_changing_price($balances, $profits, 'y', $rounding_fee_y, $Lambda); |
| 745 | log('balances after rounding fees', $balances); |
| 746 | |
| 747 | $recent.last_ts = timestamp; |
| 748 | |
| 749 | $event = json_stringify({ |
| 750 | type: 'remove', |
| 751 | shares: $received_shares_amount, |
| 752 | x: $int_xn_amount, |
| 753 | y: $int_yn_amount, |
| 754 | x_fee: $share_of_assets * ($xn - $xn_amount1) * $exit_fee + $one_sided_x_fee, |
| 755 | y_fee: $share_of_assets * ($yn - $yn_amount1) * $exit_fee + $one_sided_y_fee, |
| 756 | }); |
| 757 | |
| 758 | { |
| 759 | xn_amount: $int_xn_amount, |
| 760 | yn_amount: $int_yn_amount, |
| 761 | coef: $coef, |
| 762 | event: $event, |
| 763 | } |
| 764 | }; |
| 765 | |
| 766 | |
| 767 | $update_other_l_balances_and_get_sums = ($l_balances, $P, $final_P, $Leverage, $inverted) => { |
| 768 | $ratio = $final_P/$P; |
| 769 | $ratio_powers = $precompute($ratio); |
| 770 | $sums = { |
| 771 | initial: 0, |
| 772 | final: 0, |
| 773 | delta_XL: 0, |
| 774 | XL_denom: 0, |
| 775 | YL_denom: 0, |
| 776 | PL1_ratio: $pow($ratio_powers, $Leverage) / $ratio, |
| 777 | }; |
| 778 | foreach($get_leverages(), 6, $L => { |
| 779 | $allyL = $inverted ? -$L : $L; |
| 780 | $balance = $l_balances[$allyL||'x'].balance; |
| 781 | $obalance = $l_balances[-$allyL||'x'].balance; |
| 782 | if (!$balance AND !$obalance) |
| 783 | return; |
| 784 | $ratio_L1 = $pow($ratio_powers, $L) / $ratio; |
| 785 | if ($balance){ |
| 786 | $sums.initial = $sums.initial + $balance * ($L-1)/$L; |
| 787 | if ($L != $Leverage){ |
| 788 | $new_balance = $balance * $ratio_L1; |
| 789 | $l_balances[$allyL||'x'].balance = $new_balance; |
| 790 | $sums.final = $sums.final + $new_balance * ($L-1)/$L; |
| 791 | $sums.XL_denom = $sums.XL_denom + $new_balance * ($L-1); |
| 792 | $sums.delta_XL = $sums.delta_XL + $new_balance - $balance; |
| 793 | } |
| 794 | } |
| 795 | if ($obalance){ |
| 796 | $sums.initial = $sums.initial - $obalance/$P; |
| 797 | $new_obalance = $obalance / $ratio_L1; |
| 798 | $l_balances[-$allyL||'x'].balance = $new_obalance; |
| 799 | $sums.final = $sums.final - $new_obalance/$final_P; |
| 800 | $sums.YL_denom = $sums.YL_denom + $new_obalance * ($L-1); |
| 801 | $sums.delta_XL = $sums.delta_XL - ($new_obalance / $final_P - $obalance / $P) * ($L-1)/$L; |
| 802 | } |
| 803 | }); |
| 804 | $sums |
| 805 | }; |
| 806 | |
| 807 | |
| 808 | |
| 809 | $move_unleveraged = ($pool, $l_balances, $X0, $Y0, $dXn, $Leverage, $pool_props, $inverted) => { |
| 810 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 811 | $b = 1 - $a; |
| 812 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 813 | $l_bal_direction = $dXn < 0 ? "grow" : "fall"; |
| 814 | |
| 815 | $Xt = $pool.X + $X0; |
| 816 | $P = $a/$b * ($pool.Y + $Y0) / $Xt; |
| 817 | $final_Xn = $pool.Xn + $dXn; |
| 818 | require($final_Xn > 0, "unleveraged: final_Xn=" || $final_Xn); |
| 819 | $final_X = $final_Xn; |
| 820 | $final_Xt = $final_X + $X0; |
| 821 | $final_Y = $get_final_y($pool.X, $pool.Y, $final_X, $X0, $Y0, $pool_props, $inverted); |
| 822 | $delta_Y = $final_Y - $pool.Y; |
| 823 | $delta_Yn = $delta_Y; |
| 824 | $final_Yn = $pool.Yn + $delta_Yn; |
| 825 | require($final_Yn > 0, "unleveraged: final_Yn=" || $final_Yn); |
| 826 | $final_P = $a/$b * ($final_Y + $Y0) / $final_Xt; |
| 827 | |
| 828 | |
| 829 | $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); |
| 830 | |
| 831 | |
| 832 | |
| 833 | |
| 834 | $b1 = $sums.initial + $b/($b-1)*$Xt; |
| 835 | $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final - $b/($b-1)*$final_Xt + $b1 * ($final_Xt/$Xt)^(1/$b) ); |
| 836 | |
| 837 | require($new_l_balance >= 0, "unleveraged: new_l_balance=" || $new_l_balance); |
| 838 | $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; |
| 839 | |
| 840 | require($delta_l_balance * $dXn < 0, "unleveraged l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); |
| 841 | $l_balances[$L_key].balance = $new_l_balance; |
| 842 | |
| 843 | |
| 844 | $pool.X = $final_X; |
| 845 | $pool.Y = $final_Y; |
| 846 | $pool.Xn = $final_Xn; |
| 847 | $pool.Yn = $final_Yn; |
| 848 | $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; |
| 849 | $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); |
| 850 | $pool.YL_denom = $sums.YL_denom; |
| 851 | $pool.PL1_ratio = $pool.PL1_ratio * $sums.PL1_ratio; |
| 852 | }; |
| 853 | |
| 854 | |
| 855 | $move_along_X = ($pool, $l_balances, $dXn, $Leverage, $pool_props, $inverted) => { |
| 856 | $Lambda = $pool_props.Lambda; |
| 857 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 858 | $b = 1 - $a; |
| 859 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 860 | $l_bal_direction = $dXn < 0 ? "grow" : "fall"; |
| 861 | |
| 862 | $P = $a/$b * $pool.Y / $pool.X; |
| 863 | $final_Xn = $pool.Xn + $dXn; |
| 864 | require($final_Xn > 0, "along X: final_Xn=" || $final_Xn); |
| 865 | $final_X = $Lambda * $final_Xn; |
| 866 | $final_Y = $get_final_y_along_x($pool.X, $pool.Y, $final_X, $pool_props, $inverted); |
| 867 | $delta_Y = $final_Y - $pool.Y; |
| 868 | $delta_Yn = -$a/($b*$Lambda-1)*$delta_Y; |
| 869 | $final_Yn = $pool.Yn + $delta_Yn; |
| 870 | require($final_Yn > 0, "along X: final_Yn=" || $final_Yn); |
| 871 | $final_P = $a/$b * $final_Y / $final_X; |
| 872 | |
| 873 | $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); |
| 874 | |
| 875 | |
| 876 | $b1 = $sums.initial + $b/($b*$Lambda-1)*$pool.X; |
| 877 | $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final - $b/($b*$Lambda-1)*$final_X + $b1 * ($final_X/$pool.X)^(1/$b/$Lambda) ); |
| 878 | require($new_l_balance >= 0, "along X: new_l_balance=" || $new_l_balance); |
| 879 | $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; |
| 880 | require($delta_l_balance * $dXn < 0, "along x l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); |
| 881 | $l_balances[$L_key].balance = $new_l_balance; |
| 882 | |
| 883 | $pool.X = $final_X; |
| 884 | $pool.Y = $final_Y; |
| 885 | $pool.Xn = $final_Xn; |
| 886 | $pool.Yn = $final_Yn; |
| 887 | $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; |
| 888 | $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); |
| 889 | $pool.YL_denom = $sums.YL_denom; |
| 890 | $pool.PL1_ratio = $pool.PL1_ratio * $sums.PL1_ratio; |
| 891 | }; |
| 892 | |
| 893 | $move_along_Y = ($pool, $l_balances, $dXn, $Leverage, $pool_props, $inverted) => { |
| 894 | $Lambda = $pool_props.Lambda; |
| 895 | $a = $inverted ? $pool_props.beta : $pool_props.alpha; |
| 896 | $b = 1 - $a; |
| 897 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 898 | $l_bal_direction = $dXn < 0 ? "grow" : "fall"; |
| 899 | |
| 900 | $P = $a/$b * $pool.Y / $pool.X; |
| 901 | $final_Xn = $pool.Xn + $dXn; |
| 902 | require($final_Xn > 0, "along Y: final_Xn=" || $final_Xn); |
| 903 | $delta_X = -($a*$Lambda-1)/$b * $dXn; |
| 904 | $final_X = $pool.X + $delta_X; |
| 905 | require($final_X > 0, "along Y: final_X=" || $final_X); |
| 906 | $final_Y = $get_final_y_along_y($pool.X, $pool.Y, $final_X, $pool_props, $inverted); |
| 907 | $final_Yn = $final_Y/$Lambda; |
| 908 | $final_P = $a/$b * $final_Y / $final_X; |
| 909 | |
| 910 | $sums = $update_other_l_balances_and_get_sums($l_balances, $P, $final_P, $Leverage, $inverted); |
| 911 | |
| 912 | |
| 913 | $b2 = $sums.initial - $b/$a/$Lambda*$pool.X; |
| 914 | $new_l_balance = $Leverage/($Leverage-1) * ( -$sums.final + $b/$a/$Lambda*$final_X + $b2 * ($final_X/$pool.X)^(-1/($a*$Lambda-1)) ); |
| 915 | require($new_l_balance >= 0, "along Y: new_l_balance=" || $new_l_balance); |
| 916 | $delta_l_balance = $new_l_balance - $l_balances[$L_key].balance; |
| 917 | require($delta_l_balance * $dXn < 0, "along y l-bal should "||$l_bal_direction||", got new " || $new_l_balance || ", delta " || $delta_l_balance); |
| 918 | $l_balances[$L_key].balance = $new_l_balance; |
| 919 | |
| 920 | $pool.X = $final_X; |
| 921 | $pool.Y = $final_Y; |
| 922 | $pool.Xn = $final_Xn; |
| 923 | $pool.Yn = $final_Yn; |
| 924 | $pool.delta_XL = $pool.delta_XL + $sums.delta_XL; |
| 925 | $pool.XL_denom = $sums.XL_denom + $new_l_balance * ($Leverage-1); |
| 926 | $pool.YL_denom = $sums.YL_denom; |
| 927 | $pool.PL1_ratio = $pool.PL1_ratio * $sums.PL1_ratio; |
| 928 | }; |
| 929 | |
| 930 | |
| 931 | |
| 932 | |
| 933 | |
| 934 | $trade_l_shares = ($balances, $l_balances, $profits, $recent, $x0, $y0, $Leverage, $asset, $delta_Xn, $entry_price, $trigger_initial_address, $pool_props) => { |
| 935 | |
| 936 | require($asset == $pool_props.x_asset OR $asset == $pool_props.y_asset, "wrong asset"); |
| 937 | require($Leverage == 2 OR $Leverage == 5 OR $Leverage == 10 OR $Leverage == 20 OR $Leverage == 50 OR $Leverage == 100, "bad L"); |
| 938 | $Lambda = $pool_props.Lambda; |
| 939 | |
| 940 | if ($asset == $pool_props.x_asset){ |
| 941 | $inverted = false; |
| 942 | $X = $balances.x; |
| 943 | $Y = $balances.y; |
| 944 | $Xn = $balances.xn; |
| 945 | $Yn = $balances.yn; |
| 946 | $X0 = $x0; |
| 947 | $Y0 = $y0; |
| 948 | $a = $pool_props.alpha; |
| 949 | $b = $pool_props.beta; |
| 950 | $token = 'x'; |
| 951 | } |
| 952 | else{ |
| 953 | $inverted = true; |
| 954 | $X = $balances.y; |
| 955 | $Y = $balances.x; |
| 956 | $Xn = $balances.yn; |
| 957 | $Yn = $balances.xn; |
| 958 | $X0 = $y0; |
| 959 | $Y0 = $x0; |
| 960 | $a = $pool_props.beta; |
| 961 | $b = $pool_props.alpha; |
| 962 | $token = 'y'; |
| 963 | } |
| 964 | $direct = !$inverted; |
| 965 | require($Xn + $delta_Xn > 0, "Xn balance would be negative"); |
| 966 | $L_key = ($inverted ? -$Leverage : $Leverage) || 'x'; |
| 967 | $pool = {X: $X, Y: $Y, Xn: $Xn, Yn: $Yn, delta_XL: 0, PL1_ratio: 1}; |
| 968 | |
| 969 | |
| 970 | $initial_l_balance = $l_balances[$L_key].balance; |
| 971 | $initial_shares = $l_balances[$L_key].supply; |
| 972 | |
| 973 | |
| 974 | $initial_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); |
| 975 | |
| 976 | if ($Lambda == 1) |
| 977 | this_address#3.$move_unleveraged($pool, $l_balances, $X0, $Y0, $delta_Xn, $Leverage, $pool_props, $inverted); |
| 978 | else { |
| 979 | $underleveraged = $Xn > ceil($X/$Lambda); |
| 980 | if (!$underleveraged){ |
| 981 | if ($delta_Xn > 0){ |
| 982 | $delta_Xn_inflection = $X * (( $Lambda/($Lambda-1) * ($a + ($b * $Lambda - 1) * $Yn/$Y) )^($b * $Lambda/($b*$Lambda-1)) - 1) / $Lambda; |
| 983 | $inflected = $delta_Xn > $delta_Xn_inflection; |
| 984 | } |
| 985 | $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; |
| 986 | this_address#3.$move_along_X($pool, $l_balances, $dXn1, $Leverage, $pool_props, $inverted); |
| 987 | if ($inflected) |
| 988 | this_address#3.$move_along_Y($pool, $l_balances, $delta_Xn - $delta_Xn_inflection, $Leverage, $pool_props, $inverted); |
| 989 | } |
| 990 | else{ |
| 991 | if ($delta_Xn < 0){ |
| 992 | $delta_Xn_inflection = -$b/($Lambda-1) * ($Lambda*$Xn - $X); |
| 993 | $inflected = abs($delta_Xn) > abs($delta_Xn_inflection); |
| 994 | } |
| 995 | $dXn1 = $inflected ? $delta_Xn_inflection : $delta_Xn; |
| 996 | this_address#3.$move_along_Y($pool, $l_balances, $dXn1, $Leverage, $pool_props, $inverted); |
| 997 | if ($inflected) |
| 998 | this_address#3.$move_along_X($pool, $l_balances, $delta_Xn - $delta_Xn_inflection, $Leverage, $pool_props, $inverted); |
| 999 | } |
| 1000 | } |
| 1001 | |
| 1002 | $final_l_balance = $l_balances[$L_key].balance; |
| 1003 | $delta_l_balance = $final_l_balance - $initial_l_balance; |
| 1004 | $final_P = $a/$b * ($pool.Y + $Y0) / ($pool.X + $X0); |
| 1005 | |
| 1006 | |
| 1007 | $net_delta = $delta_Xn + $pool.delta_XL + $delta_l_balance; |
| 1008 | |
| 1009 | $final_shares = $initial_shares |
| 1010 | ? floor($final_l_balance/$initial_l_balance / $pool.PL1_ratio * $initial_shares) |
| 1011 | : round($net_delta); |
| 1012 | $shares = $final_shares - $initial_shares; |
| 1013 | $l_balances[$L_key].supply = $final_shares; |
| 1014 | $avg_share_price = $net_delta/$shares; |
| 1015 | |
| 1016 | if ($final_shares == 0){ |
| 1017 | require($final_l_balance >= 0, "negative final l-balance after redeeming all shares: "||$final_l_balance); |
| 1018 | |
| 1019 | |
| 1020 | |
| 1021 | |
| 1022 | } |
| 1023 | |
| 1024 | $denom = 1 - $pool.XL_denom/$b/($pool.X+$X0) - $pool.YL_denom/$a/($pool.Y+$Y0); |
| 1025 | |
| 1026 | require($denom >= $singularity_threshold, "too close to the singularity point, denom="||$denom||", need more liquidity in order to buy this amount of L-tokens"); |
| 1027 | |
| 1028 | $balances.x = $inverted ? $pool.Y : $pool.X; |
| 1029 | $balances.y = $inverted ? $pool.X : $pool.Y; |
| 1030 | $balances.xn = $inverted ? $pool.Yn : $pool.Xn; |
| 1031 | $balances.yn = $inverted ? $pool.Xn : $pool.Yn; |
| 1032 | |
| 1033 | |
| 1034 | if ($recent.last_trade AND $recent.last_trade.address == $trigger_initial_address AND $recent.last_ts >= timestamp - $trade_merge_period){ |
| 1035 | $min_P = min($initial_P, $final_P, $direct ? $recent.last_trade.pmin : 1/$recent.last_trade.pmax); |
| 1036 | $max_P = max($initial_P, $final_P, $direct ? $recent.last_trade.pmax : 1/$recent.last_trade.pmin); |
| 1037 | $recent_traded_amount = $recent.last_trade.amounts[$token]; |
| 1038 | $recent_paid_tax = $recent.last_trade.paid_taxes[$token]; |
| 1039 | } |
| 1040 | else{ |
| 1041 | $min_P = min($initial_P, $final_P); |
| 1042 | $max_P = max($initial_P, $final_P); |
| 1043 | } |
| 1044 | $arb_profit_in_Y = ($max_P - $min_P) * ($recent_traded_amount + abs($net_delta)) / 2; |
| 1045 | |
| 1046 | |
| 1047 | $arb_profit_in_X = $arb_profit_in_Y / $min_P; |
| 1048 | $arb_profit_tax = $arb_profit_in_X * $pool_props.arb_profit_tax - $recent_paid_tax; |
| 1049 | require($arb_profit_tax >= 0, "negative arb profit tax"); |
| 1050 | |
| 1051 | $swap_fee = abs($net_delta) * $pool_props.swap_fee; |
| 1052 | |
| 1053 | if ($delta_Xn > 0){ |
| 1054 | $gross_asset_out = -$net_delta; |
| 1055 | require($gross_asset_out > 0, "asset out must be positive, got " || $gross_asset_out); |
| 1056 | if ($entry_price){ |
| 1057 | |
| 1058 | $l_tax = max(($avg_share_price - $entry_price)*(-$shares)*$pool_props.leverage_profit_tax, 0); |
| 1059 | } |
| 1060 | else |
| 1061 | $l_tax = $gross_asset_out * $pool_props.leverage_token_tax; |
| 1062 | } |
| 1063 | |
| 1064 | |
| 1065 | $subtotal_fee = $arb_profit_tax + $swap_fee + $l_tax; |
| 1066 | $gross_delta_exact = $net_delta + $subtotal_fee; |
| 1067 | $gross_delta = ceil($gross_delta_exact); |
| 1068 | $rounding_fee = $gross_delta - $gross_delta_exact; |
| 1069 | $total_fee = $subtotal_fee + $rounding_fee; |
| 1070 | |
| 1071 | |
| 1072 | $add_net_balance_without_changing_price($balances, $profits, $token, $total_fee, $Lambda); |
| 1073 | |
| 1074 | |
| 1075 | $update_recent_data($recent, $inverted ? 1/$initial_P : $initial_P, $inverted ? 1/$final_P : $final_P, $trigger_initial_address, $token, abs($net_delta), $arb_profit_tax, $pool_props.period_length); |
| 1076 | |
| 1077 | $event = json_stringify({ |
| 1078 | type: 'leverage', |
| 1079 | token: $token, |
| 1080 | L: $Leverage, |
| 1081 | shares: $shares, |
| 1082 | amount: $gross_delta, |
| 1083 | swap_fee: $swap_fee, |
| 1084 | arb_profit_tax: $arb_profit_tax, |
| 1085 | l_tax: $l_tax, |
| 1086 | total_fee: $total_fee, |
| 1087 | }); |
| 1088 | |
| 1089 | { |
| 1090 | shares: $shares, |
| 1091 | net_delta: $net_delta, |
| 1092 | gross_delta: $gross_delta, |
| 1093 | avg_share_price: $avg_share_price, |
| 1094 | arb_profit_tax: $arb_profit_tax, |
| 1095 | l_tax: $l_tax, |
| 1096 | swap_fee: $swap_fee, |
| 1097 | total_fee: $total_fee, |
| 1098 | initial_price: $initial_P, |
| 1099 | final_price: $final_P, |
| 1100 | event: $event, |
| 1101 | } |
| 1102 | }; |
| 1103 | |
| 1104 | |
| 1105 | $handle_trade_l_shares_request = ($pool_aa, $balances, $l_balances, $profits, $recent, $x0, $y0, $trigger_data, $trigger_address, $trigger_outputs, $trigger_initial_address, $pool_props) => { |
| 1106 | $x_asset = $pool_props.x_asset; |
| 1107 | $y_asset = $pool_props.y_asset; |
| 1108 | |
| 1109 | $asset = $trigger_data.asset == 'x' ? $x_asset : ($trigger_data.asset == 'y' ? $y_asset : $trigger_data.asset); |
| 1110 | $L = $trigger_data.L; |
| 1111 | $buy = $trigger_data.buy; |
| 1112 | $sell = $trigger_data.sell; |
| 1113 | $delta = $trigger_data.delta; |
| 1114 | $received_amount = $trigger_outputs[$asset]; |
| 1115 | $min_received_amount = $asset == 'base' ? 10000 : 0; |
| 1116 | $net_received_amount = $received_amount - $min_received_amount; |
| 1117 | |
| 1118 | require(!($buy AND $sell), "buy or sell?"); |
| 1119 | require($delta > 0, "delta must be positive"); |
| 1120 | if ($buy) |
| 1121 | require($net_received_amount > 0, "you forgot to pay"); |
| 1122 | else |
| 1123 | require($net_received_amount == 0, "don't send asset"); |
| 1124 | $delta_Xn = $buy ? -$delta : $delta; |
| 1125 | $asset_label = $asset == $x_asset ? 'x' : 'y'; |
| 1126 | |
| 1127 | $signedL = $asset_label == 'x' ? $L : -$L; |
| 1128 | if ($buy AND $trigger_data.tokens OR $sell AND !$trigger_data.position){ |
| 1129 | $l_shares_asset = var[$pool_aa]['leveraged_asset' || $signedL]; |
| 1130 | require($l_shares_asset, "please define an asset for the leveraged token first"); |
| 1131 | } |
| 1132 | if ($sell){ |
| 1133 | if ($trigger_data.position){ |
| 1134 | $position = var[$pool_aa][$trigger_data.position]; |
| 1135 | require($position, "no such position"); |
| 1136 | require($position.owner == $trigger_address, "you are not the owner of this position"); |
| 1137 | $parts = split($trigger_data.position, '_'); |
| 1138 | require(+$parts[1] == $signedL, "wrong L"); |
| 1139 | $shares_in = $position.shares; |
| 1140 | } |
| 1141 | else{ |
| 1142 | $shares_in = $trigger_outputs[$l_shares_asset]; |
| 1143 | require($shares_in > 0, "no leveraged tokens received"); |
| 1144 | } |
| 1145 | } |
| 1146 | |
| 1147 | $res = $trade_l_shares($balances, $l_balances, $profits, $recent, $x0, $y0, $L, $asset, $delta_Xn, $position.price, $trigger_initial_address, $pool_props); |
| 1148 | |
| 1149 | |
| 1150 | $shares = $res.shares; |
| 1151 | $gross_delta = $res.gross_delta; |
| 1152 | |
| 1153 | if ($buy){ |
| 1154 | $asset_out = $received_amount - $gross_delta; |
| 1155 | require($asset_out >= 0, "expected " || $gross_delta || ", received " || $received_amount); |
| 1156 | } |
| 1157 | else{ |
| 1158 | $shares_change = $shares_in + $shares; |
| 1159 | require($shares_change >= 0, "expected " || (-$shares) || " shares, received " || $shares_in); |
| 1160 | $asset_out = -$gross_delta; |
| 1161 | } |
| 1162 | |
| 1163 | $res.signedL = $signedL; |
| 1164 | $res.asset_label = $asset_label; |
| 1165 | $res.asset = $asset; |
| 1166 | $res.l_shares_asset = $l_shares_asset; |
| 1167 | $res.position = $position; |
| 1168 | $res.shares_change = $shares_change; |
| 1169 | $res.asset_out = $asset_out; |
| 1170 | $res |
| 1171 | }; |
| 1172 | |
| 1173 | |
| 1174 | |
| 1175 | $validate_and_apply_new_governed_param_value = ($name, $value, $balances, $l_balances, $profits, $lp_shares, $pool_props, $locked_governance) => { |
| 1176 | |
| 1177 | if ($locked_governance) |
| 1178 | require(!$locked_governance[$name], "governance is not allowed to change "||$name); |
| 1179 | |
| 1180 | $Lambda = $pool_props.Lambda; |
| 1181 | $alpha = $pool_props.alpha; |
| 1182 | $beta = $pool_props.beta; |
| 1183 | $gamma = $pool_props.gamma; |
| 1184 | $mid_price = $pool_props.mid_price; |
| 1185 | $mid_price_beta = $pool_props.mid_price_beta; |
| 1186 | |
| 1187 | $s_curve = $lp_shares.linear * $lp_shares.coef; |
| 1188 | $x0 = $mid_price ? $s_curve / $mid_price_beta / $gamma : 0; |
| 1189 | $y0 = $x0 * $mid_price; |
| 1190 | |
| 1191 | if ($name == 'pool_leverage'){ |
| 1192 | require($profits.x < 1 AND $profits.y < 1, "profits must be added to the pool before changing pool_leverage"); |
| 1193 | $profits.x = 0; |
| 1194 | $profits.y = 0; |
| 1195 | require($alpha != 1/$value, "pool leverage = 1/alpha"); |
| 1196 | require($beta != 1/$value, "pool leverage = 1/beta"); |
| 1197 | require($value != $Lambda, "same Lambda"); |
| 1198 | if ($value > 1) |
| 1199 | require(!$mid_price, "price range setting is incompatible with new pool leverage"); |
| 1200 | $balances.x = $balances.x * $value/$Lambda; |
| 1201 | $balances.y = $balances.y * $value/$Lambda; |
| 1202 | if ($value == 1){ |
| 1203 | |
| 1204 | $profits.x = $profits.x + $balances.xn - $balances.x; |
| 1205 | $profits.y = $profits.y + $balances.yn - $balances.y; |
| 1206 | $balances.xn = $balances.x; |
| 1207 | $balances.yn = $balances.y; |
| 1208 | } |
| 1209 | |
| 1210 | } |
| 1211 | else if ($name == 'mid_price' OR $name == 'price_deviation'){ |
| 1212 | require($value AND $mid_price, $name||" must be nonzero"); |
| 1213 | require($alpha == 0.5, "equal weights only"); |
| 1214 | if ($name == 'price_deviation'){ |
| 1215 | require($value > 1, "price deviation must be > 1"); |
| 1216 | $new_p0 = $mid_price; |
| 1217 | $new_gamma = $value; |
| 1218 | } |
| 1219 | else{ |
| 1220 | $new_p0 = $value; |
| 1221 | $new_gamma = $gamma; |
| 1222 | } |
| 1223 | $sqp = $mid_price_beta; |
| 1224 | $new_sqp = sqrt($new_p0); |
| 1225 | $x = $balances.x; |
| 1226 | $y = $balances.y; |
| 1227 | |
| 1228 | |
| 1229 | $new_s1 = 1 / (1/$s_curve + (1/$gamma/$sqp - 1/$new_gamma/$new_sqp) / $x); |
| 1230 | $new_y = $new_s1 * ($y/$s_curve + $sqp/$gamma - $new_sqp/$new_gamma); |
| 1231 | if ($new_y <= $y){ |
| 1232 | require($new_y > 0, "new y is negative"); |
| 1233 | require($new_s1 > 0, "new s1 is negative"); |
| 1234 | $profits.y = $profits.y + $y - $new_y; |
| 1235 | $balances.y = $new_y; |
| 1236 | $balances.yn = $new_y; |
| 1237 | $lp_shares.coef = $lp_shares.coef * $new_s1/$s_curve; |
| 1238 | $new_s = $new_s1; |
| 1239 | } |
| 1240 | else{ |
| 1241 | $new_s2 = 1 / (1/$s_curve + ($sqp/$gamma - $new_sqp/$new_gamma) / $y); |
| 1242 | $new_x = $new_s2 * ($x/$s_curve + 1/$gamma/$sqp - 1/$new_gamma/$new_sqp); |
| 1243 | require($new_x <= $x, "can't adjust x and y to keep the price"); |
| 1244 | require($new_x > 0, "new x is negative"); |
| 1245 | require($new_s2 > 0, "new s2 is negative"); |
| 1246 | $profits.x = $profits.x + $x - $new_x; |
| 1247 | $balances.x = $new_x; |
| 1248 | $balances.xn = $new_x; |
| 1249 | $lp_shares.coef = $lp_shares.coef * $new_s2/$s_curve; |
| 1250 | $new_s = $new_s2; |
| 1251 | } |
| 1252 | $new_x0 = $new_s / $new_sqp / $new_gamma; |
| 1253 | $new_y0 = $new_s * $new_sqp / $new_gamma; |
| 1254 | |
| 1255 | /* |
| 1256 | $sqp = sqrt($new_p0); |
| 1257 | $b = ($balances.x/$s_curve*$sqp + $balances.y/$s_curve/$sqp)/$new_gamma; |
| 1258 | $a = $balances.x*$balances.y/$s_curve/$s_curve; |
| 1259 | $lp_shares.coef = $lp_shares.coef / (-$b + sqrt($b*$b - 4*$a*(1/$new_gamma/$new_gamma-1)))*2*$a; |
| 1260 | */ |
| 1261 | |
| 1262 | |
| 1263 | } |
| 1264 | else if ($name == 'alpha'){ |
| 1265 | require(!$mid_price, "can't change token weights while trading in limited range"); |
| 1266 | $new_alpha = $value; |
| 1267 | $new_beta = 1 - $new_alpha; |
| 1268 | require($new_alpha != 1/$Lambda AND $new_beta != 1/$Lambda, "pool leverage = 1/alpha or 1/beta"); |
| 1269 | |
| 1270 | |
| 1271 | |
| 1272 | |
| 1273 | $new_y2x = $new_beta/$new_alpha * $alpha/$beta * $balances.y/$balances.x; |
| 1274 | if ($Lambda > 1){ |
| 1275 | if ($balances.xn * $Lambda * $new_y2x <= $balances.yn * $Lambda){ |
| 1276 | $balances.x = $balances.xn * $Lambda; |
| 1277 | $balances.y = $balances.x * $new_y2x; |
| 1278 | } |
| 1279 | else if ($balances.yn * $Lambda / $new_y2x <= $balances.xn * $Lambda){ |
| 1280 | $balances.y = $balances.yn * $Lambda; |
| 1281 | $balances.x = $balances.y / $new_y2x; |
| 1282 | } |
| 1283 | else |
| 1284 | bounce("can't preserve the price"); |
| 1285 | } |
| 1286 | else { |
| 1287 | $new_y = $balances.xn * $new_y2x; |
| 1288 | if ($new_y <= $balances.yn){ |
| 1289 | $profits.y = $profits.y + $balances.yn - $new_y; |
| 1290 | $balances.yn = $new_y; |
| 1291 | $balances.y = $new_y; |
| 1292 | } |
| 1293 | else { |
| 1294 | $new_x = $balances.yn / $new_y2x; |
| 1295 | require($new_x <= $balances.xn, "neither excessive x nor excessive y"); |
| 1296 | $profits.x = $profits.x + $balances.xn - $new_x; |
| 1297 | $balances.xn = $new_x; |
| 1298 | $balances.x = $new_x; |
| 1299 | } |
| 1300 | } |
| 1301 | |
| 1302 | } |
| 1303 | |
| 1304 | |
| 1305 | $denom = 1 - $get_utilization_ratio($balances, $l_balances, $new_x0 OTHERWISE $x0, $new_y0 OTHERWISE $y0, $new_alpha OTHERWISE $alpha); |
| 1306 | require($denom >= $singularity_threshold, "new balances would bring us too close to the singularity point, denom="||$denom); |
| 1307 | }; |
| 1308 | |
| 1309 | |
| 1310 | |
| 1311 | }", |
| 1312 | "messages": [ |
| 1313 | { |
| 1314 | "app": "state", |
| 1315 | "state": "{ |
| 1316 | |
| 1317 | |
| 1318 | |
| 1319 | |
| 1320 | |
| 1321 | $h = $handle_trade_l_shares_request(); |
| 1322 | |
| 1323 | |
| 1324 | |
| 1325 | |
| 1326 | bounce("library only"); |
| 1327 | }" |
| 1328 | } |
| 1329 | ] |
| 1330 | } |
| 1331 | ] |