Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add market params notebook #104

Merged
merged 4 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
Loading
Loading