Skip to content

Commit

Permalink
feat: add market params notebook
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoreira-valory committed May 14, 2024
1 parent f3bc82d commit a8e9bcf
Showing 1 changed file with 303 additions and 0 deletions.
303 changes: 303 additions & 0 deletions scripts/market_parameters.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "7216b095",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# ------------------------------------------------------------------------------\n",
"#\n",
"# Copyright 2024 Valory AG\n",
"#\n",
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
"# you may not use this file except in compliance with the License.\n",
"# You may obtain a copy of the License at\n",
"#\n",
"# http://www.apache.org/licenses/LICENSE-2.0\n",
"#\n",
"# Unless required by applicable law or agreed to in writing, software\n",
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
"# See the License for the specific language governing permissions and\n",
"# limitations under the License.\n",
"#\n",
"# ------------------------------------------------------------------------------\n",
"\n",
"\"\"\"Script to simulate markets.\"\"\"\n",
"\n",
"import random\n",
"from collections import Counter\n",
"from typing import Any, List\n",
"\n",
"import ipympl\n",
"import ipywidgets as widgets\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"from IPython.display import display\n",
"from mpl_toolkits.mplot3d import Axes3D\n",
"from omen_subgraph_utils import get_fpmms\n",
"from sage.misc.prandom import shuffle as sage_shuffle\n",
"from scipy.stats import describe\n",
"\n",
"# Enable matplotlib inline backend\n",
"%matplotlib widget\n",
"\n",
"\n",
"fpmms = get_fpmms(\"0x89c5cc945dd550BcFfb72Fe42BfF002429F46Fec\")\n",
"\n",
"\n",
"def plot_histogram(values: List[Any], title: str) -> None:\n",
" \"\"\"Plot histogram and cumulative histogram.\"\"\"\n",
" summary = describe(values)\n",
" print(summary)\n",
"\n",
" plt.figure(figsize=(12, 5))\n",
" n_bins = 200\n",
" bin_size = (max(values) - min(values)) / n_bins \n",
" bins = np.arange(min(values), max(values)+bin_size, bin_size)\n",
"\n",
" # Plot histogram\n",
" plt.subplot(1, 2, 1)\n",
" plt.hist(values, bins=bins, color='blue', density=True)\n",
" plt.xlabel('Values')\n",
" plt.ylabel('Count')\n",
" plt.title(f'Histogram of {title}')\n",
"\n",
" # Plot cumulative histogram\n",
" plt.subplot(1, 2, 2)\n",
" plt.hist(values, bins=bins, color='orange', density=True, cumulative=True)\n",
" plt.xlabel('Values')\n",
" plt.ylabel('Cumulative count')\n",
" plt.title(f'Cumulative histogram of {title}')\n",
"\n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
"\n",
"def fpmm_trade(amount: float, option: int, L0: float, L1: float, fee: float=0.02) -> (int, int, int):\n",
" \"\"\"Simulates an fpmm trade with liquidity L0 and L1 for options 1 and 2, respectively.\"\"\"\n",
" k = L0*L1\n",
" amount = amount*(1-fee)\n",
" if option == 0:\n",
" return (L0+amount, (L0*L1/(L0+amount)), amount + L1-(k/(L0+amount)))\n",
" else:\n",
" return ((k/(L1+amount)), L1+amount, amount + L0-(k/(L1+amount)))\n",
"\n",
"\n",
"def fpmm_trade_all(trades: List[Any], L0: float, L1: float, fee: float=0.02) -> (float, float):\n",
" \"\"\"Executes a cycle of trades on an fpmm.\"\"\"\n",
" for val, opt in trades:\n",
" (L0, L1, rcv) = fpmm_trade(val, opt, L0, L1, fee)\n",
" #print(L0, L1, rcv, L0*L1)\n",
" return (L0, L1)\n",
"\n",
"\n",
"def simulate(nTrades0: int, nTrades1: int, amount: float, L0: float, L1: float, fee: float=0.02, iterations: int=1) -> float:\n",
" \"\"\"Simulates an fpmm.\"\"\"\n",
" p0 = 0.0\n",
" trades0 = [(amount,0)]*nTrades0\n",
" trades1 = [(amount,1)]*nTrades1\n",
" trades = trades0 + trades1\n",
"\n",
" for _ in range(iterations):\n",
" random.shuffle(trades)\n",
" (L0_result, L1_result) = fpmm_trade_all(trades, L0, L1, fee)\n",
" # The market probability of an option is proportional to its liquidity\n",
" probability = L0_result / (L0_result + L1_result)\n",
" p0 += probability\n",
" \n",
" return p0 / iterations\n",
"\n",
"\n",
"def test() -> None:\n",
" trades = [(1.,0), (1.,0), (0.8,0), (0.8,0), (0.8,0), (0.32,0)]\n",
" L0 = L1 = 10\n",
" (L0, L1) = fpmm_trade_all(trades, L0, L1)\n",
" p = L0/(L0+L1)\n",
" expected_value = 0.6814355029609638\n",
" tolerance = 1e-6\n",
" assert abs(expected_value - p) < tolerance, \"Market simulation do not match expected result.\"\n",
"\n",
"test()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b426f970",
"metadata": {},
"outputs": [],
"source": [
"# Trades per market\n",
"trades_per_market = [len(market_data.get('trades', {})) for market_data in fpmms.values()]\n",
"plot_histogram(trades_per_market, \"Trades per market\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8c9d5919",
"metadata": {},
"outputs": [],
"source": [
"# Collateral amounts invested\n",
"collateral_amounts = []\n",
"for market_data in fpmms.values():\n",
" trades = market_data.get(\"trades\", {})\n",
" for trade_data in trades.values():\n",
" collateral_amounts.append(float(trade_data[\"collateralAmount\"])/1e18)\n",
"\n",
"summary = describe(collateral_amounts)\n",
"print(summary)\n",
"limit = 2.0\n",
"print(len(collateral_amounts))\n",
"collateral_amounts = [x for x in collateral_amounts if x <= limit]\n",
"print(len(collateral_amounts))\n",
"\n",
"plot_histogram(collateral_amounts, \"Collateral amounts per bet (XDAI)\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7b11067a",
"metadata": {},
"outputs": [],
"source": [
"# Mech consistency\n",
"consistency = []\n",
"for market_data in fpmms.values():\n",
" trades = market_data.get(\"trades\", {})\n",
" \n",
" if trades:\n",
" outcome_index_counts = Counter(trade_data[\"outcomeIndex\"] for trade_data in trades.values())\n",
" majority_outcome_index, majority_count = max(outcome_index_counts.items(), key=lambda x: x[1])\n",
" consistency_percentage = (majority_count / len(trades)) * 100\n",
" consistency.append(consistency_percentage)\n",
"\n",
"plot_histogram(consistency, \"Mech consistency\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "738dcc8a",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# Simulate markets\n",
"\n",
"plt.ion()\n",
"\n",
"# Create the figure and subplots\n",
"fig = plt.figure(figsize=(10, 5))\n",
"fig.subplots_adjust(wspace=0.4)\n",
"ax1 = fig.add_subplot(121, projection='3d')\n",
"ax2 = fig.add_subplot(122)\n",
"surf = None\n",
"ax1.set_xlabel('nTrades0')\n",
"ax1.set_ylabel('nTrades1')\n",
"ax1.set_zlabel('Market probability')\n",
"ax2.set_xlabel('nTrades')\n",
"ax2.set_ylabel('Market probability')\n",
"ax2.set_title('nTrades0 = nTrades * mech_consistency')\n",
"\n",
"def update_plot(amount, liquidity, nTrades, mech_consistency, iterations, fee):\n",
" global results, surf, surf_plane\n",
" \n",
" L0 = liquidity\n",
" L1 = liquidity\n",
" \n",
" nTrades0 = int(nTrades * mech_consistency)\n",
" nTrades1 = nTrades - nTrades0\n",
" \n",
" nTrades0_range = range(nTrades0+1)\n",
" nTrades1_range = range(nTrades1+1)\n",
" nTrades0_values, nTrades1_values = np.meshgrid(nTrades0_range, nTrades1_range)\n",
"\n",
" # Flatten the arrays\n",
" nTrades0_values_flat = nTrades0_values.flatten()\n",
" nTrades1_values_flat = nTrades1_values.flatten()\n",
" \n",
" results = np.array([simulate(nTrades0, nTrades1, amount, L0, L1, fee, iterations) for nTrades0, nTrades1 in zip(nTrades0_values_flat, nTrades1_values_flat)])\n",
" results = results.reshape(nTrades0_values.shape)\n",
"\n",
" if surf is not None:\n",
" surf.remove()\n",
" surf = ax1.plot_surface(nTrades0_values, nTrades1_values, results, cmap='viridis')\n",
" ax1.set_xlim(0, nTrades0)\n",
" ax1.set_ylim(0, nTrades1)\n",
" ax1.set_zlim(0, 1)\n",
" \n",
" if 'surf_plane' in globals():\n",
" surf_plane.remove()\n",
" surf_plane = ax1.plot_surface(nTrades0_values, nTrades1_values, np.full_like(results, mech_consistency), color='red', alpha=0.5)\n",
"\n",
"\n",
" # Plot the diagonal of results\n",
" results2 = np.array([results[int(t - int(t * mech_consistency)), int(t * mech_consistency)] for t in range(nTrades)])\n",
" ax2.clear()\n",
" ax2.plot(results2)\n",
" ax2.axhline(y=mech_consistency, color='red', linestyle='--')\n",
" ax2.set_xlim(0, nTrades)\n",
" ax2.set_ylim(0, 1)\n",
"\n",
"\n",
"# Create a slider widget for amount\n",
"liquidity_slider = widgets.FloatSlider(value=10, min=0, max=10, step=0.1, description='Liquidity')\n",
"amount_slider = widgets.FloatSlider(value=1, min=0, max=10, step=0.01, description='Bet amount')\n",
"nTrades_slider = widgets.IntSlider(value=30, min=0, max=200, step=1, description='nTrades')\n",
"mech_consistency_slider = widgets.FloatSlider(value=0.75, min=0, max=1, step=0.01, description='Mech consistency')\n",
"iterations_slider = widgets.IntSlider(value=1, min=1, max=50, step=1, description='Iterations')\n",
"fee_slider = widgets.FloatSlider(value=0.02, min=0, max=1, step=0.01, description='Market fee')\n",
"widgets.interactive(update_plot, liquidity=liquidity_slider, amount=amount_slider, nTrades=nTrades_slider, mech_consistency=mech_consistency_slider, fee=fee_slider, iterations=iterations_slider)\n"
]
},
{
"cell_type": "markdown",
"id": "d29197d6",
"metadata": {},
"source": [
"Definitions:\n",
"- Market probability: Liquidity of the most voted option over the total liquidity. Ideally it should match the \"population's belief\" based on the trades.\n",
"- Market accuracy: The accuracy of the market in predicting the ground truth once resolved.\n",
"- Mech consistency: Percentage of the most voted option provided by the mech. This is the \"population's belief\".\n",
"\n",
"Some observations:\n",
"\n",
"- The market probability reflects what the traders think, which may or may not coincide with the actual result of the market once it is resolved. \n",
"- The mech is abstracted as the collection of all the tools to which the traders interact.\n",
"- Asymptotically, i.e., with a large enough number of trades and large enough bet amounts, the market probability tends to the mech consistency. This is the best result we can hope for, since the market has no way to know \"the ground truth\" until it is resolved.\n",
"- This process can be \"speed up\" by either increasing the number of trades in the market, or by increasing the amount per trade, relative to the market liquidity.\n",
"- The simulation above does not distinguish between options (the problem is symmetrical)."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

0 comments on commit a8e9bcf

Please sign in to comment.