Loading page...Загружаем страницу...正在加载页面...
AtlasRepo

How to Build Risk Controls for a Polymarket Trading Bot

Description and use

The bot now rejects trades that violate any risk rule, preventing over‑exposure, liquidity problems, duplicate orders, and trading on stale data, while providing clear logs for debugging.

Problem

Automated trading bots can amplify mistakes by executing the same erroneous signal repeatedly, leading to excessive exposure, liquidity issues, and other risks.

Outcome

The bot now rejects trades that violate any risk rule, preventing over‑exposure, liquidity problems, duplicate orders, and trading on stale data, while providing clear logs for debugging.

Workflow

  • Define constants for limits (MAX_TRADE_SIZE, MAX_EXPOSURE, MAX_MARKET_EXPOSURE, MAX_PRICE_DEVIATION, MIN_LIQUIDITY, MAX_OPEN_POSITIONS, COOLDOWN_SECONDS, MAX_DAILY_LOSS).
  • Implement helper checks: check_trade_size, check_exposure, check_market_exposure, price_is_valid, check_liquidity, is_new_event, can_open_position, cooldown_finished, daily_limit_reached.
  • Create a RiskEngine class with an approve method that runs all checks and returns approval status and reason.
  • In the strategy loop, call risk_engine.approve(trade, portfolio, market). If approved, execute or log simulated trade; if rejected, log reason.
  • Ensure dry‑run mode uses the same risk engine to avoid bypassing controls.
  • Persist processed event IDs to avoid duplicate trades across restarts.
  • Handle API failures by fail‑closed logic, rejecting trades when market state is unavailable or stale.
  • Configure all limits via a central configuration to avoid scattered constants.

Evidence

A trading bot does not become safer because it is automated.

Source

The risk engine exists between the strategy and execution.

Source

MAX_TRADE_SIZE = 50

Source

MAX_EXPOSURE = 500

Source

MAX_MARKET_EXPOSURE = 100

Source

MAX_PRICE_DEVIATION = 0.03

Source

MIN_LIQUIDITY = 100

Source

processed_events = set()

Source

MAX_OPEN_POSITIONS = 10

Source

COOLDOWN_SECONDS = 60

Source

MAX_DAILY_LOSS = 50

Source

class RiskEngine: ... def approve(self, trade, portfolio, market): ...

Source

return {"approved": False, "reason": "Maximum market exposure reached" }

Source

approved = risk_engine.approve(trade, portfolio, market)

Source

market = get_market() if market is None: return reject("Market state unavailable")

Source

  • Record date: 2026-10-04T20:25:50.192Z