DEV Community

Cover image for How to Calculate Expected Value for a Polymarket Trading Bot in Python
Dexoryn
Dexoryn

Posted on

How to Calculate Expected Value for a Polymarket Trading Bot in Python

A Polymarket trading bot can identify a promising outcome and still make a poor entry.

The problem is that predicting what will happen and determining whether a trade is worth taking are two different tasks.

A bot might estimate that an outcome has a 65% chance of winning. But if the price is already $0.63, the estimated advantage is small before accounting for trading costs and uncertainty.

This is where expected value becomes useful.

In this tutorial, we will build a small Python component that calculates the estimated expected value of a Polymarket outcome token, applies a minimum threshold, and rejects trades that do not meet the strategy's requirements.

The example uses hypothetical inputs. It does not connect to a live market, submit orders, or guarantee profitable results.

  1. Understand the expected value calculation

For a binary outcome token held until resolution, a winning share pays $1 and a losing share pays $0.

Let:

  • (q) be your estimated probability that the outcome wins.
  • (p) be the purchase price per share.

The simplified expected profit per share before costs is:

[
EV=q(1-p)+(1-q)(-p)
]

This simplifies to:

[
EV=q-p
]

Suppose your model estimates a 65% probability of winning.

If the token costs $0.45:

[
EV=0.65-0.45=$0.20
]

If the token costs $0.62:

[
EV=0.65-0.62=$0.03
]

The estimated expected profit falls from $0.20 to $0.03 per share before fees and other costs.

This calculation assumes the probability estimate is accurate enough to be useful and that the position is held until resolution. Selling before resolution requires a different model that accounts for the exit price.

Expected value is an estimate, not a guaranteed return.

  1. Implement the calculation in Python

Use "Decimal" for financial values when you want explicit decimal arithmetic rather than binary floating-point calculations.

from decimal import Decimal

def expected_value_per_share(
probability: Decimal,
price: Decimal,
cost_per_share: Decimal = Decimal("0"),
) -> Decimal:
if not Decimal("0") <= probability <= Decimal("1"):
raise ValueError("Probability must be between 0 and 1")

if not Decimal("0") < price < Decimal("1"):
    raise ValueError("Price must be between 0 and 1")

if cost_per_share < Decimal("0"):
    raise ValueError("Cost cannot be negative")

return probability - price - cost_per_share
Enter fullscreen mode Exit fullscreen mode

probability = Decimal("0.65")
price = Decimal("0.45")
cost = Decimal("0.01")

ev = expected_value_per_share(
probability=probability,
price=price,
cost_per_share=cost,
)

print(f"Estimated EV per share: ${ev}")

Expected output:

Estimated EV per share: $0.19

The example subtracts an assumed cost of $0.01 per share. This is only a placeholder. Actual costs depend on the applicable market fee schedule and execution conditions.

The function deliberately keeps probability estimation separate from expected-value calculation. That makes it easier to replace the hypothetical probability with an estimate from a model, research process, or calibrated forecasting system.

  1. Reject trades with insufficient expected value

Calculating expected value is only useful if the bot uses the result to make a decision.

A strategy can define a minimum expected-value threshold. If a proposed trade falls below it, the bot rejects the trade rather than submitting an order.

from decimal import Decimal

def evaluate_trade(
probability: Decimal,
price: Decimal,
cost_per_share: Decimal,
minimum_ev: Decimal,
) -> dict:
ev = expected_value_per_share(
probability=probability,
price=price,
cost_per_share=cost_per_share,
)

if ev < minimum_ev:
    return {
        "approved": False,
        "reason": "EXPECTED_VALUE_TOO_LOW",
        "expected_value": str(ev),
    }

return {
    "approved": True,
    "reason": "APPROVED",
    "expected_value": str(ev),
}
Enter fullscreen mode Exit fullscreen mode

result = evaluate_trade(
probability=Decimal("0.65"),
price=Decimal("0.62"),
cost_per_share=Decimal("0.01"),
minimum_ev=Decimal("0.05"),
)

print(result)

The result is:

{
"approved": False,
"reason": "EXPECTED_VALUE_TOO_LOW",
"expected_value": "0.02"
}

The trade is rejected because its estimated expected value is $0.02 per share, below the configured minimum of $0.05.

