पॉलीमार्केट बॉट ट्यूटोरियल · अध्याय 29 / 32
लाइव जाने से पहले पॉलीमार्केट पेपर ट्रेडिंग इंजन बनाएं: वास्तविक मूल्यों के विरुद्ध ऑर्डर सिम्युलेट करें, P&L पर नज़र रखें, किसी भी लाइव पूँजी से पहले 30-ट्रेड गेट (>=55% विनिंग दर, +PnL) लागू करें, और कोड का ढाँचा तैयार करें।
यह अध्याय क्या कवर करता है
पेपर ट्रेडिंग, रणनीति के विचार और लाइव तैनाती के बीच का एक अनिवार्य चरण है। यह अध्याय वही सरल पेपर इंजन है जिसने हमारे भेजे गए हर लाइव बॉट को गेट किया है - 200 पंक्तियों के Python से कम, हर ट्रेड को JSONL डायरी में दर्ज करता है, और लाइव पथ जैसी ही फ़ीस/स्लिपेज लागू करता है।
- लाइव से पहले पेपर क्यों (हमेशा)
- 30-ट्रेड गेट (सत्यापित +55% WR + धनात्मक PnL)
- सरल पेपर इंजन बनाना
- पेपर डायरी को लाइव डायरी के साथ ट्रैक करना
- जब पेपर लाइव से अलग होता है (और क्यों)
- लाइव में उन्नति करना: छोटी पहली जमा
- कोड: न्यूनतम पेपर इंजन
लाइव से पहले पेपर क्यों (हमेशा)
30-ट्रेड पेपर गेट वह एक अनुशासन है जो लाभदायक पॉलीमार्केट ट्रेडरों के 7.6% को उन 84.1% से अलग करता है जो नुक़सान उठाते हैं। ज़्यादातर बिल्डर इसे छोड़ देते हैं और इसकी क़ीमत चुकाते हैं। इसका ईमानदार कारण यह है: पेपर ट्रेडिंग पर्याप्त नमूनों पर रणनीति की असली विनिंग दर उजागर करती है ताकि संकेत और क़िस्मत में फ़र्क किया जा सके।
पेपर छोड़ना जितना बचाता है उससे कहीं ज़्यादा ख़र्च कराता है। एक रणनीति जो बैकटेस्ट में लाभदायक लगती है लेकिन असल में सिक्का उछालने जैसी है, वह 30 नमूनों जितना लाइव डेटा देने से पहले $200-500 लाइव पूँजी जला देगी। वही 30 ट्रेड पेपर-ट्रेड करने की क़ीमत $0 है।
पेपर इंजन को परिष्कृत होने की ज़रूरत नहीं है। इसे ईमानदार होना चाहिए - वही फ़ीस, वही स्लिपेज, वही फ़िल विलंब जो लाइव पथ में होते हैं। जितना सरल हो उतना बेहतर, क्योंकि जो भी वैकल्पिक होता है उसे काट दिया जाता है और बॉट ज़रूरत से पहले ही लाइव भेज दिया जाता है।
30-ट्रेड गेट (सत्यापित +55% WR + धनात्मक PnL)
गेट द्विआधारी है: 30 बंद पेपर ट्रेड, पहले से लिखे गए सफलता मानदंड (आमतौर पर धनात्मक-EV रणनीति पर WR ≥ 55%), या फिर लाइव तैनाती नहीं।
30 वह न्यूनतम नमूना आकार है जहाँ असली विनिंग दर पर 95% विश्वास अंतराल संकेत और शोर को अलग करने के लिए पर्याप्त संकरा होता है। 30 से नीचे, 60% देखी गई दर असली दर के 45-75% के बीच किसी भी मान का प्रतिनिधित्व कर सकती है। 30+ पर अंतराल लगभग 50-70% तक संकरा हो जाता है - अब भी चौड़ा है, लेकिन यह कहने के लिए काफ़ी है कि "रणनीति सिक्का उछालने जैसी नहीं है।"
सफलता मानदंड पेपर रन शुरू होने से पहले ही तय होने चाहिए। बाद में तय करने से बाद-में-गढ़ी गई तर्कसंगति पैदा होती है (आप किन्हीं भी 30 ट्रेड को "काफ़ी अच्छा" मानने का तरीका निकाल लेंगे)।
सरल पेपर इंजन बनाना
पेपर इंजन मूलतः लाइव ट्रेडिंग कोड ही है, बस ऑर्डर-प्लेसमेंट फ़ंक्शन को सिम्युलेटेड फ़िल से बदल दिया गया है। सिम्युलेशन:
- लाइव ऑर्डर बुक पढ़ें: वही कॉल जो लाइव बॉट करेगा।
- फ़िल सिम्युलेट करें: अगर FOK पर ख़रीद रहे हैं और मूल्य ≥ बेस्ट आस्क है, तो ऑर्डर को खपाए गए आस्क के वॉल्यूम-भारित औसत पर फ़िल करें; फ़िल को पेपर डायरी में दर्ज करें।
- फ़ीस लागू करें: वही फ़ीस घटाएँ जो लाइव पथ देगा।
- इन्वेंट्री ट्रैक करें: समानांतर पेपर-बैलेंस और पेपर-पोज़ीशन डिक्शनरी बनाए रखें।
पूरा इंजन 100-200 पंक्तियों के Python में समा जाता है। मुख्य अनुशासन: लाइव पथ जो भी मान्यता लेता है (फ़िल दर, विलंब, फ़ीस), उसे पेपर में दोहराना चाहिए, भले ही वह वास्तविकता से थोड़ा ख़राब ही क्यों न हो - पेपर एक निचली सीमा होनी चाहिए, ऊपरी नहीं।
पेपर डायरी को लाइव डायरी के साथ ट्रैक करना
पेपर ट्रेडिंग रन एक JSONL डायरी बनाता है जो संरचना में उस लाइव डायरी से अभेद्य होती है जिसे बॉट बाद में लिखेगा। वही फ़ील्ड: timestamp, action, market_slug, side, size, price, expected_fill_price, simulated_pnl_at_exit.
वही प्रारूप उपयोग करने के दो कारण हैं। पहला, लाइव ट्रेड पढ़ने वाले विश्लेषण उपकरण (PnL रिपोर्ट, विनिंग-दर कैलकुलेटर) बिना किसी बदलाव के पेपर पर भी काम करते हैं। दूसरा, बाद में पेपर की लाइव से तुलना करने पर उन अंतरों का पता चलता है जो बग की ओर इशारा करते हैं।
प्रोडक्शन सुझाव: पेपर इंजन को per_trade_paper.jsonl में, लाइव per_trade.jsonl की उसी डायरेक्टरी में लिखने दें। एक ही कमांड दोनों की तुलना करता है: diff -y <(jq -r .market_slug per_trade.jsonl) <(jq -r .market_slug per_trade_paper.jsonl).
जब पेपर लाइव से अलग होता है (और क्यों)
पेपर और लाइव के बीच अंतर अवश्यंभावी हैं। तीन आम अंतर:
- स्लिपेज: पेपर आस्क स्नैपशॉट पर फ़िल होता है; लाइव बुक में गहराई तक जाता है और पतले मार्केट में 1-2 सेंट ख़राब फ़िल हो सकता है। समाधान: पेपर में प्रति-ट्रेड दंड जोड़कर स्लिपेज सिम्युलेट करें, जो स्प्रेड के आधे के बराबर हो।
- फ़िल विलंब: पेपर तुरंत फ़िल होता है; लाइव में 200-500 मिलीसेकंड लगते हैं, और उस दौरान मूल्य हिल सकता है। समाधान: पेपर में "फ़िल करने" से पहले प्रतीक्षा करके बुक दोबारा पढ़ें।
- प्रतिकूल चयन: पेपर यह मान लेता है कि बेस्ट आस्क आपको मिल गया; लाइव में दूसरे बॉट प्रतिस्पर्धा कर रहे होते हैं जिन्होंने शायद वह आस्क पहले ही उठा लिया हो। समाधान: सिम्युलेट करना कठिन है; अपने आप से ईमानदारी से स्वीकार करें कि पेपर बढ़ा-चढ़ाकर आँकता है।
जब पेपर कहे +5%/महीना और लाइव -2%/महीना पर चले, तो अंतर आमतौर पर इन्हीं में से एक होता है। रणनीति ख़ुद ग़लत थी, ऐसा मानने के बजाय एक-एक करके जाँच करें।
लाइव में उन्नति करना: छोटी पहली जमा
पेपर 30 ट्रेड पास कर लेता है। लाइव तैनाती योजना:
- $25-50 धुआँ-परीक्षण पूँजी के रूप में जमा करें। इसे सीखने की क़ीमत मानें; अगर खो जाए, तो सबक इसके लायक़ था।
- बॉट को लाइव मोड में 5-10 ट्रेड के लिए न्यूनतम आकार (5 शेयर) की पोज़ीशनों के साथ चलाएँ।
- हर फ़िल की पुष्टि करें कि वह पेपर अपेक्षाओं से 2 सेंट के भीतर मेल खाता है। कोई बड़ा अंतर हो तो आगे बढ़ने से पहले जाँच करें।
- अगर 5-10 लाइव ट्रेड पेपर से मेल खाते हैं, तो $200-500 जमा करें और सामान्य आकार की पोज़ीशनें चलाएँ।
- अगर मेल नहीं खाते, तो रोकें, डिबग करें, ठीक करें, चरण 1 से फिर शुरू करें।
पहली तैनाती पर सबसे आम लाइव-पेपर अंतर, छूटी हुई फ़ीस या स्लिपेज का ग़लत अनुमान होता है। इन्हें ठीक करना सीधा है; अनुशासन यह है कि पूँजी बढ़ाने से पहले अंतर पकड़ लिया जाए।
कोड: न्यूनतम पेपर इंजन
संदर्भ: सरल पेपर इंजन जो लाइव बुक पढ़ता है + FOK फ़िल सिम्युलेट करता है।
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}
प्रोडक्शन जोड़: पेपर बेच फ़ंक्शन (ख़रीद का दर्पण), पेपर GTC सिम्युलेशन (मूल्य पर बुक पर पोस्ट करें, मिड मूल्य पहुँचने पर फ़िल सिम्युलेट करें), पेपर डायरी और "जो होती" लाइव डायरी के बीच मिलान।


