Polymarket Bot Tutorial · Chapter 29 of 32
Live जाने से पहले Polymarket paper trading engine बनाएं: real prices के खिलाफ orders simulate करें, P&L track करें, किसी भी live capital से पहले 30-trade gate (>=55% win rate, +PnL) enforce करें, और code skeleton तैयार करें.
यह chapter क्या कवर करता है
Paper trading strategy idea और live deployment के बीच का non-negotiable step है। यह chapter वही simple paper engine है जिसने हमारे shipped हर live bot को gate किया है - 200 lines of Python से कम, हर trade को JSONL diary में track करता है, और live path जैसी same fees/slippage लागू करता है।
- Live से पहले paper क्यों (हमेशा)
- 30-trade gate (verified +55% WR + positive PnL)
- Simple paper engine बनाना
- Paper diary को live diary के साथ track करना
- जब paper live से diverge करता है (और क्यों)
- Live में graduate करना: small first deposit
- Code: minimal paper engine
Live से पहले paper क्यों (हमेशा)
30-trade paper gate वह एक discipline है जो profitable Polymarket traders के 7.6% को उन 84.1% से अलग करती है जो loss करते हैं। ज़्यादातर builders इसे skip कर देते हैं और tuition चुकाते हैं। इसका ईमानदार कारण यह है: paper trading पर्याप्त samples पर strategy की true win rate reveal करता है ताकि signal और luck में फर्क किया जा सके।
Paper skip करना जितना बचाता है उससे कहीं ज़्यादा cost कराता है। एक strategy जो backtest में profitable लगती है लेकिन असल में coin flip है, 30-sample size का live data देने से पहले $200-500 live capital जला देगी। वही 30 trades paper-trade करने की cost $0 है।
Paper engine को sophisticated होने की ज़रूरत नहीं है। इसे honest होना चाहिए - same fees, same slippage, same fill latency जैसी live path में होती है। जितना simple हो उतना बेहतर, क्योंकि जो भी optional होता है उसे cut कर दिया जाता है और bot live में ज़रूरत से पहले ship हो जाता है।
30-trade gate (verified +55% WR + positive PnL)
Gate binary है: 30 closed paper trades, पहले से लिखे गए success criteria (आमतौर पर positive-EV strategy पर WR ≥ 55%), या फिर live deployment नहीं।
30 वह minimum sample size है जहाँ true win rate पर 95% confidence interval signal और noise को अलग करने के लिए पर्याप्त narrow होता है। 30 से नीचे, 60% observed rate true rate 45-75% के बीच किसी भी चीज़ का प्रतिनिधित्व कर सकता है। 30+ पर interval लगभग 50-70% तक narrow हो जाता है - अभी भी wide है, लेकिन यह कहने के लिए काफी है कि "strategy coin flip नहीं है।"
Success criteria paper run शुरू होने से पहले ही set होने चाहिए। बाद में set करने से post-hoc rationalization होती है (आप किसी भी 30 trades को "good enough" समझने का तरीका निकाल लेंगे)।
Simple paper engine बनाना
Paper engine essentially live trading code ही है, बस order-placement function को simulated fill से swap किया गया है। Simulation:
- Live order book read करें: वही call जो live bot करेगा।
- Fill simulate करें: अगर FOK पर खरीद रहे हैं और price ≥ best ask है, तो order को asks के consumed volume-weighted average पर fill करें; fill को paper diary में record करें।
- Fees लागू करें: वही fees subtract करें जो live path देगा।
- Inventory track करें: parallel paper-balance और paper-positions dictionary maintain करें।
पूरा engine 100-200 lines of Python में fit हो जाता है। मुख्य discipline: live path जो भी assumption लेता है (fill rate, latency, fee), उसे paper में reproduce करना चाहिए, भले ही reality से थोड़ा worse ही क्यों न हो - paper floor होना चाहिए, ceiling नहीं।
Paper diary को live diary के साथ track करना
Paper trading run एक JSONL diary produce करता है जो structure में उस live diary से indistinguishable होता है जिसे bot बाद में लिखेगा। Same fields: timestamp, action, market_slug, side, size, price, expected_fill_price, simulated_pnl_at_exit.
Same format use करने के दो कारण हैं। पहला, live trades पढ़ने वाले analysis tools (PnL reports, win-rate calculators) बिना किसी modification के paper पर भी काम करते हैं। दूसरा, बाद में paper को live से compare करने पर उन divergences का पता चलता है जो bugs की ओर इशारा करते हैं।
Production tip: paper engine को per_trade_paper.jsonl में, live per_trade.jsonl के same directory में लिखने दें। Single command दोनों को compare करता है: diff -y <(jq -r .market_slug per_trade.jsonl) <(jq -r .market_slug per_trade_paper.jsonl).
जब paper live से diverge करता है (और क्यों)
Paper और live के बीच divergences inevitable हैं। तीन common ones:
- Slippage: paper ask snapshot पर fill होता है; live book walk करता है और thin markets में 1-2c worse fill हो सकता है। Solution: paper में per-trade penalty जोड़कर slippage simulate करें, जो spread के half के बराबर हो।
- Fill latency: paper instantly fill होता है; live में 200-500ms लगते हैं, और उस दौरान price move हो सकती है। Solution: paper में "filling" से पहले wait करके book दोबारा read करें।
- Adverse selection: paper assume करता है कि best ask आपको मिल गया; live में दूसरे bots compete कर रहे होते हैं जिन्होंने शायद वह ask पहले ही lift कर लिया हो। Solution: simulate करना कठिन है; अपने आप से ईमानदारी से स्वीकार करें कि paper overestimate करता है।
जब paper कहे +5%/month और live -2%/month पर चले, तो gap आमतौर पर इन्हीं में से एक होता है। Strategy खुद गलत थी, ऐसा मानने के बजाय एक-एक करके audit करें।
Live में graduate करना: small first deposit
Paper 30 trades pass कर लेता है। Live deployment plan:
- $25-50 smoke-test capital के रूप में deposit करें। इसे tuition मानें; अगर खो जाए, तो lesson worth it था।
- Bot को live mode में 5-10 trades के लिए minimum size (5 shares) positions के साथ चलाएं।
- हर fill verify करें कि वह paper expectations से 2c के भीतर match करता है। कोई बड़ा gap हो तो आगे बढ़ने से पहले investigate करें।
- अगर 5-10 live trades paper से match करते हैं, तो $200-500 deposit करें और normal-size positions चलाएं।
- अगर match नहीं करते, तो halt करें, debug करें, fix करें, step 1 से restart करें।
पहली deployment पर सबसे common live-paper gap missing fee या slippage misestimate होता है। इन्हें fix करना straightforward है; discipline यह है कि capital scale करने से पहले gap पकड़ लिया जाए।
Code: minimal paper engine
Reference: simple paper engine जो live book पढ़ता है + FOK fill simulate करता है।
import json, time
PAPER_BAL = 10_000.0 # USD starting
positions = {} # token_id -> shares
def paper_fok_buy(token_id, max_price, size):
book = fetch_book(token_id)
# Walk asks, fill what we can within max_price
filled = 0; cost = 0
for level in book.asks:
px = float(level["price"])
if px > max_price: break
avail = float(level["size"])
take = min(avail, size - filled)
filled += take
cost += take * px
if filled >= size: break
if filled < size:
return {"status":"rejected","filled":0} # FOK semantics
global PAPER_BAL
PAPER_BAL -= cost
positions[token_id] = positions.get(token_id, 0) + filled
log_paper({"ts": int(time.time()), "action":"buy",
"token": token_id, "size": filled, "price": cost/filled})
return {"status":"matched","filled":filled,"cost":cost}
Production additions: paper sell function (buy का mirror), paper GTC simulation (price पर book पर post करें, mid price reach होने पर fill simulate करें), paper diary और "would-have-been" live diary के बीच reconciliation।












