DEV Community

Cover image for How to Test a Polymarket Trading Bot in Python Before Going Live
Dexoryn
Dexoryn

Posted on

How to Test a Polymarket Trading Bot in Python Before Going Live

A Polymarket trading bot can run without crashing and still make serious mistakes. It might submit duplicate orders, calculate the wrong position size, trade using stale prices, or assume an order was filled when it was not.

Testing only whether the script starts successfully is not enough.

A reliable trading bot needs tests for its decision logic, risk controls, execution behaviour, and recovery from failures. You can test most of these components without placing real orders or risking funds.

This tutorial shows how to structure a basic Python testing workflow using a simple trading decision, a risk check, a simulated execution layer, and automated tests.

  1. Separate trading logic from order execution

One of the most useful design decisions is to separate the component that decides what to do from the component that communicates with the trading API.

A simple architecture looks like this:

  • Market data: Supplies prices, order-book information, and market identifiers.
  • Strategy: Evaluates whether a trading signal meets the strategy's conditions.
  • Risk manager: Checks position limits, available capital, and permitted prices.
  • Execution layer: Submits, tracks, and reconciles orders.
  • Test suite: Verifies the behaviour of each component independently.

This separation makes testing easier because you can evaluate the strategy without connecting a wallet or sending an order.

It also helps identify which component failed when a test does not behave as expected.

  1. Write a small, testable risk-control function

Start with a function that decides whether a proposed order is allowed.

For this example, the bot accepts an order only when its price, size, and total cost meet predefined limits. These are illustrative rules, not a complete trading strategy.

risk.py

from decimal import Decimal

def validate_order(
price: Decimal,
shares: Decimal,
available_cash: Decimal,
max_order_cost: Decimal,
) -> bool:
if price <= Decimal("0") or price >= Decimal("1"):
return False

if shares <= Decimal("0"):
    return False

order_cost = price * shares

if order_cost > available_cash:
    return False

if order_cost > max_order_cost:
    return False

return True
Enter fullscreen mode Exit fullscreen mode

The price restriction reflects the conventional probability-token range used in this simplified example. A production implementation must also validate the actual market's supported price increments, order-size constraints, applicable fees, and current API requirements.

Notice that the function does not place an order. It only evaluates whether the proposed order satisfies these particular checks.

That makes it deterministic and straightforward to test.

  1. Add automated unit tests with pytest

Install the testing library:

pip install pytest

Create a file named "test_risk.py":

from decimal import Decimal

from risk import validate_order

def test_accepts_order_within_limits():
assert validate_order(
price=Decimal("0.40"),
shares=Decimal("10"),
available_cash=Decimal("20"),
max_order_cost=Decimal("10"),
)

def test_rejects_order_exceeding_available_cash():
assert not validate_order(
price=Decimal("0.50"),
shares=Decimal("50"),
available_cash=Decimal("20"),
max_order_cost=Decimal("100"),
)

def test_rejects_order_exceeding_order_limit():
assert not validate_order(
price=Decimal("0.50"),
shares=Decimal("30"),
available_cash=Decimal("100"),
max_order_cost=Decimal("10"),
)

def test_rejects_zero_shares():
assert not validate_order(
price=Decimal("0.40"),
shares=Decimal("0"),
available_cash=Decimal("100"),
max_order_cost=Decimal("10"),
)

def test_rejects_invalid_price():
assert not validate_order(
price=Decimal("1.00"),
shares=Decimal("10"),
available_cash=Decimal("100"),
max_order_cost=Decimal("10"),
)

Run the tests:

python -m pytest -v

These tests check normal behaviour and several invalid inputs. They do not prove that the entire bot is safe, but they establish that the specific risk rules behave as intended.

Add tests whenever a new rule is introduced. If you change how order size is calculated, for example, test both the expected allocation and the conditions that should cause the order to be rejected.

  1. Simulate execution instead of sending real orders

Unit tests cannot establish how the bot behaves when an execution request times out, an order is partially filled, or the market price changes.

For those scenarios, use a simulated execution layer.

A minimal interface might look like this:

class SimulatedBroker:
def init(self):
self.orders = []

def submit(self, token_id, side, price, shares):
    order = {
        "token_id": token_id,
        "side": side,
        "price": price,
        "shares": shares,
        "status": "SIMULATED",
    }

    self.orders.append(order)
    return order
Enter fullscreen mode Exit fullscreen mode

Your strategy can call this interface in tests instead of the real exchange client.

