diff --git a/examples/monitor/match_bets_with_langfuse_traces.py b/examples/monitor/match_bets_with_langfuse_traces.py index 965e5d54..1942f634 100644 --- a/examples/monitor/match_bets_with_langfuse_traces.py +++ b/examples/monitor/match_bets_with_langfuse_traces.py @@ -17,6 +17,7 @@ ) from prediction_market_agent_tooling.markets.data_models import ResolvedBet from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket +from prediction_market_agent_tooling.tools.httpx_cached_client import HttpxCachedClient from prediction_market_agent_tooling.tools.langfuse_client_utils import ( ProcessMarketTrace, ResolvedBetWithTrace, @@ -33,6 +34,11 @@ class SimulatedOutcome(BaseModel): profit: float +class MSEProfit(BaseModel): + p_yes_mse: list[float] + total_profit: list[float] + + def get_outcome_for_trace( strategy: BettingStrategy, trace: ProcessMarketTrace, @@ -93,9 +99,11 @@ def get_outcome_for_trace( "DeployableThinkThoroughlyProphetResearchAgent": "pma-think-thoroughly-prophet-research", "DeployableKnownOutcomeAgent": "pma-knownoutcome", } + agent_pkey_map = { k: get_private_key_from_gcp_secret(v) for k, v in agent_gcp_secret_map.items() } + # Define strategies we want to test out strategies = [ MaxAccuracyBettingStrategy(bet_amount=1), @@ -103,17 +111,49 @@ def get_outcome_for_trace( MaxAccuracyBettingStrategy(bet_amount=25), KellyBettingStrategy(max_bet_amount=1), KellyBettingStrategy(max_bet_amount=2), + KellyBettingStrategy(max_bet_amount=5), KellyBettingStrategy(max_bet_amount=25), MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=1), MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=2), MaxAccuracyWithKellyScaledBetsStrategy(max_bet_amount=25), MaxExpectedValueBettingStrategy(bet_amount=1), MaxExpectedValueBettingStrategy(bet_amount=2), + MaxExpectedValueBettingStrategy(bet_amount=5), MaxExpectedValueBettingStrategy(bet_amount=25), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.01), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.05), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.1), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.15), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.2), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.25), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.3), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.4), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.5), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.6), + KellyBettingStrategy(max_bet_amount=2, max_price_impact=0.7), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.1), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.15), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.2), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.3), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.4), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.5), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.6), + KellyBettingStrategy(max_bet_amount=5, max_price_impact=0.7), + KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.1), + KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.2), + KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.3), + KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.5), + KellyBettingStrategy(max_bet_amount=25, max_price_impact=0.7), ] + httpx_client = HttpxCachedClient().get_client() + overall_md = "" + strat_mse_profits: dict[str, MSEProfit] = {} + for strategy in strategies: + strat_mse_profits[repr(strategy)] = MSEProfit(p_yes_mse=[], total_profit=[]) + print("# Agent Bet vs Simulated Bet Comparison") for agent_name, private_key in agent_pkey_map.items(): print(f"\n## {agent_name}\n") @@ -126,6 +166,7 @@ def get_outcome_for_trace( secret_key=api_keys.langfuse_secret_key.get_secret_value(), public_key=api_keys.langfuse_public_key, host=api_keys.langfuse_host, + httpx_client=httpx_client, ) traces = get_traces_for_agent( @@ -196,7 +237,18 @@ def get_outcome_for_trace( ) details.sort(key=lambda x: x["sim_profit"], reverse=True) + pd.DataFrame.from_records(details).to_csv( + f"{agent_name} - {strategy} - all bets.csv", index=False + ) + sum_squared_errors = 0.0 + for bet_with_trace in bets_with_traces: + bet = bet_with_trace.bet + trace = bet_with_trace.trace + estimated_p_yes = trace.answer.p_yes + actual_answer = float(bet.market_outcome) + sum_squared_errors += (estimated_p_yes - actual_answer) ** 2 + p_yes_mse = sum_squared_errors / len(bets_with_traces) total_bet_amount = sum([bt.bet.amount.amount for bt in bets_with_traces]) total_bet_profit = sum([bt.bet.profit.amount for bt in bets_with_traces]) total_simulated_amount = sum([so.size for so in simulated_outcomes]) @@ -212,11 +264,17 @@ def get_outcome_for_trace( "bet_amount": total_bet_amount, "bet_profit": total_bet_profit, "roi": roi, + "p_yes mse": p_yes_mse, # We don't know these for the original run. "start_balance": None, "end_balance": None, } ) + else: + strat_mse_profits[repr(strategy)].p_yes_mse.append(p_yes_mse) + strat_mse_profits[repr(strategy)].total_profit.append( + total_simulated_profit + ) simulations.append( { @@ -224,15 +282,27 @@ def get_outcome_for_trace( "bet_amount": total_simulated_amount, "bet_profit": total_simulated_profit, "roi": simulated_roi, + "p_yes mse": p_yes_mse, "start_balance": starting_balance, "end_balance": agent_balance, } ) + simulations_df = pd.DataFrame.from_records(simulations) + simulations_df.sort_values(by="bet_profit", ascending=False, inplace=True) overall_md += ( f"\n\n## {agent_name}\n\n{len(bets_with_traces)} bets\n\n" - + pd.DataFrame.from_records(simulations).to_markdown(index=False) + + simulations_df.to_markdown(index=False) ) + # export details per agent + pd.DataFrame.from_records(details).to_csv(f"{agent_name}_details.csv") + + print(f"Correlation between p_yes mse and total profit:") + for strategy_name, mse_profit in strat_mse_profits.items(): + mse = mse_profit.p_yes_mse + profit = mse_profit.total_profit + correlation = pd.Series(mse).corr(pd.Series(profit)) + print(f"{strategy_name}: {correlation=}") with open("match_bets_with_langfuse_traces_overall.md", "w") as overall_f: overall_f.write(overall_md) diff --git a/poetry.lock b/poetry.lock index 7f1c7026..3bd8af68 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1997,6 +1997,27 @@ doc = ["sphinx (>=5.0.0)", "sphinx-rtd-theme (>=1.0.0)", "towncrier (>=21,<22)"] lint = ["black (>=22)", "flake8 (==6.0.0)", "flake8-bugbear (==23.3.23)", "isort (>=5.10.1)", "mypy (==0.971)", "pydocstyle (>=5.0.0)"] test = ["eth-utils (>=1.0.1,<3)", "hypothesis (>=3.44.24,<=6.31.6)", "pytest (>=7.0.0)", "pytest-xdist (>=2.4.0)"] +[[package]] +name = "hishel" +version = "0.0.31" +description = "Persistent cache implementation for httpx and httpcore" +optional = false +python-versions = ">=3.8" +files = [ + {file = "hishel-0.0.31-py3-none-any.whl", hash = "sha256:8c0b4b3e6861d3eb643dccbedbe84e559d8422bdc58ebfea8ea8a5419ca18f52"}, + {file = "hishel-0.0.31.tar.gz", hash = "sha256:0577291a0159a466152096e2b8ac33c476707eb6a44e5dca6de5a855d7fbd38b"}, +] + +[package.dependencies] +httpx = ">=0.22.0" +typing-extensions = ">=4.8.0" + +[package.extras] +redis = ["redis (==5.0.1)"] +s3 = ["boto3 (>=1.15.0,<=1.15.3)", "boto3 (>=1.15.3)"] +sqlite = ["anysqlite (>=0.0.5)"] +yaml = ["pyyaml (==6.0.1)"] + [[package]] name = "hpack" version = "4.0.0" @@ -2209,13 +2230,13 @@ files = [ [[package]] name = "ipython" -version = "8.27.0" +version = "8.28.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.10" files = [ - {file = "ipython-8.27.0-py3-none-any.whl", hash = "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c"}, - {file = "ipython-8.27.0.tar.gz", hash = "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e"}, + {file = "ipython-8.28.0-py3-none-any.whl", hash = "sha256:530ef1e7bb693724d3cdc37287c80b07ad9b25986c007a53aa1857272dac3f35"}, + {file = "ipython-8.28.0.tar.gz", hash = "sha256:0d0d15ca1e01faeb868ef56bc7ee5a0de5bd66885735682e8a322ae289a13d1a"}, ] [package.dependencies] @@ -2546,13 +2567,13 @@ langchain-core = ">=0.2.38,<0.3.0" [[package]] name = "langfuse" -version = "2.51.2" +version = "2.51.3" description = "A client library for accessing langfuse" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langfuse-2.51.2-py3-none-any.whl", hash = "sha256:7aab94a9452cda4587a2cd4917e455da1afd7f8a2696688742130e2f2d23ca59"}, - {file = "langfuse-2.51.2.tar.gz", hash = "sha256:0982b108ab4c02947f682e442b0796b7a73825d31eeace1771575f6454b8f79a"}, + {file = "langfuse-2.51.3-py3-none-any.whl", hash = "sha256:32aba050123656ec0c165583e1d33243cc7a14e7b8498ed3c9de808aa90306f1"}, + {file = "langfuse-2.51.3.tar.gz", hash = "sha256:ccd2109556ee232db717abfb751ee8a3139f074db7d3e06a4f1a756c349fc5ba"}, ] [package.dependencies] @@ -2571,13 +2592,13 @@ openai = ["openai (>=0.27.8)"] [[package]] name = "langsmith" -version = "0.1.129" +version = "0.1.130" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.129-py3-none-any.whl", hash = "sha256:31393fbbb17d6be5b99b9b22d530450094fab23c6c37281a6a6efb2143d05347"}, - {file = "langsmith-0.1.129.tar.gz", hash = "sha256:6c3ba66471bef41b9f87da247cc0b493268b3f54656f73648a256a205261b6a0"}, + {file = "langsmith-0.1.130-py3-none-any.whl", hash = "sha256:acf27d77e699d84b03045f3f226e78be1dffb3e756aa1a085f9993a45380e8b2"}, + {file = "langsmith-0.1.130.tar.gz", hash = "sha256:3e43f87655a86395133e3a745d5968667d4d05dc9a24c617f89224c8cbf54dce"}, ] [package.dependencies] @@ -2585,6 +2606,7 @@ httpx = ">=0.23.0,<1" orjson = ">=3.9.14,<4.0.0" pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} requests = ">=2,<3" +requests-toolbelt = ">=1.0.0,<2.0.0" [[package]] name = "lazyasd" @@ -3915,43 +3937,43 @@ pyasn1 = ">=0.4.6,<0.7.0" [[package]] name = "pycryptodome" -version = "3.20.0" +version = "3.21.0" description = "Cryptographic library for Python" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"}, - {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"}, - {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"}, - {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"}, - {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"}, - {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"}, - {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"}, - {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"}, - {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"}, - {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"}, - {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"}, - {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"}, - {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"}, - {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"}, - {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"}, - {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"}, - {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"}, - {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"}, +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, + {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, + {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ba4cc304eac4d4d458f508d4955a88ba25026890e8abff9b60404f76a62c55e"}, + {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cb087b8612c8a1a14cf37dd754685be9a8d9869bed2ffaaceb04850a8aeef7e"}, + {file = "pycryptodome-3.21.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:26412b21df30b2861424a6c6d5b1d8ca8107612a4cfa4d0183e71c5d200fb34a"}, + {file = "pycryptodome-3.21.0-cp27-cp27m-win32.whl", hash = "sha256:cc2269ab4bce40b027b49663d61d816903a4bd90ad88cb99ed561aadb3888dd3"}, + {file = "pycryptodome-3.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0fa0a05a6a697ccbf2a12cec3d6d2650b50881899b845fac6e87416f8cb7e87d"}, + {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6cce52e196a5f1d6797ff7946cdff2038d3b5f0aba4a43cb6bf46b575fd1b5bb"}, + {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a915597ffccabe902e7090e199a7bf7a381c5506a747d5e9d27ba55197a2c568"}, + {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e74c522d630766b03a836c15bff77cb657c5fdf098abf8b1ada2aebc7d0819"}, + {file = "pycryptodome-3.21.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:a3804675283f4764a02db05f5191eb8fec2bb6ca34d466167fc78a5f05bbe6b3"}, + {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4"}, + {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b"}, + {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e"}, + {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8"}, + {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1"}, + {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a"}, + {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2"}, + {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93"}, + {file = "pycryptodome-3.21.0-cp36-abi3-win32.whl", hash = "sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764"}, + {file = "pycryptodome-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53"}, + {file = "pycryptodome-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca"}, + {file = "pycryptodome-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd"}, + {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8"}, + {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6"}, + {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0"}, + {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6"}, + {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:590ef0898a4b0a15485b05210b4a1c9de8806d3ad3d47f74ab1dc07c67a6827f"}, + {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35e442630bc4bc2e1878482d6f59ea22e280d7121d7adeaedba58c23ab6386b"}, + {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58"}, + {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8acd7d34af70ee63f9a849f957558e49a98f8f1634f86a59d2be62bb8e93f71c"}, + {file = "pycryptodome-3.21.0.tar.gz", hash = "sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297"}, ] [[package]] @@ -4141,70 +4163,70 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pymongo" -version = "4.10.0" +version = "4.10.1" description = "Python driver for MongoDB " optional = false python-versions = ">=3.8" files = [ - {file = "pymongo-4.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0197e0ae594d7b04bf1a012d9c6bbde6f0f5ccf1d88349fabeacd0b40b4334f5"}, - {file = "pymongo-4.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81b1454f361f27066552a931206a1562f72b34198eb2154c5c430d7b00b1c5f8"}, - {file = "pymongo-4.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:debfdbbed338fbdcf16774a8f7f9ab7dca80314946d31cc6ce9a23a87b0b4903"}, - {file = "pymongo-4.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0087eb80d7d253479b974e5b531a75433942bbdd3ab28c7c4677176815288c52"}, - {file = "pymongo-4.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b602e714575eba4cb521ad56eb5af55f0e44d723f23ae1d9357629a34b783281"}, - {file = "pymongo-4.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da6eff9c804c1fa178a965228d2c4787cf9a6e333df2cb9fa6beeb80b519845a"}, - {file = "pymongo-4.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4894e933088f2cd43a6891371a3f1c6a16f7acd8b4a736f4034536a7d46d17e5"}, - {file = "pymongo-4.10.0-cp310-cp310-win32.whl", hash = "sha256:5de901d39f0f014e2d54fca03e12e2d5ebbb5067c58577fc71a6caea3b244dbf"}, - {file = "pymongo-4.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:46bc15df824df877efa174f09b71ef87fb3838fe213d4fd95e35be7b2b6b21c7"}, - {file = "pymongo-4.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f4ebf41c7b219c3a155b675c4d0db66199af1c7c64370f5f9eb06004fd24d9b1"}, - {file = "pymongo-4.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6edb2c9fe5e78af017d1d7c251ed6460fd0764c2a74405c3d783eec92abfdf3d"}, - {file = "pymongo-4.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c62dcdc19af778fb8e7980fcef9e251b3e8a6395d466e0316470df72a7e30339"}, - {file = "pymongo-4.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b234f73a1198f49663bb4848b942bb8c78a31f4fe46cb559081c13662f556e1"}, - {file = "pymongo-4.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51c525337be3fc1d96d585ab981114962ae50b0ad43e8fcb8735d6493d42be0f"}, - {file = "pymongo-4.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b3a2f6acac03ddfe774e3565bf34d752ca85198a5720538492e58b9747f2e2"}, - {file = "pymongo-4.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e86b0cc697b45098467c3c4dba6ec671090106d6ed09822e45cc5f614e7d2d7"}, - {file = "pymongo-4.10.0-cp311-cp311-win32.whl", hash = "sha256:0447e8d6341675d896c10c9f27770011575c659525f058d94451f54beebe85cf"}, - {file = "pymongo-4.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:6128ef22ea99a2c15e7909ef812cfcb1179b8ce858d8050259e5a614aac2d8cd"}, - {file = "pymongo-4.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:43cc579b1aad7381839baa36ec673d21afa7785d10babfb0470d8fadc4334636"}, - {file = "pymongo-4.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:be12a4fa20813c04e66e25392fa5c48129fa29f724191148547bfb288fd46b08"}, - {file = "pymongo-4.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f50e85aaa04dcd55846a86512c56c720a386b06067b4ad4ba4ac6eb73a07cc4"}, - {file = "pymongo-4.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:252bf16d45f2049190994a9c17bdf3998ead44f1f4ceb97f6a149c25396ee054"}, - {file = "pymongo-4.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8328927e89d650175bca6974faf8830c08ea507a933b6ca8442cbb4f131c9b5"}, - {file = "pymongo-4.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12f2a17ef849529a471bf23f7b8553d37197e8b5a671d03d83de97873e9e923"}, - {file = "pymongo-4.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee01c79e7193edd14f9d1f182b5d1a96de709085f946a112a6f652844fe65789"}, - {file = "pymongo-4.10.0-cp312-cp312-win32.whl", hash = "sha256:c880d7333d46801abb5050da80238a838a773bcd95a7045ea9ae8869928f32f0"}, - {file = "pymongo-4.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:4ac85ad92d2f237bd368626bd3794aa1b643da1016defe484b676f1d92e31801"}, - {file = "pymongo-4.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b7385eb45f6a6d3bb78182570d7d9bfecb9d041044dc0a75188937136a01fbf"}, - {file = "pymongo-4.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:864d9e0ff0d525d8bdae1b23e11f5c9a7e625866fb38c0f19e0d3d6800235722"}, - {file = "pymongo-4.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43ca69abb3012b512e9cf17a9da8a7fa5ba6a9a32a3cd00df8daffedeb2b32f6"}, - {file = "pymongo-4.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:889592aded274b970c2980a3460b51a2f9407724c02ebb37df006f7ff9354f9d"}, - {file = "pymongo-4.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96b8d8a070c0bad72e0c075bea1c6715fc26f402ccfb0040a6fdf6a2ae0c582d"}, - {file = "pymongo-4.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f1a4afb651edacb9f8a2da61c56d4b030f30073751b51b9fef66efd1e835ebe"}, - {file = "pymongo-4.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:088afbb90331a4c5d628351245d576bcb7eb2b3ab49eb7f648284de51566b3bb"}, - {file = "pymongo-4.10.0-cp313-cp313-win32.whl", hash = "sha256:33d89ba16e083fa596763b87ef8e54fbc54b7582245b12cfc59af29a4f578505"}, - {file = "pymongo-4.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:5c3da4af3f20c1c4558650b46e2d0c8a261bd825476372c07e72aca6144ee5b0"}, - {file = "pymongo-4.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f38902d3c306795451cd328f9e3f694da05ce95c460273965159b7891f303e71"}, - {file = "pymongo-4.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0c163e9b747ff2a44a3967fe7a1803809d114505447e744f2aba0b910da24ea7"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41c9f875ddda4733ec3ba1e1b95aa419fee3b34dd7b14d79677292f5871d0a2b"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0fa3cb722d7e59b24f58ddd6c75a9f7f1509edcdd9fee3799805b36eab523bfc"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ad5df86de544e44c3a296d0a165ff3d661b9245868e957d6be98340c8b406f7"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f102160b1f43085717851988b03a45cce2603a856065b2281f3f529da989668"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c1af5c689dd67a9b33e88bc4adb63e61d823108b26d81cbc0ab7373b467a048"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:32ebc28f2648b544742c812e26b020ac1b35a5c74be6fc9f96eeaad229713f52"}, - {file = "pymongo-4.10.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:49c09629a9a3218ff176d7cfb7cf2bbb96869be288b2581762f31eae25ae1b30"}, - {file = "pymongo-4.10.0-cp38-cp38-win32.whl", hash = "sha256:c4e270ec6d3210d76c5d1f8642d9f904a02d138ca11937a78cb742b56c14fa7c"}, - {file = "pymongo-4.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:7a4c61d613d597205d70f0514ba7a11b466d399cc501b43a42225cad79d4ad46"}, - {file = "pymongo-4.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b4e41fac6874678dd49b29b58ec8c11cfccdee4bd66dc54f96c5cec831b802ae"}, - {file = "pymongo-4.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e668a5d3ec7990276a7bfe5945f754036398088939367f5d0bbf85d0979cfd3"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a78aae1fc42bcf690e749fd8d14f1dfc1be664c058e49a05504ed9f4c53ef43"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:711bdf49126c5bd78db9db24c3788003f0d22b21ffcdbbee2e099ac2f72d7309"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ff01206b0c20ab462889bed31b44c327fbf018343ade8f40aca1093a651ee61"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50d52322bb3220f93d05f7e5e94d744b2bc9070fea6231eb3a0c9569064db558"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c253ca10460d641461fb302c03fabb30e80567719b739ddf07501ff7cb5508"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:71e9b0e623e8395595d359100dfee38d033c7a842db51ff2a13207650336c24a"}, - {file = "pymongo-4.10.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:10438c08eb05208cebb198b740921bfe0eee303e5e7283bc451f7cad76dba4b7"}, - {file = "pymongo-4.10.0-cp39-cp39-win32.whl", hash = "sha256:eec4dd90bd109ce5cc7a9ea0616b7c4211cd1e7a93733d2a8a0809060f1e9950"}, - {file = "pymongo-4.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:ba06fea288b08b9e44d177703f0c96a9fb17ff96e8d444be757bdec030b39f37"}, - {file = "pymongo-4.10.0.tar.gz", hash = "sha256:2b56e499e0066c4a21a26b451b10377f147c360aa318f49f8c640b7f588e8e8c"}, + {file = "pymongo-4.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e699aa68c4a7dea2ab5a27067f7d3e08555f8d2c0dc6a0c8c60cfd9ff2e6a4b1"}, + {file = "pymongo-4.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70645abc714f06b4ad6b72d5bf73792eaad14e3a2cfe29c62a9c81ada69d9e4b"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2fd94c9fe048c94838badcc6e992d033cb9473eb31e5710b3707cba5e8aee2"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ded27a4a5374dae03a92e084a60cdbcecd595306555bda553b833baf3fc4868"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ecc2455e3974a6c429687b395a0bc59636f2d6aedf5785098cf4e1f180f1c71"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920fee41f7d0259f5f72c1f1eb331bc26ffbdc952846f9bd8c3b119013bb52c"}, + {file = "pymongo-4.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0a15665b2d6cf364f4cd114d62452ce01d71abfbd9c564ba8c74dcd7bbd6822"}, + {file = "pymongo-4.10.1-cp310-cp310-win32.whl", hash = "sha256:29e1c323c28a4584b7095378ff046815e39ff82cdb8dc4cc6dfe3acf6f9ad1f8"}, + {file = "pymongo-4.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:88dc4aa45f8744ccfb45164aedb9a4179c93567bbd98a33109d7dc400b00eb08"}, + {file = "pymongo-4.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:57ee6becae534e6d47848c97f6a6dff69e3cce7c70648d6049bd586764febe59"}, + {file = "pymongo-4.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f437a612f4d4f7aca1812311b1e84477145e950fdafe3285b687ab8c52541f3"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a970fd3117ab40a4001c3dad333bbf3c43687d90f35287a6237149b5ccae61d"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c4d0e7cd08ef9f8fbf2d15ba281ed55604368a32752e476250724c3ce36c72e"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca6f700cff6833de4872a4e738f43123db34400173558b558ae079b5535857a4"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec237c305fcbeef75c0bcbe9d223d1e22a6e3ba1b53b2f0b79d3d29c742b45b"}, + {file = "pymongo-4.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3337804ea0394a06e916add4e5fac1c89902f1b6f33936074a12505cab4ff05"}, + {file = "pymongo-4.10.1-cp311-cp311-win32.whl", hash = "sha256:778ac646ce6ac1e469664062dfe9ae1f5c9961f7790682809f5ec3b8fda29d65"}, + {file = "pymongo-4.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:9df4ab5594fdd208dcba81be815fa8a8a5d8dedaf3b346cbf8b61c7296246a7a"}, + {file = "pymongo-4.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fbedc4617faa0edf423621bb0b3b8707836687161210d470e69a4184be9ca011"}, + {file = "pymongo-4.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7bd26b2aec8ceeb95a5d948d5cc0f62b0eb6d66f3f4230705c1e3d3d2c04ec76"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb104c3c2a78d9d85571c8ac90ec4f95bca9b297c6eee5ada71fabf1129e1674"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4924355245a9c79f77b5cda2db36e0f75ece5faf9f84d16014c0a297f6d66786"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11280809e5dacaef4971113f0b4ff4696ee94cfdb720019ff4fa4f9635138252"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5d55f2a82e5eb23795f724991cac2bffbb1c0f219c0ba3bf73a835f97f1bb2e"}, + {file = "pymongo-4.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e974ab16a60be71a8dfad4e5afccf8dd05d41c758060f5d5bda9a758605d9a5d"}, + {file = "pymongo-4.10.1-cp312-cp312-win32.whl", hash = "sha256:544890085d9641f271d4f7a47684450ed4a7344d6b72d5968bfae32203b1bb7c"}, + {file = "pymongo-4.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:dcc07b1277e8b4bf4d7382ca133850e323b7ab048b8353af496d050671c7ac52"}, + {file = "pymongo-4.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:90bc6912948dfc8c363f4ead54d54a02a15a7fee6cfafb36dc450fc8962d2cb7"}, + {file = "pymongo-4.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:594dd721b81f301f33e843453638e02d92f63c198358e5a0fa8b8d0b1218dabc"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0783e0c8e95397c84e9cf8ab092ab1e5dd7c769aec0ef3a5838ae7173b98dea0"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fb6a72e88df46d1c1040fd32cd2d2c5e58722e5d3e31060a0393f04ad3283de"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e3a593333e20c87415420a4fb76c00b7aae49b6361d2e2205b6fece0563bf40"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72e2ace7456167c71cfeca7dcb47bd5dceda7db2231265b80fc625c5e8073186"}, + {file = "pymongo-4.10.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad05eb9c97e4f589ed9e74a00fcaac0d443ccd14f38d1258eb4c39a35dd722b"}, + {file = "pymongo-4.10.1-cp313-cp313-win32.whl", hash = "sha256:ee4c86d8e6872a61f7888fc96577b0ea165eb3bdb0d841962b444fa36001e2bb"}, + {file = "pymongo-4.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:45ee87a4e12337353242bc758accc7fb47a2f2d9ecc0382a61e64c8f01e86708"}, + {file = "pymongo-4.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:442ca247f53ad24870a01e80a71cd81b3f2318655fd9d66748ee2bd1b1569d9e"}, + {file = "pymongo-4.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23e1d62df5592518204943b507be7b457fb8a4ad95a349440406fd42db5d0923"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6131bc6568b26e7495a9f3ef2b1700566b76bbecd919f4472bfe90038a61f425"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdeba88c540c9ed0338c0b2062d9f81af42b18d6646b3e6dda05cf6edd46ada9"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a624d752dd3c89d10deb0ef6431559b6d074703cab90a70bb849ece02adc6b"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba164e73fdade9b4614a2497321c5b7512ddf749ed508950bdecc28d8d76a2d9"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9235fa319993405ae5505bf1333366388add2e06848db7b3deee8f990b69808e"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4a65567bd17d19f03157c7ec992c6530eafd8191a4e5ede25566792c4fe3fa2"}, + {file = "pymongo-4.10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f1945d48fb9b8a87d515da07f37e5b2c35b364a435f534c122e92747881f4a7c"}, + {file = "pymongo-4.10.1-cp38-cp38-win32.whl", hash = "sha256:345f8d340802ebce509f49d5833cc913da40c82f2e0daf9f60149cacc9ca680f"}, + {file = "pymongo-4.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:3a70d5efdc0387ac8cd50f9a5f379648ecfc322d14ec9e1ba8ec957e5d08c372"}, + {file = "pymongo-4.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15b1492cc5c7cd260229590be7218261e81684b8da6d6de2660cf743445500ce"}, + {file = "pymongo-4.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95207503c41b97e7ecc7e596d84a61f441b4935f11aa8332828a754e7ada8c82"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb99f003c720c6d83be02c8f1a7787c22384a8ca9a4181e406174db47a048619"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2bc1ee4b1ca2c4e7e6b7a5e892126335ec8d9215bcd3ac2fe075870fefc3358"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:93a0833c10a967effcd823b4e7445ec491f0bf6da5de0ca33629c0528f42b748"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f56707497323150bd2ed5d63067f4ffce940d0549d4ea2dfae180deec7f9363"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:409ab7d6c4223e5c85881697f365239dd3ed1b58f28e4124b846d9d488c86880"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dac78a650dc0637d610905fd06b5fa6419ae9028cf4d04d6a2657bc18a66bbce"}, + {file = "pymongo-4.10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1ec3fa88b541e0481aff3c35194c9fac96e4d57ec5d1c122376000eb28c01431"}, + {file = "pymongo-4.10.1-cp39-cp39-win32.whl", hash = "sha256:e0e961923a7b8a1c801c43552dcb8153e45afa41749d9efbd3a6d33f45489f7a"}, + {file = "pymongo-4.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:dabe8bf1ad644e6b93f3acf90ff18536d94538ca4d27e583c6db49889e98e48f"}, + {file = "pymongo-4.10.1.tar.gz", hash = "sha256:a9de02be53b6bb98efe0b9eda84ffa1ec027fcb23a2de62c4f941d9a2f2f3330"}, ] [package.dependencies] @@ -4558,6 +4580,20 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + [[package]] name = "rich" version = "13.9.1" @@ -5243,13 +5279,13 @@ files = [ [[package]] name = "tomli" -version = "2.0.1" +version = "2.0.2" description = "A lil' TOML parser" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, + {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, + {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, ] [[package]] @@ -5360,13 +5396,13 @@ typing-extensions = ">=3.7.4.3" [[package]] name = "types-pytz" -version = "2024.2.0.20240913" +version = "2024.2.0.20241003" description = "Typing stubs for pytz" optional = false python-versions = ">=3.8" files = [ - {file = "types-pytz-2024.2.0.20240913.tar.gz", hash = "sha256:4433b5df4a6fc587bbed41716d86a5ba5d832b4378e506f40d34bc9c81df2c24"}, - {file = "types_pytz-2024.2.0.20240913-py3-none-any.whl", hash = "sha256:a1eebf57ebc6e127a99d2fa2ba0a88d2b173784ef9b3defcc2004ab6855a44df"}, + {file = "types-pytz-2024.2.0.20241003.tar.gz", hash = "sha256:575dc38f385a922a212bac00a7d6d2e16e141132a3c955078f4a4fd13ed6cb44"}, + {file = "types_pytz-2024.2.0.20241003-py3-none-any.whl", hash = "sha256:3e22df1336c0c6ad1d29163c8fda82736909eb977281cb823c57f8bae07118b7"}, ] [[package]] @@ -5862,4 +5898,4 @@ openai = ["openai"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.12" -content-hash = "a8aa7db40788e0cbabcab2be15e479007ffab8542ef3266743f29cd85b1cacd1" +content-hash = "4f60565b99290a6a4d57f0dafb5523548c01f5d10315f7ec396cc5db0018c8b2" diff --git a/prediction_market_agent_tooling/deploy/betting_strategy.py b/prediction_market_agent_tooling/deploy/betting_strategy.py index 3ad289ed..52259628 100644 --- a/prediction_market_agent_tooling/deploy/betting_strategy.py +++ b/prediction_market_agent_tooling/deploy/betting_strategy.py @@ -1,5 +1,9 @@ from abc import ABC, abstractmethod +from scipy.optimize import minimize_scalar + +from prediction_market_agent_tooling.gtypes import xDai +from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.markets.agent_market import AgentMarket from prediction_market_agent_tooling.markets.data_models import ( Currency, @@ -10,10 +14,14 @@ TradeType, ) from prediction_market_agent_tooling.markets.omen.data_models import get_boolean_outcome +from prediction_market_agent_tooling.markets.omen.omen import ( + get_buy_outcome_token_amount, +) from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import ( get_kelly_bet_full, get_kelly_bet_simplified, ) +from prediction_market_agent_tooling.tools.betting_strategies.utils import SimpleBet from prediction_market_agent_tooling.tools.utils import check_not_none @@ -134,8 +142,9 @@ def calculate_direction(market_p_yes: float, estimate_p_yes: float) -> bool: class KellyBettingStrategy(BettingStrategy): - def __init__(self, max_bet_amount: float): + def __init__(self, max_bet_amount: float, max_price_impact: float | None = None): self.max_bet_amount = max_bet_amount + self.max_price_impact = max_price_impact def calculate_trades( self, @@ -165,9 +174,19 @@ def calculate_trades( ) ) + kelly_bet_size = kelly_bet.size + if self.max_price_impact: + # Adjust amount + max_price_impact_bet_amount = self.calculate_bet_amount_for_price_impact( + market, kelly_bet, 0 + ) + + # We just don't want Kelly size to extrapolate price_impact - hence we take the min. + kelly_bet_size = min(kelly_bet.size, max_price_impact_bet_amount) + amounts = { market.get_outcome_str_from_bool(kelly_bet.direction): TokenAmount( - amount=kelly_bet.size, currency=market.currency + amount=kelly_bet_size, currency=market.currency ), } target_position = Position(market_id=market.id, amounts=amounts) @@ -176,8 +195,69 @@ def calculate_trades( ) return trades + def calculate_price_impact_for_bet_amount( + self, buy_direction: bool, bet_amount: float, yes: float, no: float, fee: float + ) -> float: + total_outcome_tokens = yes + no + expected_price = ( + no / total_outcome_tokens if buy_direction else yes / total_outcome_tokens + ) + + tokens_to_buy = get_buy_outcome_token_amount( + bet_amount, buy_direction, yes, no, fee + ) + + actual_price = bet_amount / tokens_to_buy + # price_impact should always be > 0 + price_impact = (actual_price - expected_price) / expected_price + return price_impact + + def calculate_bet_amount_for_price_impact( + self, + market: AgentMarket, + kelly_bet: SimpleBet, + fee: float, + ) -> float: + def calculate_price_impact_deviation_from_target_price_impact( + bet_amount: xDai, + ) -> float: + price_impact = self.calculate_price_impact_for_bet_amount( + kelly_bet.direction, + bet_amount, + yes_outcome_pool_size, + no_outcome_pool_size, + fee, + ) + # We return abs for the algorithm to converge to 0 instead of the min (and possibly negative) value. + + max_price_impact = check_not_none(self.max_price_impact) + return abs(price_impact - max_price_impact) + + if not market.outcome_token_pool: + logger.warning( + "Market outcome_token_pool is None, cannot calculate bet amount" + ) + return kelly_bet.size + + yes_outcome_pool_size = market.outcome_token_pool[ + market.get_outcome_str_from_bool(True) + ] + no_outcome_pool_size = market.outcome_token_pool[ + market.get_outcome_str_from_bool(False) + ] + + # The bounds below have been found to work heuristically. + optimized_bet_amount = minimize_scalar( + calculate_price_impact_deviation_from_target_price_impact, + bounds=(0, 1000 * (yes_outcome_pool_size + no_outcome_pool_size)), + method="bounded", + tol=1e-11, + options={"maxiter": 10000}, + ) + return float(optimized_bet_amount.x) + def __repr__(self) -> str: - return f"{self.__class__.__name__}(max_bet_amount={self.max_bet_amount})" + return f"{self.__class__.__name__}(max_bet_amount={self.max_bet_amount}, max_price_impact={self.max_price_impact})" class MaxAccuracyWithKellyScaledBetsStrategy(BettingStrategy): diff --git a/prediction_market_agent_tooling/markets/agent_market.py b/prediction_market_agent_tooling/markets/agent_market.py index d258d35b..bff0d4be 100644 --- a/prediction_market_agent_tooling/markets/agent_market.py +++ b/prediction_market_agent_tooling/markets/agent_market.py @@ -28,6 +28,8 @@ class SortBy(str, Enum): CLOSING_SOONEST = "closing-soonest" NEWEST = "newest" + HIGHEST_LIQUIDITY = "highest_liquidity" + LOWEST_LIQUIDITY = "lowest_liquidity" NONE = "none" diff --git a/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py b/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py index e830127b..7ee33991 100644 --- a/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py +++ b/prediction_market_agent_tooling/markets/omen/omen_subgraph_handler.py @@ -319,6 +319,16 @@ def _build_sort_params( sort_by_field = ( self.trades_subgraph.FixedProductMarketMaker.openingTimestamp ) + case SortBy.HIGHEST_LIQUIDITY: + sort_direction = "desc" + sort_by_field = ( + self.trades_subgraph.FixedProductMarketMaker.liquidityMeasure + ) + case SortBy.LOWEST_LIQUIDITY: + sort_direction = "asc" + sort_by_field = ( + self.trades_subgraph.FixedProductMarketMaker.liquidityMeasure + ) case SortBy.NONE: sort_direction = None sort_by_field = None diff --git a/prediction_market_agent_tooling/markets/polymarket/api.py b/prediction_market_agent_tooling/markets/polymarket/api.py index 2ba161cf..8abf7057 100644 --- a/prediction_market_agent_tooling/markets/polymarket/api.py +++ b/prediction_market_agent_tooling/markets/polymarket/api.py @@ -2,8 +2,8 @@ import requests import tenacity -from loguru import logger +from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.markets.polymarket.data_models import ( POLYMARKET_FALSE_OUTCOME, POLYMARKET_TRUE_OUTCOME, diff --git a/prediction_market_agent_tooling/tools/httpx_cached_client.py b/prediction_market_agent_tooling/tools/httpx_cached_client.py new file mode 100644 index 00000000..971abd49 --- /dev/null +++ b/prediction_market_agent_tooling/tools/httpx_cached_client.py @@ -0,0 +1,11 @@ +import hishel + + +class HttpxCachedClient: + def __init__(self) -> None: + storage = hishel.FileStorage(ttl=3600, check_ttl_every=600) + controller = hishel.Controller(force_cache=True) + self.client = hishel.CacheClient(storage=storage, controller=controller) + + def get_client(self) -> hishel.CacheClient: + return self.client diff --git a/prediction_market_agent_tooling/tools/is_predictable.py b/prediction_market_agent_tooling/tools/is_predictable.py index 99743b31..569be7c7 100644 --- a/prediction_market_agent_tooling/tools/is_predictable.py +++ b/prediction_market_agent_tooling/tools/is_predictable.py @@ -1,7 +1,7 @@ import tenacity -from loguru import logger from prediction_market_agent_tooling.config import APIKeys +from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.tools.cache import persistent_inmemory_cache from prediction_market_agent_tooling.tools.langfuse_ import ( get_langfuse_langchain_config, diff --git a/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py b/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py index 429e0bc4..491082c7 100644 --- a/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py +++ b/prediction_market_agent_tooling/tools/tavily_storage/tavily_models.py @@ -2,7 +2,6 @@ from datetime import datetime, timedelta import tenacity -from loguru import logger from pydantic import BaseModel from sqlalchemy import Column from sqlalchemy.dialects.postgresql import JSONB @@ -18,6 +17,7 @@ ) from prediction_market_agent_tooling.config import APIKeys +from prediction_market_agent_tooling.loggers import logger from prediction_market_agent_tooling.tools.utils import utcnow diff --git a/pyproject.toml b/pyproject.toml index 2d7072a4..a22bf5e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ psycopg2-binary = "^2.9.9" base58 = ">=1.0.2,<2.0" loky = "^3.4.1" pinatapy-vourhey = "^0.2.0" +hishel = "^0.0.31" [tool.poetry.extras] openai = ["openai"] diff --git a/tests_integration/markets/omen/test_kelly.py b/tests_integration/markets/omen/test_kelly.py new file mode 100644 index 00000000..1871d70f --- /dev/null +++ b/tests_integration/markets/omen/test_kelly.py @@ -0,0 +1,135 @@ +import numpy as np +import pytest + +from prediction_market_agent_tooling.deploy.betting_strategy import KellyBettingStrategy +from prediction_market_agent_tooling.markets.agent_market import FilterBy, SortBy +from prediction_market_agent_tooling.markets.omen.omen import OmenAgentMarket +from prediction_market_agent_tooling.markets.omen.omen_subgraph_handler import ( + OmenSubgraphHandler, +) +from prediction_market_agent_tooling.tools.betting_strategies.kelly_criterion import ( + get_kelly_bet_full, +) +from prediction_market_agent_tooling.tools.utils import check_not_none + + +def test_kelly_price_impact_calculation1() -> None: + # First case from https://docs.gnosis.io/conditionaltokens/docs/introduction3/#an-example-with-cpmm + kelly = KellyBettingStrategy(max_bet_amount=1, max_price_impact=0.5) + yes = 10 + no = 10 + bet_amount = 10 + buy_direction = True + assert_price_impact(bet_amount, buy_direction, yes, no, kelly) + + +def test_kelly_price_impact_calculation2() -> None: + # Follow-up from first case from https://docs.gnosis.io/conditionaltokens/docs/introduction3/#an-example-with-cpmm + kelly = KellyBettingStrategy(max_bet_amount=1, max_price_impact=0.5) + # after first bet 10 xDAI on Yes, new yes/no + yes = 5 + no = 20 + bet_amount = 10 + buy_direction = False + assert_price_impact(bet_amount, buy_direction, yes, no, kelly) + + +@pytest.mark.parametrize( + "max_bet_amount, max_price_impact, p_yes", [(2, 0.5, 0.9), (5, 0.7, 0.8)] +) +def test_kelly_price_impact_works_large_pool( + max_bet_amount: int, max_price_impact: float, p_yes: float +) -> None: + large_market = OmenSubgraphHandler().get_omen_binary_markets_simple( + limit=1, filter_by=FilterBy.OPEN, sort_by=SortBy.HIGHEST_LIQUIDITY + )[0] + omen_agent_market = OmenAgentMarket.from_data_model(large_market) + confidence = 1.0 + assert_price_impact_converges( + omen_agent_market, max_bet_amount, p_yes, confidence, max_price_impact + ) + + +@pytest.mark.parametrize( + "max_bet_amount, max_price_impact, p_yes", [(2, 0.5, 0.9), (5, 0.7, 0.8)] +) +def test_kelly_price_impact_works_small_pool( + max_bet_amount: int, max_price_impact: float, p_yes: float +) -> None: + large_market = OmenSubgraphHandler().get_omen_binary_markets_simple( + limit=2, filter_by=FilterBy.OPEN, sort_by=SortBy.LOWEST_LIQUIDITY + )[0] + omen_agent_market = OmenAgentMarket.from_data_model(large_market) + confidence = 1.0 + assert_price_impact_converges( + omen_agent_market, max_bet_amount, p_yes, confidence, max_price_impact + ) + + +def assert_price_impact_converges( + omen_agent_market: OmenAgentMarket, + max_bet_amount: float, + p_yes: float, + confidence: float, + max_price_impact: float, +) -> None: + outcome_token_pool = check_not_none(omen_agent_market.outcome_token_pool) + yes_outcome_pool_size = outcome_token_pool[ + omen_agent_market.get_outcome_str_from_bool(True) + ] + no_outcome_pool_size = outcome_token_pool[ + omen_agent_market.get_outcome_str_from_bool(False) + ] + + kelly_bet = get_kelly_bet_full( + yes_outcome_pool_size=yes_outcome_pool_size, + no_outcome_pool_size=no_outcome_pool_size, + estimated_p_yes=p_yes, + max_bet=max_bet_amount, + confidence=confidence, + ) + + kelly = KellyBettingStrategy( + max_bet_amount=max_bet_amount, max_price_impact=max_price_impact + ) + + max_price_impact_bet_amount = kelly.calculate_bet_amount_for_price_impact( + omen_agent_market, kelly_bet, 0 + ) + price_impact = kelly.calculate_price_impact_for_bet_amount( + kelly_bet.direction, + bet_amount=max_price_impact_bet_amount, + yes=yes_outcome_pool_size, + no=no_outcome_pool_size, + fee=0, + ) + + # assert convergence + assert np.isclose(price_impact, max_price_impact, atol=max_price_impact * 0.001) + + +def assert_price_impact( + bet_amount: float, + buy_direction: bool, + yes: float, + no: float, + kelly: KellyBettingStrategy, +) -> None: + price_impact = kelly.calculate_price_impact_for_bet_amount( + buy_direction, bet_amount=bet_amount, yes=yes, no=no, fee=0 + ) + + # Calculation is done assuming buy_direction is True. Else, we invert the reserves. + if not buy_direction: + yes, no = no, yes + + expected_price_yes = no / (yes + no) + k = yes * no + tokens_bought_yes = (yes + bet_amount) - (k / (no + bet_amount)) # 23.333 # x*y = k + actual_price_yes = bet_amount / tokens_bought_yes + expected_price_impact = (actual_price_yes - expected_price_yes) / expected_price_yes + #### + + assert np.isclose(price_impact, expected_price_impact, rtol=0.01) + + print(price_impact)