The threshold is an example, not a universally correct value. It should be selected and evaluated based on the strategy's assumptions, uncertainty, execution costs, and risk requirements.

A positive expected value alone does not mean a trade is worth taking.

  1. Use the executable price, not an assumed price

One of the most important implementation details is choosing the correct price input.

Polymarket provides market data through its APIs. The CLOB API exposes order-book information and price-related endpoints, while the Gamma API provides market metadata and discovery information. The "Polymarket Institute's data guide" (https://institute.polymarket.com/data) includes examples of retrieving CLOB prices and historical prices.

For a potential buy order, the bot should evaluate the price available to buyers, not blindly use a last-traded price or a midpoint.

For larger orders, even the best ask may not represent the average price of the entire order. The bot may need to inspect available ask levels and estimate the cost of filling the intended quantity.

Consider this simplified order book:

Ask price| Available shares
$0.50| 20
$0.52| 30
$0.55| 50

A purchase of 20 shares might be available at $0.50. A purchase of 50 shares would require buying across at least two price levels if those quantities remain available.

The estimated average entry price therefore depends on the size of the order.

For a live implementation, the process should be:

  1. Retrieve the relevant outcome token's current order book.
  2. Validate that the response is complete and sufficiently fresh.
  3. Estimate the average execution price for the intended quantity.
  4. Calculate expected value using that estimate and applicable costs.
  5. Reject the trade if the resulting expected value falls below the strategy's threshold.

Market data can change between retrieval and order submission. The execution layer should enforce suitable price limits and handle cases where the market moves before the order is filled.

  1. Add unit tests

Expected-value logic is small enough that it should be tested independently before being integrated into a trading bot.

Using "pytest", create a file called "test_expected_value.py":

from decimal import Decimal
import pytest

from trading_logic import expected_value_per_share

def test_positive_expected_value():
result = expected_value_per_share(
Decimal("0.65"),
Decimal("0.45"),
Decimal("0.01"),
)

assert result == Decimal("0.19")
Enter fullscreen mode Exit fullscreen mode

def test_zero_expected_value():
result = expected_value_per_share(
Decimal("0.50"),
Decimal("0.50"),
)

assert result == Decimal("0.00")
Enter fullscreen mode Exit fullscreen mode

def test_invalid_probability():
with pytest.raises(ValueError):
expected_value_per_share(
Decimal("1.10"),
Decimal("0.50"),
)

def test_invalid_price():
with pytest.raises(ValueError):
expected_value_per_share(
Decimal("0.60"),
Decimal("1.00"),
)

Place the calculation function in "trading_logic.py", then run:

pip install pytest
pytest -q

These tests verify the arithmetic and basic input validation. They do not establish that a probability model is accurate or that a live trading system will execute orders as expected.

  1. What this calculation does not tell you

Expected value is useful, but it is only one part of a trading decision.

First, the probability estimate may be wrong. A model that consistently overestimates an outcome's chances can report positive expected value while making poor decisions.

Second, costs may be more complicated than a fixed amount per share. The actual implementation must account for the applicable fee schedule and the economics of the order.

Third, expected value does not measure the uncertainty of the estimate. Two opportunities can have the same calculated expected value but very different levels of model uncertainty.

Fourth, it does not account for portfolio concentration. Several individually attractive positions may depend on the same underlying event and create excessive combined exposure.

A production trading bot should combine expected-value evaluation with data-quality checks, executable-price validation, position limits, order-state management, and monitoring.

It should also record rejected trades and the reasons for rejection. This makes it possible to assess whether the rules are too permissive, too restrictive, or based on assumptions that do not hold in live conditions.

Conclusion

Expected value gives a Polymarket trading bot a clear mathematical basis for evaluating a proposed entry. Implementing it as an independent Python component makes the logic easier to test, audit, and improve.

The calculation is straightforward. The difficult work lies in producing reliable probability estimates, using realistic execution prices, accounting for costs, and enforcing the result consistently throughout the trading system.

At Dexoryn Labs, we work on Polymarket trading automation, including existing bot projects and custom development. You can explore our "open-source projects on GitHub" (https://github.com/dexorynlabs) or visit "dexoryn.xyz" (https://dexoryn.xyz/) to learn more.

Build the decision logic first, test its assumptions, and only then consider connecting it to live execution.

Top comments (0)