Polymarket Bot Tutorial · Chapter 29 of 32
লাইভে যাওয়ার আগে একটি Polymarket paper trading engine তৈরি করুন: real prices-এর বিরুদ্ধে orders simulate করুন, P&L track করুন, কোনো live capital ব্যবহার করার আগে 30-trade gate (>=55% win rate, +PnL) enforce করুন, এবং code skeleton তৈরি করুন।
এই chapter-এ কী covered আছে
Paper trading হলো strategy idea আর live deployment-এর মাঝের non-negotiable step। এই chapter-এ আছে simple paper engine, যা আমরা shipped করা প্রতিটি live bot-এর আগে gate হিসেবে ব্যবহার করেছি - 200 lines-এরও কম Python, প্রতিটি trade JSONL diary-তে track করে, live path-এর মতোই fees/slippage apply করে।
- কেন live-এর আগে paper (সবসময়)
- 30-trade gate (verified +55% WR + positive PnL)
- একটি simple paper engine তৈরি করা
- live diary-এর পাশাপাশি paper diary track করা
- কখন paper live থেকে diverge করে (এবং কেন)
- live-এ graduate করা: first deposit ছোট রাখা
- Code: minimal paper engine
কেন live-এর আগে paper (সবসময়)
30-trade paper gate হলো সেই single discipline, যা profitable Polymarket traders-এর 7.6% অংশকে 84.1% loss করা trader থেকে আলাদা করে। বেশিরভাগ builder এটি skip করে এবং tuition দেয়। এটা কাজ করে - এই honest reason হলো: paper trading যথেষ্ট sample-এ strategy-এর real win rate reveal করে, যাতে signal আর luck আলাদা করা যায়।
Paper skip করা saving-এর চেয়ে বেশি খরচ করায়। Backtest-এ profitable দেখানো কিন্তু আসলে coin flip হওয়া একটি strategy live capital-এর $200-500 খেয়ে ফেলতে পারে, তার আগে 30-sample size-এর live data তৈরি করার আগেই। একই 30 trades paper-এ করতে খরচ $0।
Paper engine-কে sophisticated হওয়ার দরকার নেই। এটিকে honest হতে হবে - same fees, same slippage, live path-এর মতোই same fill latency। যত simple তত ভালো, কারণ যা কিছু optional, তা কেটে ফেলা হয় এবং 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 যথেষ্ট narrow হয় signal আর noise আলাদা করার জন্য। 30-এর নিচে, 60% observed rate-এর মানে true rate 45-75% হতে পারে। 30+ এ interval narrows to ~50-70% - এখনও wide, তবে “strategyটা coin flip” নয়, এটা rule out করার জন্য যথেষ্ট।
Success criteria অবশ্যই paper run শুরু হওয়ার আগেই set করতে হবে। পরে set করলে post-hoc rationalization হয় (আপনি যেকোনো 30 trades-কে “good enough” বলে interpret করার উপায় খুঁজে পাবেন)।
একটি simple paper engine তৈরি করা
Paper engine মূলত live trading code-ই, শুধু order-placement function-এর জায়গায় simulated fill বসানো হয়। Simulation:
- Live order book পড়ে: live bot যেমন call করত, ঠিক সেই call।
- Fill simulate করে: যদি FOK-এ buy করেন এবং price ≥ best ask হয়, তাহলে asks consumed-এর volume-weighted average-এ order fill হবে; paper diary-তে fill record হবে।
- Fees apply করে: live path যে same fees দিত, তা subtract করে।
- Inventory track করে: parallel paper-balance এবং paper-positions dictionary maintain করে।
পুরো engine 100-200 lines-এর Python-এ fit হয়ে যায়। Key discipline: live path যা কিছু assume করে (fill rate, latency, fee), paper-এ সেটাই reproduce করতে হবে, এমনকি reality থেকে সামান্য worse হলেও - paper হওয়া উচিত floor, ceiling নয়।
live diary-এর পাশাপাশি paper diary track করা
Paper trading run একটি JSONL diary তৈরি করে, যা structure-এ পরে bot যে live diary লিখবে তার থেকে আলাদা করা যায় না। Same fields: timestamp, action, market_slug, side, size, price, expected_fill_price, simulated_pnl_at_exit।
একই format ব্যবহার করার দুটো কারণ। প্রথমত, live trades পড়ে এমন analysis tools (PnL reports, win-rate calculators) কোনো modification ছাড়াই paper-এ কাজ করে। দ্বিতীয়ত, পরে paper আর live compare করলে divergence ধরা পড়ে, যা bug-এর ইঙ্গিত দেয়।
Production tip: paper engine-কে live per_trade.jsonl-এর একই directory-তে per_trade_paper.jsonl এ লিখতে বলুন। 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-এর মধ্যে inevitable divergence হয়। তিনটি common one:
- Slippage: paper ask snapshot-এ fill হয়; live book walk করে এবং thin markets-এ 1-2c worse fill হতে পারে। Solution: paper-এ per-trade penalty যোগ করে slippage simulate করুন, spread-এর অর্ধেকের সমান।
- Fill latency: paper instantly fill হয়; live-এ 200-500ms লাগে, এই সময়ে price move করতে পারে। Solution: paper-এ “filling” করার আগে wait করে book re-read করে simulate করুন।
- Adverse selection: paper ধরে নেয় আপনি best ask পেলেন; live-এ অন্য bot-এর সঙ্গে compete করতে হয়, যারা হয়তো ইতিমধ্যেই সেই ask তুলে নিয়েছে। Solution: simulate করা কঠিন; নিজেকে honest disclosure দিন যে paper overestimate করে।
Paper যদি বলে +5%/month, আর live চলে -2%/month-এ, gap সাধারণত এদের একটির কারণে হয়। Strategy-টাকে ভুল ধরে নেওয়ার আগে একে একে audit করুন।
Live-এ graduate করা: first deposit ছোট রাখা
Paper 30 trades pass করেছে। Live deployment plan:
- $25-50 deposit করুন smoke-test capital হিসেবে। এটিকে tuition হিসেবে ধরুন; হারালে শেখার দাম তা-ই ছিল।
- Bot-টি live mode-এ 5-10 trades চালান, minimum size (5 shares)-এর positions নিয়ে।
- প্রতিটি fill paper expectations-এর সঙ্গে 2c-এর মধ্যে match করে কিনা verify করুন। তার চেয়ে বড় gap হলে continue করার আগে 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 (book-এ price পোস্ট করা, mid price-এ পৌঁছালে fill simulate করা), paper diary আর “would-have-been” live diary-এর মধ্যে reconciliation।





