मुख्य सामग्री पर जाएं
engineeringbacktestingalgorithms
Backtest झूठ क्यों बोलते हैं: Indicator-आधारित Exit का Simulation दिखने से कहीं ज़्यादा मुश्किल है

Backtest झूठ क्यों बोलते हैं: Indicator-आधारित Exit का Simulation दिखने से कहीं ज़्यादा मुश्किल है

एक backtest एक नंबर देता है, और नंबर लुभावने होते हैं। असली दिक्कत यही है। जब कोई simulation बारीकी से गलत होता है, तो न वह crash होता है और न आपको चेतावनी देता है। वह आपको हकीकत से थोड़ा बेहतर नतीजा थमा देता है और आपको उसी पर कदम उठाने देता है।

एक तय take-profit का simulation आसान है: या तो कीमत आपके level तक पहुँची, या नहीं पहुँची। Indicator-आधारित entry या exit का simulation ही मुश्किल होता है, क्योंकि अब simulation को एक कहीं ज़्यादा पेचीदा सवाल का जवाब देना पड़ता है: उस पल पर bot को असल में क्या पता था?

यहाँ तीन तरीके हैं जिनसे हमने वह सवाल गलत समझा, और हर एक ने हमें क्या सिखाया।

मुख्य बातें

  • क्लासिक bug है किसी higher-timeframe indicator को उसकी bar बंद होने से पहले पढ़ना। bar open पर देखा गया 4-hour RSI अपने साथ चार घंटे की उस price action को ढोता है जो अभी हुई ही नहीं थी।
  • उपाय heuristics नहीं, गणित है: timeframe T की value तभी वैध रूप से इस्तेमाल की जा सकती है जब T बीत चुका हो, इसलिए lookup को (trigger timeframe घटा simulation timeframe) जितना पीछे खिसका दें।
  • Live engine के पास यही दिक्कत एक अलग भेस में है। अभी बन रही candle पर गणना किया गया indicator repaint करता है, यानी एक condition उसी bar पर सच हो सकती है और फिर झूठी।
  • जो simulator अपनी replay न कर सकने वाली चीज़ को खामोशी से छोड़ देता है, वह live bot से ज़्यादा उदार होता है, और यह फासला हमेशा backtest के पक्ष में जाता है।
  • ईमानदार कदम है इस फासले को नतीजे में सामने लाना, न कि छिपाना। जो backtest आपको बता दे कि वह कहाँ झूठ बोल रहा है, वह उस backtest से ज़्यादा कीमती है जो साफ-सुथरा दिखता है।

1. वह look-ahead bug जो multi-timeframe strategies में छिपा रहता है

यही वह bug है जिसे लिखना सबसे आसान और पकड़ना सबसे मुश्किल है।

मान लीजिए simulation एक बार में एक 5-minute candle आगे बढ़ता है, और strategy में एक 4-hour RSI पर कोई condition है। हर 5-minute कदम पर engine को उस RSI की एक value चाहिए। भोला-भाला implementation उस 4-hour bar को ढूँढता है जिसमें मौजूदा पल आता है और उसकी value पढ़ लेता है।

वह एक time machine है।

एक 4-hour bar जो 12:00 पर खुली, उसके पास तब तक कोई अंतिम RSI value नहीं होती जब तक वह 16:00 पर बंद न हो जाए। अगर simulation 12:05 पर खड़ा है और उस bar को पढ़ता है, तो वह चार घंटे की price action से निकाली गई एक संख्या पढ़ रहा है जो, 12:05 के नज़रिए से, अभी हुई ही नहीं है। ऐसे में strategy ठीक उन moves से पहले entry करती दिखती है जिन्हें वह आते हुए देख ही नहीं सकती थी, और equity curve शानदार लगती है।

उपाय कोई heuristic नहीं है। यह इसी बात से निकलता है कि आप लिख लें कि कोई value असल में कब जानी जा सकती है। triggerMs timeframe पर t समय पर खुलने वाली bar केवल t + triggerMs पर जानी जा सकती है। Simulation अपना फैसला मौजूदा candle के close पर, यानी candleOpenTime + simulationMs पर करता है। तो value ठीक तभी वैध रूप से इस्तेमाल की जा सकती है जब:

t + triggerMs <= candleOpenTime + simulationMs
t             <= candleOpenTime - (triggerMs - simulationMs)

