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