348 lines
11 KiB
Plaintext
348 lines
11 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Primitives Gallery\n",
|
|
"\n",
|
|
"Visual tour of all bonding curve primitives in the MYCO stack."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import numpy as np\n",
|
|
"import matplotlib.pyplot as plt\n",
|
|
"from matplotlib import cm\n",
|
|
"\n",
|
|
"%matplotlib inline\n",
|
|
"plt.rcParams['figure.figsize'] = (12, 5)\n",
|
|
"plt.rcParams['figure.dpi'] = 100"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 1. Weighted Constant Product\n",
|
|
"\n",
|
|
"Balancer invariant: $I = \\prod_i b_i^{w_i}$"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from src.primitives.weighted_product import compute_invariant, calc_out_given_in, spot_price\n",
|
|
"\n",
|
|
"# Compare 50/50 vs 80/20 weight pools\n",
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
|
"\n",
|
|
"for ax, weights, title in [\n",
|
|
" (axes[0], np.array([0.5, 0.5]), '50/50 Pool'),\n",
|
|
" (axes[1], np.array([0.8, 0.2]), '80/20 Pool'),\n",
|
|
"]:\n",
|
|
" balances = np.array([1000.0, 1000.0])\n",
|
|
" amounts_in = np.linspace(1, 500, 100)\n",
|
|
" amounts_out = [calc_out_given_in(balances, weights, 0, 1, a) for a in amounts_in]\n",
|
|
" prices = [spot_price(np.array([balances[0] + a, balances[1] - o]), weights, 0, 1)\n",
|
|
" for a, o in zip(amounts_in, amounts_out)]\n",
|
|
"\n",
|
|
" ax.plot(amounts_in, amounts_out, 'b-', label='Amount out')\n",
|
|
" ax2 = ax.twinx()\n",
|
|
" ax2.plot(amounts_in, prices, 'r--', label='Spot price')\n",
|
|
" ax.set_xlabel('Amount in (token A)')\n",
|
|
" ax.set_ylabel('Amount out (token B)', color='b')\n",
|
|
" ax2.set_ylabel('Spot price (A/B)', color='r')\n",
|
|
" ax.set_title(f'Weighted Product — {title}')\n",
|
|
" ax.legend(loc='upper left')\n",
|
|
" ax2.legend(loc='upper right')\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 2. StableSwap\n",
|
|
"\n",
|
|
"Curve invariant blending constant-sum and constant-product."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from src.primitives.stableswap import compute_invariant as ss_invariant, calc_out_given_in as ss_swap\n",
|
|
"\n",
|
|
"fig, ax = plt.subplots(figsize=(10, 6))\n",
|
|
"\n",
|
|
"for A, color in [(1, 'red'), (10, 'orange'), (100, 'green'), (1000, 'blue')]:\n",
|
|
" balances = np.array([1000.0, 1000.0])\n",
|
|
" amounts_in = np.linspace(1, 800, 200)\n",
|
|
" amounts_out = [ss_swap(balances, A, 0, 1, a) for a in amounts_in]\n",
|
|
" ax.plot(amounts_in, amounts_out, color=color, label=f'A = {A}')\n",
|
|
"\n",
|
|
"ax.plot([0, 800], [0, 800], 'k:', alpha=0.3, label='1:1 line')\n",
|
|
"ax.set_xlabel('Amount in')\n",
|
|
"ax.set_ylabel('Amount out')\n",
|
|
"ax.set_title('StableSwap — Effect of Amplification Parameter A')\n",
|
|
"ax.legend()\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 3. Concentrated 2-CLP\n",
|
|
"\n",
|
|
"Gyroscope virtual-reserve concentrated liquidity."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from src.primitives.concentrated_2clp import (\n",
|
|
" TwoCLPParams, compute_invariant as clp_invariant,\n",
|
|
" calc_out_given_in as clp_swap, virtual_offset_x, virtual_offset_y,\n",
|
|
")\n",
|
|
"\n",
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
|
"\n",
|
|
"# Invariant curves for different price ranges\n",
|
|
"ax = axes[0]\n",
|
|
"for sqrt_alpha, sqrt_beta, label in [\n",
|
|
" (0.5, 2.0, 'Wide [0.25, 4.0]'),\n",
|
|
" (0.9, 1.11, 'Narrow [0.81, 1.23]'),\n",
|
|
" (0.99, 1.01, 'Very narrow [0.98, 1.02]'),\n",
|
|
"]:\n",
|
|
" params = TwoCLPParams(sqrt_alpha=sqrt_alpha, sqrt_beta=sqrt_beta)\n",
|
|
" balances = np.array([1000.0, 1000.0])\n",
|
|
" L = clp_invariant(balances, params)\n",
|
|
" a = virtual_offset_x(L, params)\n",
|
|
" b = virtual_offset_y(L, params)\n",
|
|
"\n",
|
|
" x_range = np.linspace(10, 2000, 500)\n",
|
|
" y_range = L**2 / (x_range + a) - b\n",
|
|
" valid = y_range > 0\n",
|
|
" ax.plot(x_range[valid], y_range[valid], label=label)\n",
|
|
"\n",
|
|
"ax.set_xlabel('Balance X')\n",
|
|
"ax.set_ylabel('Balance Y')\n",
|
|
"ax.set_title('2-CLP Invariant Curves')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"# Swap comparison\n",
|
|
"ax = axes[1]\n",
|
|
"for sqrt_alpha, sqrt_beta, label in [\n",
|
|
" (0.5, 2.0, 'Wide'),\n",
|
|
" (0.9, 1.11, 'Narrow'),\n",
|
|
"]:\n",
|
|
" params = TwoCLPParams(sqrt_alpha=sqrt_alpha, sqrt_beta=sqrt_beta)\n",
|
|
" balances = np.array([1000.0, 1000.0])\n",
|
|
" amounts_in = np.linspace(1, 500, 100)\n",
|
|
" amounts_out = [clp_swap(balances, params, 0, 1, a) for a in amounts_in]\n",
|
|
" ax.plot(amounts_in, amounts_out, label=label)\n",
|
|
"\n",
|
|
"ax.plot([0, 500], [0, 500], 'k:', alpha=0.3)\n",
|
|
"ax.set_xlabel('Amount in')\n",
|
|
"ax.set_ylabel('Amount out')\n",
|
|
"ax.set_title('2-CLP Swap Output by Concentration')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 4. Elliptical CLP (E-CLP)\n",
|
|
"\n",
|
|
"Gyroscope's A-matrix elliptical invariant."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from src.primitives.elliptical_clp import (\n",
|
|
" ECLPParams, compute_derived_params, compute_invariant as eclp_invariant,\n",
|
|
" calc_out_given_in as eclp_swap,\n",
|
|
")\n",
|
|
"\n",
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
|
"\n",
|
|
"# Effect of lambda\n",
|
|
"ax = axes[0]\n",
|
|
"for lam, label in [(1.0, 'λ=1 (circle)'), (2.0, 'λ=2'), (5.0, 'λ=5')]:\n",
|
|
" params = ECLPParams(alpha=0.9, beta=1.1, c=1.0, s=0.0, lam=lam)\n",
|
|
" derived = compute_derived_params(params)\n",
|
|
" balances = np.array([1000.0, 1000.0])\n",
|
|
" amounts_in = np.linspace(1, 400, 100)\n",
|
|
" amounts_out = [eclp_swap(balances, params, derived, 0, 1, a) for a in amounts_in]\n",
|
|
" ax.plot(amounts_in, amounts_out, label=label)\n",
|
|
"\n",
|
|
"ax.set_xlabel('Amount in')\n",
|
|
"ax.set_ylabel('Amount out')\n",
|
|
"ax.set_title('E-CLP — Effect of λ (stretch)')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"# Effect of rotation\n",
|
|
"ax = axes[1]\n",
|
|
"for angle, label in [(0, 'θ=0°'), (15, 'θ=15°'), (30, 'θ=30°'), (45, 'θ=45°')]:\n",
|
|
" rad = np.radians(angle)\n",
|
|
" params = ECLPParams(alpha=0.8, beta=1.2, c=np.cos(rad), s=np.sin(rad), lam=3.0)\n",
|
|
" derived = compute_derived_params(params)\n",
|
|
" balances = np.array([1000.0, 1000.0])\n",
|
|
" amounts_in = np.linspace(1, 400, 100)\n",
|
|
" amounts_out = [eclp_swap(balances, params, derived, 0, 1, a) for a in amounts_in]\n",
|
|
" ax.plot(amounts_in, amounts_out, label=label)\n",
|
|
"\n",
|
|
"ax.set_xlabel('Amount in')\n",
|
|
"ax.set_ylabel('Amount out')\n",
|
|
"ax.set_title('E-CLP — Effect of Rotation Angle')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 5. Reserve Management Primitives"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from src.primitives.redemption_curve import PAMMParams, PAMMState, compute_redemption_rate\n",
|
|
"\n",
|
|
"# P-AMM redemption rate vs backing ratio\n",
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
|
"\n",
|
|
"ax = axes[0]\n",
|
|
"params = PAMMParams()\n",
|
|
"ba_range = np.linspace(0.01, 1.5, 200)\n",
|
|
"rates = []\n",
|
|
"for ba in ba_range:\n",
|
|
" state = PAMMState(reserve_value=ba * 1000, myco_supply=1000)\n",
|
|
" rates.append(compute_redemption_rate(state, params, 10.0))\n",
|
|
"ax.plot(ba_range, rates, 'b-', linewidth=2)\n",
|
|
"ax.axhline(y=1.0, color='g', linestyle='--', alpha=0.5, label='Par (rate=1)')\n",
|
|
"ax.axhline(y=params.theta_bar, color='r', linestyle='--', alpha=0.5, label=f'Floor (θ̄={params.theta_bar})')\n",
|
|
"ax.axvline(x=1.0, color='gray', linestyle=':', alpha=0.5)\n",
|
|
"ax.set_xlabel('Backing Ratio')\n",
|
|
"ax.set_ylabel('Redemption Rate')\n",
|
|
"ax.set_title('P-AMM Redemption Curve')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"# Flow dampening\n",
|
|
"from src.primitives.flow_dampening import simulate_bank_run\n",
|
|
"\n",
|
|
"ax = axes[1]\n",
|
|
"result = simulate_bank_run(initial_reserve=10000, outflow_per_step=500, memory=0.99, steps=100)\n",
|
|
"ax.plot(result['reserve'], 'b-', label='Reserve value')\n",
|
|
"ax.plot(result['penalties'], 'r--', label='Flow penalty')\n",
|
|
"ax.set_xlabel('Time step')\n",
|
|
"ax.set_ylabel('Value')\n",
|
|
"ax.set_title('Flow Dampening During Bank Run')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## 6. Imbalance Fees & Dynamic Weights"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from src.primitives.imbalance_fees import compute_imbalance, surge_fee\n",
|
|
"from src.primitives.dynamic_weights import simulate_lbp, create_lbp_schedule\n",
|
|
"\n",
|
|
"fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n",
|
|
"\n",
|
|
"# Imbalance fee surface\n",
|
|
"ax = axes[0]\n",
|
|
"ratios = np.linspace(0.1, 5.0, 50)\n",
|
|
"fees = []\n",
|
|
"for r in ratios:\n",
|
|
" old = np.array([1000.0, 1000.0, 1000.0])\n",
|
|
" deposit = np.array([r * 100, 100.0, 100.0])\n",
|
|
" f = surge_fee(old, old + deposit, 0.003, 0.05, 0.2)\n",
|
|
" fees.append(f)\n",
|
|
"ax.plot(ratios, fees, 'b-', linewidth=2)\n",
|
|
"ax.set_xlabel('Deposit ratio (asset 0 vs others)')\n",
|
|
"ax.set_ylabel('Surge fee rate')\n",
|
|
"ax.set_title('Imbalance-Contingent Surge Fee')\n",
|
|
"\n",
|
|
"# LBP weight decay\n",
|
|
"ax = axes[1]\n",
|
|
"schedule = create_lbp_schedule(\n",
|
|
" start_weights=np.array([0.9, 0.1]),\n",
|
|
" end_weights=np.array([0.5, 0.5]),\n",
|
|
" start_time=0,\n",
|
|
" end_time=72,\n",
|
|
")\n",
|
|
"times = np.linspace(0, 72, 200)\n",
|
|
"w0 = [schedule.get_weight(t, 0) for t in times]\n",
|
|
"w1 = [schedule.get_weight(t, 1) for t in times]\n",
|
|
"ax.plot(times, w0, 'b-', label='Token weight')\n",
|
|
"ax.plot(times, w1, 'r-', label='Collateral weight')\n",
|
|
"ax.set_xlabel('Time (hours)')\n",
|
|
"ax.set_ylabel('Weight')\n",
|
|
"ax.set_title('LBP Weight Decay (72h launch)')\n",
|
|
"ax.legend()\n",
|
|
"\n",
|
|
"plt.tight_layout()\n",
|
|
"plt.show()"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"name": "python",
|
|
"version": "3.11.0"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 4
|
|
}
|