यानी सही lookup वही है जो आपके पास पहले से था, बस उसे triggerMs - simulationMs जितना पीछे खिसके timestamp पर चलाया जाए।

उस expression के दो गुण बताने लायक हैं, क्योंकि यही उसे हर जगह लागू करने के लिहाज़ से सुरक्षित बनाते हैं:

  • Same-timeframe conditions को शून्य shift मिलता है। 5-minute candle के close पर पढ़ा गया 5-minute indicator अपनी ही close-आधारित value है, जो हमेशा सही थी। केवल सचमुच higher timeframes ही खिसकते हैं।
  • Shift निकाला जाता है, तय नहीं किया जाता। यह किसी ऐसी setting से नहीं, बल्कि खुद candle से आता है जिसे किसी को याद रखकर सेट करना पड़े।

आम सबक जो मैं किसी भी simulation पर लागू करूँगा, सिर्फ trading पर नहीं: जब आपको पक्का न हो कि आपमें look-ahead bias है या नहीं, तो हर input के लिए वह timestamp लिख लें जिस पर वह जानी जा सकती है, और उसकी तुलना उस timestamp से करें जिस पर आप उसे इस्तेमाल करते हैं। Bias वही बचता है जब आप यह कदम छोड़कर अपने अंदाज़े पर भरोसा करते हैं, और अंदाज़ा यहाँ खराब साबित होता है क्योंकि "मौजूदा 4-hour bar" पढ़ना सुनने में पूरी तरह वाजिब लगता है।

2. Live code में यही bug एक अलग भेस पहनता है

आप सोचेंगे कि यह दिक्कत सिर्फ backtesting की है। नहीं है। Live engine को भी इसका सामना करना पड़ता है, और वहाँ इसे repainting कहते हैं।

किसी भी पल पर, सबसे ताज़ा candle अभी बन ही रही होती है। उस candle पर खत्म होने वाली window पर गणना किया गया indicator candle बदलने के साथ बदलता रहेगा। एक condition 12:01 पर सच, 12:03 पर झूठी, और 12:04 पर फिर सच हो सकती है, सब कुछ उसी एक bar के भीतर, बिना market के कुछ असामान्य किए। अगर bot इनमें से पहली पर कदम उठाता है, तो उसने ऐसी value पर कदम उठाया जो अब मौजूद ही नहीं है।

हमारा default है कि अभी बन रही candle को छोड़ दें और केवल confirmed bars पर गणना करें, ताकि कोई signal कभी repaint न करे। यही वह चीज़ भी है जो live और backtest को आपस में मिलाती है, क्योंकि simulation अपनी बनावट से ही बंद bars के साथ काम करता है।

पर "कभी repaint न करना" मुफ्त नहीं है। Confirmation का इंतज़ार करने का मतलब है देर से कदम उठाना, और कुछ strategies के लिए यह देरी उस noise से ज़्यादा महँगी पड़ती है जिससे वह बचती है। इसलिए इसे per-condition विकल्प के रूप में सामने रखा जाता है: confirmed bar पर मूल्यांकन करें, या live मूल्यांकन करें और मान लें कि value आपके नीचे से खिसक सकती है।

Design का जो हिस्सा मैं संभालकर रखूँगा वह यह है: जब कोई trade-off सचमुच trade-off हो, तो उसे किसी default में दबाकर उसे तथ्य होने का दिखावा न करें। सुरक्षित विकल्प को default बनाइए, फिर user को जानबूझकर दूसरे विकल्प में जाने दीजिए। जो आपको हरगिज़ नहीं करना चाहिए वह यह कि दोनों modes live और backtest के बीच आपस में न मिलें, क्योंकि तब user दो अलग bots की तुलना कर रहा होता है।

3. जो simulator replay नहीं कर सकता वह उससे ज़्यादा खतरनाक है जो वह गलत करता है

पहले दो correctness bugs हैं। यह एक design समस्या है, और मुझे लगता है इसे सबसे कम आँका जाता है।

कुछ conditions replay ही नहीं की जा सकतीं। एक बाहरी webhook signal इसका सबसे साफ उदाहरण है: इसका कोई ऐतिहासिक रिकॉर्ड नहीं है कि पिछले March के किसी दिन 14:20 पर कोई बाहरी system क्या भेजता। Simulation को बस पता ही नहीं चल सकता।