The simulator records the order request but does not sign, submit, or settle a live trade. This lets you inspect what the strategy intended to do.

However, a simulated broker is not a realistic market simulator by itself. It does not model order-book depth, queue position, latency, fees, partial fills, or adverse price movement.

Those behaviours must be added explicitly if you want the simulation to represent them.

  1. Test failures, not just successful trades

Many important bugs appear when something unexpected happens.

A useful test suite should include the following scenarios.

Insufficient balance: The strategy detects an opportunity but does not submit an order that exceeds available capital.

Stale market data: The bot refuses to use a price snapshot older than its configured freshness limit.

Duplicate signal: Processing the same source event twice does not create two independent orders.

Partial fill: The system records the filled quantity separately from the remaining quantity.

Rejected order: The bot records the rejection and does not mark the position as successfully opened.

Network timeout: The bot treats the result as unknown until it can reconcile the order state, rather than blindly submitting a duplicate.

Restart recovery: The bot reloads persistent state and reconciles open orders and positions before resuming execution.

These tests should verify state transitions as well as return values. For example, after a timeout, the order should enter an unresolved state and trigger reconciliation. It should not automatically become a failed order simply because the response was missing.

  1. Test against historical data carefully

Historical data can help evaluate whether a strategy's rules would have generated signals under past market conditions.

But a historical simulation is only as realistic as its assumptions.

A test that assumes every order fills at the price recorded in a historical dataset may ignore spread, order-book depth, execution delays, and the fact that the desired quantity might not have been available.

For a more useful evaluation, document:

  • Which price or order-book data the simulation uses.
  • How orders are assumed to fill.
  • How fees and execution costs are handled.
  • What happens when data is missing.
  • Whether the strategy uses only information that would have been available at the decision time.

Avoid using future information to make past trading decisions. That creates look-ahead bias and can make an ineffective strategy appear successful.

Keep historical strategy evaluation separate from software correctness testing. A strategy can pass every unit test and still have no profitable trading edge.

  1. Use a staged testing process

Before connecting a bot to live execution, progress through distinct stages.

Stage 1: Unit testing

Test calculations, validation rules, signal processing, and state transitions using controlled inputs.

Stage 2: Integration testing

Test how the data, strategy, risk, persistence, and execution components work together. Mock external services where appropriate.

Stage 3: Historical simulation

Evaluate the strategy against historical data using explicit assumptions about fills, fees, and data availability.

Stage 4: Dry-run monitoring

Connect to the relevant market-data feeds and record what the bot would do without submitting real orders. Compare intended decisions with current market conditions.

Stage 5: Controlled live testing

Only after the preceding stages have been reviewed should live execution be considered. Use a dedicated wallet with limited funds, conservative exposure limits, protected credentials, and a tested emergency stop.

Passing one stage does not automatically guarantee success at the next. In particular, dry-run execution cannot fully reproduce real order matching or settlement.

  1. Define what passing actually means

A test suite is more useful when its acceptance criteria are explicit.

For example, a team might require that:

  • Invalid orders are rejected before submission.
  • Repeated source events are deduplicated.
  • Unknown order outcomes trigger reconciliation.
  • Stale market data blocks new trading decisions.
  • Partial fills are represented accurately.
  • Restart recovery does not blindly duplicate previous orders.
  • Secrets never appear in logs.
  • Risk limits remain enforced during errors and reconnects.

These are testable engineering requirements. They are more meaningful than simply declaring that a bot is ready because it ran for an hour without crashing.

The appropriate criteria will depend on the bot's architecture and intended use.

Conclusion

Testing a Polymarket trading bot is not just about checking whether its strategy produces signals. It is about verifying that the entire system behaves correctly when inputs are invalid, data becomes stale, orders fail, and the application restarts.

Start with deterministic unit tests, isolate external execution behind a testable interface, simulate failure conditions, and evaluate historical performance with realistic assumptions. Then progress to dry-run monitoring before considering live execution.

Dexoryn Labs maintains a public "Polymarket copy-trading bot repository" (https://github.com/dexorynlabs/polymarket-copy-trading-bot) that developers can inspect when evaluating an existing implementation. Teams that need different testing, monitoring, execution, or risk-control workflows may require custom development.

The goal is not to prove that a bot can never fail. It is to identify predictable failures before they become expensive, and to make unexpected failures easier to detect and recover from.

This article is for educational purposes only and is not financial advice. Automated trading involves risk, and software tests cannot guarantee profitability or prevent every production failure.

Top comments (0)