লুক-অ্যাহেড বায়াস কী এবং ব্যাকটেস্টে এর প্রভাব
লুক-অ্যাহেড বায়াস ব্যাকটেস্টে ভবিষ্যত তথ্য ব্যবহার করলে ফলাফল স্ফীত করে। একই বার সিদ্ধান্ত, সারভাইভারশিপ ও সংশোধিত ডেটা কীভাবে ফলাফল নষ্ট করে এবং তা পরীক্ষার উপায় জানুন।
লুক-অ্যাহেড বায়াস হলো ব্যাকটেস্টে এমন তথ্য ব্যবহার করা যা সিমুলেটেড ট্রেড করার সময় কারও কাছে থাকতে পারেনি। এর ফলে ইকুইটি কার্ভ উপরে ওঠে, পরিসংখ্যান চমৎকার দেখায়, কিন্তু লাইভ অর্ডারের সংস্পর্শে এলে তার কিছুই টিকে না। এই পৃষ্ঠাটি বায়াসটিকে সংজ্ঞায়িত করে, এটি তৈরি করে এমন তিনটি প্রক্রিয়া ব্যাখ্যা করে এবং বাস্তব বাজার তথ্যের উপর প্রতিটিকে পরিমাপ করে।
লুক-অ্যাহেড বায়াস কী
একটি ব্যাকটেস্ট একটি সিদ্ধান্ত সম্পর্কে একটি দাবি: এই টাইমস্ট্যাম্পে, এই তথ্য দিয়ে, নিয়মটি এটি করত। দাবিটি তখনই সত্য হয় যখন নিয়মটি পড়া প্রতিটি ইনপুট তার চূড়ান্ত আকারে, সেই টাইমস্ট্যাম্পের আগেই বিদ্যমান ছিল। লুক-অ্যাহেড বায়াস হলো সেই শর্তের যেকোনো লঙ্ঘন, এবং এটি নীরব। একটি স্প্রেডশিট বা স্ক্রিপ্টের কিছুই অভিযোগ করে না যখন একটি সারি তার খাওয়ানো সিদ্ধান্তের চেয়ে পরে স্ট্যাম্প করা একটি মান ফেরত দেয়।
তিনটি লঙ্ঘন বাস্তবে দেখা যাওয়া বেশিরভাগ সমস্যাকে কভার করে। একটি সিদ্ধান্ত যা একটি প্রাইস বার পড়ে যার ভিতরেও এটি ট্রেড করে। একটি ইউনিভার্স যা আজও তালিকাভুক্ত নামগুলি থেকে তৈরি। একটি ডেটা ফিল্ড যা তার বহন করা তারিখের পরে সংশোধন করা হয়েছিল।
প্রক্রিয়া এক: একটি বারের উপর সিদ্ধান্ত নেওয়া যাতে আপনি ট্রেডও করেন
একটি নিয়ম দিয়ে শুরু করুন যা নিরীহ শোনায়। শক্তির উপর কিনুন, অর্থাৎ সেই সেশনে যেখানে ফান্ড তার খোলার চেয়ে উপরে শেষ হয়। অসতর্কভাবে কোড করা হলে, পরীক্ষাটি সেই সেশনের ওপেনে প্রবেশ করে এবং সেই সেশনের ক্লোজে প্রস্থান করে, যখন এটি যে শর্তটি পরীক্ষা করে তা হল একই বারের ক্লোজিং প্রাইস। উদ্বোধনী বেলায় কেউ ক্লোজিং প্রাইস জানে না।
নীচের প্যানেলটি দশ ক্যালেন্ডার বছর জুড়ে SPY, S&P 500 ট্র্যাকার, উভয় সংস্করণ চালায়। প্রথম কলামটি অসম্ভবটি। দ্বিতীয়টি অভিন্ন শর্তটিকে একটি একক সেশন পিছিয়ে দেয়, যা একটি তহবিলযুক্ত অ্যাকাউন্ট এটির উপর কাজ করতে পারে তার প্রথমতম সময়।
প্রতিটি সংখ্যার পেছনের সঠিক SQL
WITH bars AS (
SELECT date,
toFloat64(open) AS o,
toFloat64(close) AS c,
toFloat64(close) > toFloat64(open) AS up_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
lagged AS (
SELECT date, o, c, up_day,
any(up_day) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prior_up
FROM bars
)
SELECT toYear(date) AS year,
countIf(up_day) AS signal_days,
round(100 * avgIf(c / o - 1, up_day), 3) AS same_bar_avg_pct,
round(100 * avgIf(c / o - 1, prior_up), 3) AS lagged_avg_pct,
round(100 * (avgIf(c / o - 1, up_day) - avgIf(c / o - 1, prior_up)), 3) AS gap_pp
FROM lagged
WHERE date >= toDate('2016-01-01')
GROUP BY year
ORDER BY yearপার্থক্যটি সামান্য নয়। 2016-এ, একই-বার সংস্করণটি প্রতি যোগ্য সেশনে গড়ে 0.447% অর্জন করেছিল, যেখানে পিছিয়ে দেওয়া সংস্করণটি গড়ে -0.008% অর্জন করেছিল, যা প্রতিদিন 0.455 শতাংশ পয়েন্টের একটি স্প্রেড। 2025-তে দুটি 0.636% এবং -0.03% পড়ে। এক বছরে প্রায় 140 যোগ্য সেশনের সাথে, প্রতি সেশনে অর্ধ শতাংশ পয়েন্টের একটি স্প্রেড একটি ইকুইটি কার্ভে পরিণত হয় যা কোনো লাইভ অ্যাকাউন্ট কখনও প্রিন্ট করেনি।
পুরো সুবিধাটি সংজ্ঞাগত। যে সেশনগুলি তাদের ওপেনের উপরে বন্ধ হয়েছে সেগুলি নির্বাচন করা, তারপর সেই একই সেশনগুলিতে ওপেন-টু-ক্লোজ পরিমাপ করা, একটি পরিমাণ পরিমাপ করে যা ইতিবাচক হওয়ার নিশ্চয়তা। নিয়মটি তার নিজস্ব ফিল্টার আবিষ্কার করেছে।
এর বৈচিত্রগুলি প্রতিদিনের ডেটাতে সর্বদা লুকিয়ে থাকে: একটি মুভিং এভারেজ যা আজকের ক্লোজ অন্তর্ভুক্ত করে এবং আজ ট্রেড করতে ব্যবহৃত হয়, একটি সিগন্যাল যা ইতিমধ্যে একটি স্প্লিটের জন্য সামঞ্জস্য করা দামে গণনা করা হয় যা এখনও ঘটেনি, একটি ইন্ট্রাডে স্টপ যা একটি সেশন হাই-এ স্থাপন করা হয় যা এখনও প্রিন্ট হয়নি। সাধারণ আকৃতি হল একটি সিদ্ধান্ত টাইমস্ট্যাম্প যা তার নিজস্ব ইনপুটগুলির একটির চেয়ে আগে বসে।
প্রক্রিয়া দুই: আজকের টিকে থাকা থেকে তৈরি একটি ইউনিভার্স
দ্বিতীয় প্রক্রিয়াটি কখনই নিয়মটিকে স্পর্শ করে না। এটি নিয়মটিকে দেখার অনুমতি দেওয়া নামের তালিকায় বসে।
একটি বর্তমান ডেটা প্রদানকারী থেকে একটি টিকার তালিকা টানুন, এটি দশ বছর পিছিয়ে চালান, এবং নমুনাটি নীরবে প্রতিটি কোম্পানিকে বাদ দেয় যা দেউলিয়া হয়ে গেছে, অধিগ্রহণ করা হয়েছে, বা পথে ডিলিস্ট করা হয়েছে। নিয়মটিকে কখনই পরাজিতদের দেওয়া হয়নি। প্যানেলটি সেই বাদ দেওয়ার আকার পরিমাপ করে: একটি নির্দিষ্ট ক্যালেন্ডার বছরে কমপক্ষে 200 সেশন $1-এর উপরে ট্রেড করা প্রতিটি মার্কিন-তালিকাভুক্ত নাম, জুলাই 2026-এ এখনও প্রাইস প্রিন্ট করা সেই কোহর্টের ভাগ এবং পুরো কোহর্টের মধ্যমা ক্যালেন্ডার-বছরের রিটার্ন শুধুমাত্র টিকে থাকাদের মধ্যমার পাশে।
প্রতিটি সংখ্যার পেছনের সঠিক SQL
WITH recent AS (
SELECT DISTINCT ticker
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2026-06-15')
AND date <= toDate('2026-07-28')
),
per_name AS (
SELECT toYear(date) AS year,
ticker,
argMin(toFloat64(close), date) AS first_close,
argMax(toFloat64(close), date) AS last_close,
count() AS sessions
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2015-01-01')
AND date <= toDate('2024-12-31')
AND close > 1
GROUP BY year, ticker
HAVING sessions >= 200
)
SELECT year,
uniqExact(ticker) AS names_trading,
round(100 * uniqExactIf(ticker, ticker IN (SELECT ticker FROM recent)) / uniqExact(ticker), 1) AS still_listed_pct,
round(100 * quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker)), 2) AS median_return_all_pct,
round(100 * quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent)), 2) AS median_return_survivors_pct,
round(100 * (quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent))
- quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker))), 2) AS survivor_gap_pp
FROM per_name
GROUP BY year
ORDER BY year2015-এ বারটি পরিষ্কার করা 7063 নামের মধ্যে, 52.5% জুলাই 2026-এ এখনও ট্রেড করছিল। 2024 কোহর্টের জন্য চিত্রটি 87.5%, যা মূলত অতিবাহিত সময়ের ব্যাপার: একটি 2024 নামের অদৃশ্য হতে দুই বছর হয়েছে এবং একটি 2015 নামের এগারো বছর হয়েছে।
রিটার্ন কলামগুলি বায়াস নিজেই বহন করে। 2017-এ মধ্যমা নামটি 7.51% ফিরিয়ে দিয়েছে, যেখানে মধ্যমা টিকে থাকা 11.32% ফিরিয়ে দিয়েছে, যা বিনামূল্যে হাতে দেওয়া 3.81 শতাংশ পয়েন্টের একটি ব্যবধান। চার্ট করা 10 বছরের প্রতিটিতে ব্যবধানটি ইতিবাচক। টিকে থাকাদের উপর পরীক্ষিত একটি কৌশল একটি একক সিদ্ধান্ত নেওয়ার আগেই এটি উত্তরাধিকার সূত্রে পায়, এবং দুর্বলতার জন্য স্ক্রিন করা নিয়মগুলি মধ্যমার চেয়ে বেশি উত্তরাধিকার সূত্রে পায়, কারণ অদৃশ্য হওয়া নামগুলি অসামঞ্জস্যপূর্ণভাবে সেই বালতিতে বসে।
সংশোধন হল একটি পয়েন্ট-ইন-টাইম ইউনিভার্স: প্রতিটি রিব্যালেন্স তারিখে যেমন ছিল নামের তালিকা, ডিলিস্টিং সহ, একটি ডিলিস্টেড পজিশন রেকর্ড থেকে বাদ দেওয়ার পরিবর্তে তার চূড়ান্ত প্রিন্টে বন্ধ করা হয়।
প্রক্রিয়া তিন: ডেটা যা তার তারিখ স্ট্যাম্পের পরে সংশোধন করা হয়েছিল
দাম একবার স্ট্যাম্প করা হয়। মৌলিক বিষয়গুলি নয়। একটি ত্রৈমাসিক চিত্র ত্রৈমাসিকের শেষ তারিখ বহন করে, পরবর্তী ফাইলিং দ্বারা সংশোধন করা হয়, এবং একটি ডাটাবেস যা শুধুমাত্র বর্তমান সংস্করণ সঞ্চয় করে, স্বেচ্ছায় সংশোধিত সংখ্যাটি মূল তারিখের বিরুদ্ধে পরিবেশন করবে। সেই সংখ্যার উপর স্ক্রিন করা একটি নিয়ম একটি সংশোধন পড়ছে যা আসতে মাস লেগেছে।
একই প্যাটার্ন বর্তমান উপাদানগুলিতে পুনর্নির্মিত সূচক সদস্যতা তালিকা, চূড়ান্ত ঐকমত্যের সাথে ওভাররাইট করা বিশ্লেষক আয়ের ইতিহাস এবং একটি সম্পূর্ণ মূল্য সিরিজ জুড়ে পশ্চাৎমুখী প্রয়োগ করা কর্পোরেট অ্যাকশন সমন্বয়ের মাধ্যমে চলে। প্রতিটি ক্ষেত্রে, সংরক্ষিত রেকর্ডটি একটি বর্তমান-কালের স্ন্যাপশট যা অতীত-কালের তারিখ পরিধান করে।
দুটি অনুশীলন এটিকে ধারণ করে। একটি উৎস ব্যবহার করুন যা ভিন্টেজ রাখে, অর্থাৎ এখন যেমন জানা যায় তার পরিবর্তে প্রতিটি তারিখে যেমন জানা ছিল সেই মান। যেখানে ভিন্টেজ পাওয়া যায় না, সেখানে একটি রিপোর্টিং ল্যাগ প্রয়োগ করুন যা বাস্তবটিকে কভার করার জন্য যথেষ্ট উদার, এবং সেই ল্যাগের দৈর্ঘ্যকে অনুমান করার মতো একটি বিবরণের পরিবর্তে পরীক্ষা করার মতো একটি প্যারামিটার হিসাবে বিবেচনা করুন।
ভবিষ্যৎ ফাঁস হলে কার্ভটি কেমন দেখায়
লুক-অ্যাহেড বায়াসের একটি স্বাক্ষর রয়েছে: কর্মক্ষমতা যা অন্তর্নিহিত বাজার যা দিয়েছে তার থেকে অনেক উপরে বসে। একটি দরকারী রেফারেন্স পয়েন্ট হল রেট্রোস্পেক্ট সিলিং, একটি নিখুঁত একদিন-অ্যাহেড পূর্বাভাস একটি একক ইন্সট্রুমেন্টে যে বৃদ্ধি তৈরি করত।
প্রতিটি সংখ্যার পেছনের সঠিক SQL
WITH bars AS (
SELECT date,
toFloat64(close) AS c,
any(toFloat64(close)) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
rets AS (
SELECT toYear(date) AS year,
c / prev_c - 1 AS r
FROM bars
WHERE date >= toDate('2016-01-01')
AND prev_c > 0
)
SELECT year,
count() AS sessions,
round(100 * (exp(sum(log(1 + r))) - 1), 1) AS buy_and_hold_pct,
round(100 * (exp(sumIf(log(1 + r), r < 0.02)) - 1), 1) AS without_big_up_days_pct,
round(exp(sum(log(1 + abs(r)))), 1) AS hindsight_ceiling_multiple
FROM rets
GROUP BY year
ORDER BY yearসিলিংটি বিশাল। 2020-এ, SPY-তে নিখুঁত একদিনের দূরদর্শিতা 253 সেশনে মূলধনকে 27.7 গুণ করেছে, যেখানে কেবল ফান্ডটি ধরে রাখার জন্য 16.2% ছিল। দশটির মধ্যে সবচেয়ে শান্ত বছর, 2017-এ, একই নিখুঁত পূর্বাভাস 2.2 গুণ মূলধন ফিরিয়ে দিয়েছে।
মাঝের কলামটি দেখায় যে সিলিংটি কতটা কেন্দ্রীভূত। 2% এর বেশি লাভ করা সেশনগুলি বাদ দিন, এবং 2020 16.2% থেকে -44.9%-এ পরিণত হয়। বছরে মাত্র কয়েকটি সেশনের মূল্যের একটি ফাঁস একটি ব্যাকটেস্টকে অনেক দূরে সরিয়ে দেয়। অগভীর ড্রডাউন, একটি উচ্চ হিট রেট এবং বাজারের সবচেয়ে হিংস্র সেশনে ক্লাস্টার করা সেরা দিন সহ একটি কার্ভ একটি অডিট অর্জন করেছে।
যেকোনো একক থ্রেশহোল্ডের চেয়ে ক্যালিব্রেশন বেশি গুরুত্বপূর্ণ। একটি নিয়ম যা এক দশক ধরে একটি মসৃণ 40% বার্ষিক দাবি করে, তা রেট্রোস্পেক্ট সিলিংয়ের একটি বড় ভগ্নাংশ দাবি করছে, এবং সৎ প্রশ্ন হল কোন ইনপুট এটিকে বলেছে কী ঘটতে চলেছে।
লুক-অ্যাহেড বায়াসের জন্য কীভাবে একটি ব্যাকটেস্ট পরীক্ষা করবেন
- সবকিছু এক বার পিছিয়ে দিন। প্রতিটি সিগন্যালকে এক পূর্ণ সময়কাল পরে স্থানান্তর করুন এবং পুনরায় চালান। প্রকৃত প্রান্তগুলি কিছুটা ক্ষয় হয়। ফাঁসগুলি ভেঙে পড়ে, প্রায়শই কিছুই থাকে না।
- ভবিষ্যৎকে ব্যাহত করুন। প্রতিটি সিদ্ধান্ত টাইমস্ট্যাম্পের পরে ডেটা নিন, এটিকে নয়েজ বা একটি রিশাফল দিয়ে প্রতিস্থাপন করুন এবং সিদ্ধান্তের যুক্তি পুনরায় চালান। প্রতিটি সিদ্ধান্ত অভিন্ন হওয়া উচিত। যে কোনো সিদ্ধান্ত যা সরে যায়, তা এমন কিছু পড়েছে যা এটি জানতে পারেনি।
- টাইমস্ট্যাম্পগুলিকে ডেটা হিসাবে বিবেচনা করুন। প্রতিটি ইনপুটের জন্য, একটি মান উপলব্ধ হওয়ার মুহূর্তটি এটি বর্ণনা করার মুহূর্তের পাশে সংরক্ষণ করুন। একটি জয়েন যা কখনই সেই দুটি কলামের তুলনা করে না, নিয়মটি প্রয়োগ করতে পারে না।
- তারিখ অনুযায়ী ইউনিভার্স পুনর্নির্মাণ করুন। ডিলিস্টেড এবং অধিগ্রহণ করা নামগুলি অন্তর্ভুক্ত করুন, তাদের শেষ প্রিন্টে বন্ধ করুন এবং সারভাইভারশিপ অবদানের আকার নির্ধারণ করতে উভয় ইউনিভার্সে একই নিয়ম চালান।
- সিলিংয়ের বিরুদ্ধে পরিমাপ করুন। একই উইন্ডোর জন্য কৌশলের রিটার্নকে একটি বাই অ্যান্ড হোল্ড ফিগার এবং একটি নিখুঁত-দূরদর্শিতা ফিগারের পাশে রাখুন। সিলিং থেকে দূরত্ব হল প্রসঙ্গ যা একটি শার্প রেশিও একা দেয় না।
পজিশন সাইজিং এই সবের ডাউনস্ট্রিমে বসে। একটি স্ফীত জয়ের হার একটি স্ফীত বেট সাইজকে খাওয়ায়, যেখানে কেলি ক্রাইটেরিয়ন পজিশন সাইজিং একটি অ্যাকাউন্টিং ত্রুটিকে একটি ড্রডাউনে পরিণত করে। বাস্তব মূলধন সরানোর আগে একই শৃঙ্খলা প্রযোজ্য: বাস্তব টাকার আগে পেপার ট্রেডিং অর্ডার মেকানিক্স পরীক্ষা করে, এবং স্বয়ংক্রিয় স্ট্যাকগুলি মেশিনের গতিতে অভিন্ন বিপদ বহন করে, যা মাল্টি এজেন্ট AI ট্রেডিং সিস্টেম-এ আচ্ছাদিত। সতর্ক পয়েন্ট-ইন-টাইম পরীক্ষায় টিকে থাকা প্রভাবগুলি, যেমন লো ভোলাটিলিটি অ্যানোমালি, ঠিক সেই কারণেই আকর্ষণীয়।
FAQ
ব্যাকটেস্টিং-এ লুক-অ্যাহেড বায়াস কী?
এটি একটি সিমুলেটেড ট্রেডে এমন ডেটার ব্যবহার যা ট্রেডটি স্থাপন করার সময় উপলব্ধ ছিল না। সাধারণ রূপগুলি হল একটি প্রাইস বারের উপর সিদ্ধান্ত নেওয়া যার ভিতরে ট্রেডটিও কার্যকর হয়, কোম্পানিগুলির একটি ইউনিভার্স স্ক্রিন করা যা আজও তালিকাভুক্ত, এবং একটি আর্থিক চিত্র পড়া যা এর সাথে সংযুক্ত তারিখের পরে সংশোধন করা হয়েছিল।
আপনি কীভাবে লুক-অ্যাহেড বায়াস সনাক্ত করতে পারেন?
প্রতিটি সিগন্যালকে এক পূর্ণ সময়কাল পিছিয়ে দিন এবং পুনরায় চালান: একটি ফাঁস সাধারণত ভেঙে পড়ে, যখন একটি প্রকৃত প্রান্ত মৃদুভাবে ক্ষয় হয়। একটি শক্তিশালী পরীক্ষা প্রতিটি সিদ্ধান্ত টাইমস্ট্যাম্পের পরে সমস্ত ডেটাকে নয়েজ দিয়ে প্রতিস্থাপন করে এবং নিশ্চিত করে যে প্রতিটি সিদ্ধান্ত অপরিবর্তিত বেরিয়ে আসে।
সারভাইভারশিপ বায়াস কি লুক-অ্যাহেড বায়াসের মতো?
এগুলি স্বতন্ত্র কিন্তু তারা একসাথে ভ্রমণ করে। সারভাইভারশিপ বায়াস হল টিকে থাকা নামগুলি থেকে তৈরি একটি নমুনা, এবং কোন নামগুলি টিকে ছিল তা জানা নিজেই ভবিষ্যতের তথ্য, তাই একটি সারভাইভার-অনলি ইউনিভার্স হল নিয়মের পরিবর্তে ডেটাতে অবস্থিত একটি লুক-অ্যাহেড ফাঁস।
লুক-অ্যাহেড বায়াস সহ ব্যাকটেস্টগুলি কেন এত ভাল দেখায়?
ফাঁসটি ফলাফল নির্বাচন করে, ভবিষ্যদ্বাণী করে না। উপরের প্যানেলে, তাদের নিজস্ব ক্লোজিং প্রাইসে সেশন স্ক্রিন করা 2016-এ পিছিয়ে দেওয়া সংস্করণের তুলনায় গড় সেশন লাভ প্রায় 0.455 শতাংশ পয়েন্ট তৈরি করেছিল, যা সংজ্ঞাগত, অর্জিত নয়।
পয়েন্ট-ইন-টাইম ডেটা কী?
পয়েন্ট-ইন-টাইম ডেটা প্রতিটি ঐতিহাসিক তারিখে যেমন জানা ছিল প্রতিটি মান সঞ্চয় করে, মূল চিত্র এবং প্রতিটি পরবর্তী সংশোধন সহ, সেই দিনের মতো সিকিউরিটিজের ইউনিভার্সের পাশাপাশি। এটি একটি ব্যাকটেস্টের প্রয়োজনীয় রেফারেন্স যে তার ইনপুটগুলি তার সিদ্ধান্তের আগে বিদ্যমান ছিল তা দেখানোর জন্য।
উপরের প্রতিটি চিত্র বাস্তব দৈনিক বারের উপর একটি সংরক্ষিত, সংস্করণযুক্ত কোয়েরি। যেকোনো প্যানেলের SQL প্রসারিত করুন, অথবা Strasmore টার্মিনালে আপনার নিজের নিয়মে ল্যাগ টেস্ট চালান।