तो उसे क्या करना चाहिए? लुभाने वाला जवाब है उस condition को छोड़ देना और बाकी का मूल्यांकन करना। यही वह चीज़ है जो नतीजे को पूरा दिखाती है। यही वह जगह भी है जहाँ नुकसान होता है, और इसका तरीका खोलकर बताने लायक है:

अगर किसी strategy को A AND B चाहिए, और B का simulation नहीं हो सकता, तो B को छोड़ने से A अकेला फैसला करता रह जाता है। simulated bot उन conditions पर entry कर लेता है जहाँ live bot बाहर ही रुका रहता। Simulation असली चीज़ से व्यवस्थित रूप से ज़्यादा उदार होता है।

दिशा पर गौर कीजिए। यह error बेतरतीब नहीं है। AND logic के तहत किसी condition को हटाने से हकीकत से हमेशा ज़्यादा trades ही निकल सकते हैं, कभी कम नहीं, इसलिए bias हर बार बेहतर दिखने वाले नतीजे की ओर इशारा करता है। यही असमानता इसे खतरनाक बनाती है: बेतरतीब noise कई runs पर औसत में घुल जाता है, पर एक-दिशा वाला bias नहीं घुलता।

हमने simulation को चलाते रहने का चुनाव किया, इनकार करने का नहीं, क्योंकि एक ज्ञात फासले के साथ अधूरा नतीजा भी उपयोगी है। पर तब वह फासला output में दिखना चाहिए, किसी log में दबा हुआ नहीं। नतीजा एक साफ चेतावनी अपने साथ रखता है, user की अपनी भाषा में, ऐसे नाम के साथ जो बताता है कि क्या हुआ: "कुछ Entry Conditions का Simulation नहीं हुआ।" Exits के लिए भी इससे मिलती-जुलती एक चेतावनी है।

इस system के जिस फैसले का मैं सबसे कड़ाई से बचाव करूँगा वह यही है। जो simulator ऊँची आवाज़ में fail नहीं हो सकता, वह चुपचाप fail होगा, और जो चीज़ नंबर बनाती है उसमें चुपचाप fail होना सबसे बुरा failure mode है, क्योंकि लोग नंबरों पर कदम उठाते हैं, stack traces पर नहीं।

इससे क्या सीखें

अगर आप ऐसा कुछ भी बना रहे हैं जो एक नंबर देने के लिए इतिहास को replay करता है:

  • हर input को एक "knowable at" timestamp चाहिए। अगर आप उसे बता नहीं सकते, तो आप look-ahead bias को खारिज नहीं कर सकते।
  • अपनी errors की दिशा जाँचिए, सिर्फ उनका आकार नहीं। जो error हमेशा एक ही ओर इशारा करता है वह जमा होता जाता है; जो बेतरतीब ओर इशारा करता है वह औसत में घुल जाता है। एक-दिशा वाला आशावाद look-ahead bias की पहचान है।
  • Simulator और live system को अपनी decision logic साझा करवाइए जहाँ भी हो सके। "क्या यह condition सच है" के दो अलग implementations में फर्क आ ही जाएगा, और आपको इसका पता किसी test से नहीं बल्कि किसी user से चलेगा।
  • जो आप नहीं कर सके, उसकी रिपोर्ट दीजिए। किसी नतीजे की विश्वसनीयता उसकी बताई गई सीमाओं से आती है, उसके साफ-सुथरेपन से नहीं।

असहज निचोड़ यह है कि एक backtest एक counterfactual के बारे में दावा है, और counterfactuals को चुपचाप गलत कर देना आसान है। इंजीनियरिंग का लक्ष्य ऐसा simulator नहीं है जो कभी झूठ न बोले। लक्ष्य ऐसा है जो आपको बता दे कि वह कहाँ झूठ बोल रहा है।


संबंधित पठन

खाता बनाएं

आज ही Freya खाता खोलकर अपने ज्ञान को व्यवहार में लाएं।

अभी रजिस्टर करें
यह सामग्री केवल शैक्षिक उद्देश्यों के लिए है और वित्तीय सलाह नहीं है। क्रिप्टोकरेंसी ट्रेडिंग में महत्वपूर्ण जोखिम शामिल हैं। कभी भी अपनी क्षमता से अधिक निवेश न करें।