Python میں پوزیشننگ پر مبنی خام حکمت عملی کیسے بنائی جائے۔ [Full Handbook]

کموڈٹیز ٹریڈنگ میں کمٹمنٹ آف ٹریڈرز (COT) ڈیٹا کا بہت حوالہ دیا جاتا ہے، خاص طور پر جب لوگ پرہجوم پوزیشننگ، قیاس آرائی پر مبنی جذبات یا الٹ رسک کے بارے میں بات کرتے ہیں۔ تاہم، زیادہ تر بات چیت خیال کی سطح پر رہتی ہے۔ یہ شاذ و نادر ہی ایسے اصول ہیں جن کا عملی طور پر تجربہ کیا جا سکتا ہے۔

یہ اس منصوبے کا نقطہ آغاز تھا۔

میں یہ دیکھنا چاہتا تھا کہ کیا خام تیل کی پوزیشننگ ڈیٹا کو مبہم مارکیٹ ریڈنگ سے زیادہ مفید چیز میں تبدیل کیا جا سکتا ہے۔ یہ ایک نفیس میکرو بیانیہ نہیں ہے۔ ایک حقیقی حکمت عملی کا فریم ورک جسے آپ کوڈ کر سکتے ہیں، جانچ سکتے ہیں اور اپنے آپ کو چیلنج کر سکتے ہیں۔

یہاں مقصد ایک مکمل حکمت عملی کے ساتھ شروع کرنا نہیں ہے۔ خیال یہ تھا کہ ایک معقول مفروضے کے ساتھ آغاز کریں، قدم بہ قدم سگنل تیار کریں، اور دیکھیں کہ ڈیٹا شامل ہونے کے بعد کیا بچتا ہے۔

اس کے لیے، ہم نے تاریخی ویسٹ ٹیکساس انٹرمیڈیٹ (WTI) خام تیل کی قیمتوں کے ساتھ FinancialModelingPrep سے تاجروں کے عزم کا ڈیٹا استعمال کیا۔ پہلا خیال سادہ تھا۔ قیاس آرائی پر مبنی پوزیشننگ میں انتہائی تبدیلیاں ہمیں یہ بھی بتا سکتی ہیں کہ خام تیل آگے کیا کرے گا۔ لیکن جیسے جیسے تعمیر ترقی کرتی گئی، ان خیالات کو استعمال کے قابل ہونے سے پہلے ان کو کم کرنا، فلٹر کرنا اور دوبارہ کام کرنا پڑا۔

لہذا یہ مضمون ان حکمت عملیوں پر صاف نظر نہیں ہے جنہوں نے پہلی کوشش میں کام کیا۔ یہ وہاں پہنچنے کا پورا عمل ہے۔

انڈیکس

شرطیں

اس مضمون کے لیے Python اور pandas لائبریری کے بارے میں بنیادی معلومات درکار ہیں، کیونکہ ہم ڈیٹا فریمز کا استعمال کرتے ہوئے زیادہ تر ڈیٹا ہیرا پھیری اور تجزیہ کریں گے۔ آپ کو اپنے ماحول میں درج ذیل پیکیجز کو انسٹال کرنا ہوگا۔ requests، numpy، pandasاور matplotlib.

COT اور WTI دونوں خام تیل کی قیمت کے ڈیٹا کو بازیافت کرنے کے لیے آپ کو FinancialModelingPrep API کلید کی بھی ضرورت ہوگی۔ اگر آپ کے پاس اکاؤنٹ نہیں ہے، تو آپ FinancialModelingPrep ویب سائٹ پر مفت اکاؤنٹ کے لیے رجسٹر کر سکتے ہیں۔

آخر میں، ٹریڈر کمٹمنٹ رپورٹ کیا ہوتی ہے اور غیر تجارتی پوزیشننگ کی نمائندگی کرنے کے بارے میں ایک عام فہم سگنلز کی ساخت کے پیچھے ہونے والے استدلال پر عمل کرنے میں آپ کی مدد کرے گی، حالانکہ کوڈ سے ہی قدر حاصل کرنا ضروری نہیں ہے۔

اس مضمون میں مالیاتی منڈیوں اور تجارتی تصورات کے بارے میں کچھ بنیادی معلومات بھی شامل ہیں۔ اگر آپ لمبی اور مختصر پوزیشننگ، کھلی دلچسپی، اور قیاس آرائی پر مبنی جذبات جیسی اصطلاحات سے ناواقف ہیں، تو غوطہ لگانے سے پہلے ان کے بارے میں سیکھنے میں کچھ وقت گزارنا اچھا خیال ہوگا۔

ابتدائی خیال: پوزیشننگ کی انتہاؤں کا استعمال کرتے ہوئے مارکیٹ کے نظام کی وضاحت کریں۔

خیال کا پہلا ورژن تجارتی اصول نہیں تھا۔ یہ ایک فریم ورک تھا۔

اگر خام تیل میں قیاس آرائی پر مبنی پوزیشننگ انتہائی ہو جاتی ہے، تو اس کے مختلف مضمرات ہوں گے اس پر منحصر ہے کہ آگے کیا ہوتا ہے۔ ایک ایسی مارکیٹ جو بہت لمبی ہے لیکن پھر بھی بڑھتی ہوئی بھیڑ ہے اس مارکیٹ سے مختلف ہے جو بہت لمبی ہے لیکن آرام کرنا شروع کر رہی ہے۔ یہی منطق نیچے کے رجحانات پر لاگو ہوتی ہے۔

لہٰذا "انتہائی لمبی عمریں مختصر ہیں” یا "انتہائی شارٹس خریدی جاتی ہیں” جیسے دو ٹوک اشاروں پر مجبور کرنے کے بجائے، ہم نے مارکیٹ کو کئی نظاموں میں تقسیم کرکے آغاز کیا۔

میں نے جو دو متغیرات استعمال کیے وہ آسان تھے: پہلا، حالیہ تاریخ کے مقابلے میں پوزیشننگ کا کتنا شدید موازنہ ہے۔ دوسرا، چاہے وہ پوزیشننگ اب بھی بنائی جا رہی ہے یا ریورس ہونا شروع ہو رہی ہے۔

اس نے مجھے چار ممکنہ ریاستیں دیں:

  • تیزی سے اضافہ

  • پر امید آرام

  • نیچے کی طرف رجحان کی تشکیل

  • کھولنا

یہ براہ راست حکمت عملی میں کودنے سے بہتر نقطہ آغاز کی طرح محسوس ہوا۔ یہ ہمیں سب سے پہلے COT ڈیٹا کو مارکیٹ کی ریاستوں کو بیان کرنے کے طریقے کے طور پر استعمال کرنے کی اجازت دیتا ہے اور پھر یہ جانچنے کی اجازت دیتا ہے کہ آیا ان ریاستوں میں سے کوئی ایک کارآمد قیمت کارروائی کا باعث بنی۔

اس مرحلے پر یہ معلوم نہیں تھا کہ ان میں سے کون سی حکومت برقرار رہے گی۔ نقطہ ایک ایسا ڈھانچہ بنانا تھا جس کا صحیح طریقے سے تجربہ کیا جاسکے۔

پیکج درآمد کریں

آئیے پیکیج کی درآمدات کو کم سے کم اور سادہ رکھیں۔

import requests
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

plt.rcParams["figure.figsize"] = (14,6)
plt.style.use("ggplot")

api_key = "YOUR FMP API KEY"
base_url = "https://financialmodelingprep.com/stable" 

یہاں کچھ بھی پسند نہیں ہے۔ اپنی FMP API کلید کو اپنی اصل FMP API کلید سے تبدیل کریں۔ اگر آپ کے پاس اکاؤنٹ نہیں ہے تو، آپ FMP ڈویلپر اکاؤنٹ کھول کر اکاؤنٹ حاصل کر سکتے ہیں۔

ڈیٹا حاصل کریں: FMP API کا استعمال کرتے ہوئے COT + WTI خام تیل کی قیمتیں۔

اس حکمت عملی کو بنانے کے لیے دو ڈیٹا سیٹ کی ضرورت تھی۔ سب سے پہلے، ہمیں خام تیل کے لیے COT ڈیٹا کی ضرورت تھی۔ دوسرا، ہمیں تاریخی WTI خام تیل کی قیمتوں کی ضرورت تھی۔

میں نے خام تیل کے صحیح معاہدوں کی شناخت کے لیے COT مارکیٹوں کی فہرست کے ساتھ شروعات کی۔

url = f"{base_url}/commitment-of-traders-list?apikey={api_key}"
r = requests.get(url)
cot_list = pd.DataFrame(r.json())

crude_candidates = cot_list[
    cot_list.astype(str)
    .apply(lambda col: col.str.contains("crude", case=False, na=False))
    .any(axis=1)
]

crude_candidates

یہ COT دنیا سے خام تیل سے متعلق معاہدوں کی فلٹر شدہ فہرست فراہم کرتا ہے۔ اس وقت میں نے جو کلیدی معاہدہ استعمال کیا وہ CL تھا۔

cot_symbol = "CL"
start_date = "2010-01-01"
end_date = "2026-03-20"

url = f"{base_url}/commitment-of-traders-report?symbol={cot_symbol}&from={start_date}&to={end_date}&apikey={api_key}"
r = requests.get(url)

cot_df = pd.DataFrame(r.json())
cot_df["date"] = pd.to_datetime(cot_df["date"])
cot_df = cot_df.sort_values("date").drop_duplicates(subset="date").reset_index(drop=True)
cot_df = cot_df.rename(columns={"date": "cot_date"})

cot_df.head()

یہ خام تیل کے لیے ہفتہ وار COT ریکارڈ واپس کرتا ہے۔

ہفتہ وار COT خام تیل کا ڈیٹا

آپ کو بعد میں جن اہم فیلڈز کی ضرورت ہوگی وہ ہیں:

  • date

  • openInterestAll

  • noncommPositionsLongAll

  • noncommPositionsShortAll

اگلا، ہم نے FMP سے کموڈٹی پرائس اینڈ پوائنٹ کا استعمال کرتے ہوئے WTI خام تیل کی قیمت کا ڈیٹا درآمد کیا۔

price_symbol = "CLUSD"
start_date = "2010-01-01"
end_date = "2026-03-20"

url = f"{base_url}/historical-price-eod/full?symbol={price_symbol}&from={start_date}&to={end_date}&apikey={api_key}"
r = requests.get(url)

price_df = pd.DataFrame(r.json())
price_df["date"] = pd.to_datetime(price_df["date"])
price_df = price_df.sort_values("date").drop_duplicates(subset="date").reset_index(drop=True)

price_df

ڈبلیو ٹی آئی خام تیل کی قیمت کا ڈیٹا

چونکہ COT ڈیٹاسیٹ ہفتہ وار ہے، میں نے جمعہ کی بند قیمتوں کا استعمال کرتے ہوئے قیمتوں کی سیریز کو ہفتہ وار بارز میں تبدیل کر دیا۔

price_df["date"] = pd.to_datetime(price_df["date"])
price_df = price_df.sort_values("date").drop_duplicates(subset="date").reset_index(drop=True)

weekly_price = price_df.set_index("date").resample("W-FRI").agg({
    "symbol": "last",
    "open": "first",
    "high": "max",
    "low": "min",
    "close": "last",
    "volume": "sum",
    "vwap": "mean"
}).dropna().reset_index()

weekly_price["weekly_return"] = weekly_price["close"].pct_change()
weekly_price = weekly_price.rename(columns={"date": "price_date"})

weekly_price

یہ مرحلہ اہم ہے کیونکہ دونوں ڈیٹا سیٹ ایک ہی ٹائم اسکیل پر ہونے چاہئیں۔ اگر آپ روزانہ قیمتیں رکھتے ہیں جبکہ COT ہفتہ وار رکھتا ہے، تو سگنل کی سیدھ بہت جلد خراب ہو جائے گی۔

WTI خام تیل کی قیمت کا ہفتہ وار ڈیٹا

آخر میں، ہر COT مشاہدے کو اگلے ہفتے کے WTI پرائس بار میں ایڈجسٹ کیا جاتا ہے۔

merged_df = pd.merge_asof(
    cot_df.sort_values("cot_date"),
    weekly_price.sort_values("price_date"),
    left_on="cot_date",
    right_on="price_date",
    direction="forward"
)

merged_df[["cot_date", "price_date", "close", "weekly_return", "openInterestAll", "noncommPositionsLongAll", "noncommPositionsShortAll"]]

COT اور قیمت کے ڈیٹا کا انضمام

آؤٹ پٹ ایک صاف ورکنگ ٹیبل ہے جس میں شامل ہیں:

یہ حکمت عملی کے لیے مکمل بیس ڈیٹاسیٹ ہے۔ اس کو لاگو کرنے کا اگلا مرحلہ خام مقام کے ڈیٹا کو مزید مفید چیز میں تبدیل کرنا ہے۔

خام COT ڈیٹا کو قابل استعمال خصوصیات میں تبدیل کریں۔

اس وقت خام ڈیٹا تیار تھا، لیکن پھر بھی سگنل کے طور پر مفید نہیں تھا۔ COT رپورٹ پوزیشننگ نمبرز فراہم کرتی ہے، لیکن صرف وہی نمبرز آپ کو زیادہ کچھ نہیں بتاتے جب تک کہ وہ وقت کے ساتھ ساتھ کسی چیز میں تبدیل نہ ہوں۔

لہذا اگلا مرحلہ کچھ ایسی فعالیت بنانا تھا جو ہمیں پوزیشننگ کو زیادہ معنی خیز انداز میں بیان کرنے کی اجازت دے گی۔

میں نے خالصتاً غیر تجارتی پوزیشن میں شروعات کی۔ یہ صرف غیر تجارتی شارٹ سیلنگ اور نان کمرشل شارٹ سیلنگ میں فرق ہے۔

merged_df["net_position"] = merged_df["noncommPositionsLongAll"] - merged_df["noncommPositionsShortAll"]

یہ ایک خام قیاس آرائی پر مبنی تعصب فراہم کرتا ہے۔ مثبت قدر کا مطلب ہے کہ غیر تجارتی تاجر خالص طویل ہیں۔ منفی قدر کا مطلب خالص کمی ہے۔

تاہم، خام نیٹ پوزیشننگ کے مسائل ہیں. وقت کے ساتھ ساتھ مارکیٹ کے سائز بدلتے رہتے ہیں، اس لیے جو قدریں ایک مدت میں بہت زیادہ معلوم ہوتی ہیں ان کا مطلب دوسرے دور میں ایک جیسا نہیں ہو سکتا۔ اس مسئلے کو حل کرنے کے لیے، ہم نے کھلے مفاد پر معیاری بنایا۔

merged_df["net_position_ratio"] = merged_df["net_position"] / merged_df["openInterestAll"]

یہ سگنل کو زیادہ مفید بناتا ہے۔ مطلق پوزیشننگ کو دیکھنے کے بجائے، میں اب پوزیشننگ کو مجموعی مارکیٹ کے حصہ کے طور پر دیکھ رہا تھا۔

اگلا، ہمیں یہ جاننے کی ضرورت ہے کہ آیا پوزیشننگ ابھی بھی بنائی جا رہی ہے یا کھولنا شروع ہو رہی ہے۔ ایسا کرنے کے لیے، ہم نے ریاست کے لحاظ سے فیصد تبدیلیوں کا حساب لگایا۔

merged_df["net_position_ratio_change"] = merged_df["net_position_ratio"].diff()

یہ اہم تھا کیونکہ تبدیلی کی سمت سیاق و سباق کو جوڑتی ہے۔ ایک انتہائی لمبی پوزیشن جو اب بھی بڑھ رہی ہے ایک انتہائی لمبی پوزیشن جیسی نہیں ہے جس میں کمی آنا شروع ہو گئی ہے۔

آخری خصوصیت سب سے اہم خصوصیت ہے: پوزیشننگ تناسب کا رولنگ فیصد۔ میں نے 104 ہفتے کی مدت استعمال کی۔

def rolling_percentile(x):
    return pd.Series(x).rank(pct=True).iloc[-1]

merged_df["position_percentile_104"] = merged_df["net_position_ratio"].rolling(104).apply(rolling_percentile)

یہ آپ کو بتاتا ہے کہ گزشتہ دو سالوں کے مقابلے میں موجودہ پوزیشن کس قدر شدید ہے۔ 0.80 سے زیادہ قدر کا مطلب ہے کہ مارکیٹ حالیہ تاریخ کے مقابلے میں تیزی کی پوزیشننگ کے ٹاپ 20% میں ہے۔ 0.20 سے نیچے کی قدر کا مطلب ہے کہ مارکیٹ نیچے 20٪ میں ہے۔

چاروں فنکشنز کو شامل کرنے کے بعد، میں نے آؤٹ پٹ کو چیک کیا۔

merged_df[["cot_date","price_date","net_position","net_position_ratio","net_position_ratio_change","position_percentile_104"]]

حتمی انضمام_df

پہلی چند سطریں ہیں۔ net_position_ratio_change تھا NaNاس کی توقع ہے کیونکہ پہلی قطار میں موازنہ کرنے کے لیے کوئی پچھلے ہفتے نہیں ہیں۔ پہلی 103 لائنیں position_percentile_104 بھی NaN اس کی وجہ یہ ہے کہ رولنگ پیریڈ میں پرسنٹائل کا حساب لگانے کے لیے 104 ہفتوں کا ریکارڈ درکار ہوتا ہے۔

یہ ٹھیک تھا۔ اہم بات یہ ہے کہ اب ہمارے پاس اپنے ڈیٹاسیٹ میں چار ٹکڑے دستیاب ہیں۔

  • خام اندازے کی پوزیشننگ

  • معمول کی پوزیشننگ

  • ہفتہ وار پوزیشننگ تبدیلیاں

  • اس بات کا ایک رولنگ پیمانہ کہ وہ پوزیشننگ کتنی شدید ہے۔

یہ وہ نقطہ تھا جہاں COT ڈیٹا ڈیٹا بننا شروع ہوا جسے صرف تاجر عہدوں کی میز کے بجائے ایک رجیم ماڈل میں تبدیل کیا جا سکتا ہے۔

حکومتی ماڈل کے پہلے ورژن کی تعمیر

خصوصیات کے تیار ہونے کے بعد، اگلا مرحلہ انہیں حقیقی مارکیٹ کے حالات میں ترجمہ کرنا تھا۔

مرکزی خیال سادہ تھا۔ اپنے طور پر غلو کرنا کافی نہیں ہے۔ مارکیٹ ایک طویل عرصے تک بہت لمبی یا بہت مختصر ہوسکتی ہے۔ زیادہ اہم بات، جب پوزیشننگ انتہائی ہو تو کیا ہوتا ہے؟ کیا آپ اب بھی اسے بنا رہے ہیں یا یہ گھومنا شروع کر رہا ہے؟

تو میں نے دو جہتیں استعمال کیں۔

ان دو متغیرات کا استعمال کرتے ہوئے، ہم نے چار حکومتوں کی وضاحت کی۔

merged_df["regime"] = "neutral"

merged_df.loc[(merged_df["position_percentile_104"] > 0.8) & (merged_df["net_position_ratio_change"] > 0), "regime"] = "bullish_buildup"
merged_df.loc[(merged_df["position_percentile_104"] > 0.8) & (merged_df["net_position_ratio_change"] < 0), "regime"] = "bullish_unwind"
merged_df.loc[(merged_df["position_percentile_104"] < 0.2) & (merged_df["net_position_ratio_change"] < 0), "regime"] = "bearish_buildup"
merged_df.loc[(merged_df["position_percentile_104"] < 0.2) & (merged_df["net_position_ratio_change"] > 0), "regime"] = "bearish_unwind"

یہاں ہر ایک کا کیا مطلب ہے:

  • تیزی سے اضافہ: پوزیشننگ پہلے ہی بہت پرامید ہے اور اب بھی مضبوط سے مضبوطی کی طرف جا رہی ہے۔

  • پر امید آرام: پوزیشننگ بہت تیز ہے، لیکن تیزی ختم ہونے لگی ہے۔

  • نیچے کی طرف رجحان کی تشکیل: پوزیشننگ پہلے ہی بہت مندی ہے اور بہت مندی رہتی ہے۔

  • کھولنا: پوزیشننگ بہت مندی ہے، لیکن یہ کمزوری کم ہونے لگی ہے۔

کوئی بھی چیز جو ان انتہائی شرائط میں سے ایک کو پورا نہیں کرتی ہے۔ neutral بالٹی

حکومتیں تفویض کرنے کے بعد، ہم نے جانچا کہ ہر حکومت میں کتنے مشاہدات آئے۔

print(merged_df["regime"].value_counts())

حکومتوں کی تعداد

یہ آؤٹ پٹ اہم ہے کیونکہ یہ آپ کو بتاتا ہے کہ آیا فریم ورک قابل استعمال ہے یا بہت کم۔ اس معاملے میں، نیوٹرلز اب بھی سب سے بڑا گروپ تھا، جس کی توقع تھی۔ زیادہ تر ریاستوں کو انتہائی نہیں ہونا چاہئے۔ چار حکومتی بالٹیاں چھوٹی تھیں لیکن پھر بھی مناسب طریقے سے جانچنے کے لیے کافی مشاہدات تھے۔

ہم نے ترتیب شدہ قطاروں کے نمونے کو بھی دیکھا۔

merged_df[["cot_date","price_date","net_position_ratio","net_position_ratio_change","position_percentile_104","regime"]].tail(10)

merged_df + regime

اس مقام پر، خام COT ڈیٹا کو رجیم ماڈل میں تبدیل کر دیا گیا تھا۔ اگلا سوال یہ تھا کہ کیا ان میں سے کوئی بھی حکومت درحقیقت کارآمد قیمتوں کی کارروائی کا باعث بنی۔

پہلا امتحان: ہر حکومت کے بعد کیا ہوتا ہے؟

اس وقت، میرے پاس ایک فریم ورک تھا لیکن کوئی حکمت عملی نہیں تھی۔ ان ریاستوں کی تجارت کی طرف رجوع کرنے سے پہلے، میں جاننا چاہتا تھا کہ ہر ریاست کے بعد خام تیل درحقیقت کیا کرتا ہے۔

لہذا، اگلا مرحلہ چار انعقاد کے ادوار میں تمام حکومتوں کے بعد فارورڈ ریٹرن کی پیمائش کرنا تھا۔

  • 1 ہفتہ

  • 2 ہفتے

  • 4 ہفتے

  • 8 ہفتے

میں نے اپنی ہفتہ وار اختتامی سیریز میں فارورڈ ریٹرن کالم بنا کر شروع کیا۔

merged_df["fwd_return_1w"] = merged_df["close"].shift(-1) / merged_df["close"] - 1
merged_df["fwd_return_2w"] = merged_df["close"].shift(-2) / merged_df["close"] - 1
merged_df["fwd_return_4w"] = merged_df["close"].shift(-4) / merged_df["close"] - 1
merged_df["fwd_return_8w"] = merged_df["close"].shift(-8) / merged_df["close"] - 1

merged_df[["cot_date","price_date","close","regime","fwd_return_1w","fwd_return_2w","fwd_return_4w","fwd_return_8w"]].tail(12)

ہر کالم ایک آسان سوال کا جواب دیتا ہے۔ اگر اس ہفتے خام تیل ایک مخصوص نظام میں ہے، تو اگلے 1، 2، 4 یا 8 ہفتوں میں کیا ہوگا؟

ہفتہ وار اختتامی سیریز کے لیے واپسی کالم کو آگے بھیجیں۔

آخری چند قطاروں میں NaN قدریں تھیں، جو کہ معمول کی بات ہے۔ چونکہ ڈیٹا سیٹ کے اختتام کے بعد کوئی مستقبل کی قیمت کا ڈیٹا دستیاب نہیں ہے، اس لیے سب سے لمبا افق سب سے پہلے آتا ہے۔

اگلا، ہم نے نظام کے لحاظ سے ڈیٹا کو گروپ کیا اور کچھ خلاصہ کے اعدادوشمار کا حساب لگایا۔

  • شمار

  • اوسط آگے کی واپسی۔

  • میڈین فارورڈ ریٹرن

  • ہٹ کی شرح

regime_summary = merged_df.groupby("regime").agg(
    count=("regime", "size"),
    avg_1w=("fwd_return_1w", "mean"),
    median_1w=("fwd_return_1w", "median"),
    hit_rate_1w=("fwd_return_1w", lambda x: (x > 0).mean()),
    avg_2w=("fwd_return_2w", "mean"),
    median_2w=("fwd_return_2w", "median"),
    hit_rate_2w=("fwd_return_2w", lambda x: (x > 0).mean()),
    avg_4w=("fwd_return_4w", "mean"),
    median_4w=("fwd_return_4w", "median"),
    hit_rate_4w=("fwd_return_4w", lambda x: (x > 0).mean()),
    avg_8w=("fwd_return_8w", "mean"),
    median_8w=("fwd_return_8w", "median"),
    hit_rate_8w=("fwd_return_8w", lambda x: (x > 0).mean())
).reset_index()

regime_summary

نظام کے لحاظ سے گروپ کردہ ڈیٹا

یہ جدول فریم ورک کا ہمارا پہلا حقیقی امتحان تھا اور اس نے فوری طور پر ہمارے کچھ اصل خیالات کو مسترد کر دیا۔

خام حکومت کے ماڈل کے نتائج اچھے نہیں تھے۔ اصل میں، یہ میرے خیال سے زیادہ کمزور تھا۔

چند چیزیں جو نمایاں ہیں وہ یہ ہیں:

  • neutral اکثر حکومت کی بالٹیوں سے بہتر کارکردگی کا مظاہرہ کیا۔

  • bullish_buildup آپ کمزور نظر آتے رہے۔

  • bearish_buildup کمزور بھی لگ رہا تھا۔

  • bearish_unwind یہ پہلی نظر میں زیادہ مضبوط لگ رہا تھا، لیکن اس میں سے کچھ بڑے الٹا کے ساتھ باہر سے آئے تھے۔

  • bullish_unwind بہت سے معاملات میں یہ واحد حکومت تھی جو کسی حد تک مستحکم نظر آتی تھی۔

اس نے منصوبے کا رخ بدل دیا۔

اب تک کا منصوبہ یہ ہے کہ ایک مکمل چار رجیم فریم ورک قائم کیا جائے اور ریاستوں کو تجارتی قوانین میں منتقل کیا جائے۔ مستقبل کی واپسیوں کو دیکھنے کے بعد، یہ اب کوئی معنی نہیں رکھتا. زیادہ تر حکومتوں نے زیادہ اہمیت نہیں دی۔

لہٰذا، ان چاروں کا تعاقب کرنے کے بجائے، میں نے ایک ایسے نظام پر توجہ مرکوز کرنا شروع کی جو اب بھی امید افزا لگ رہا تھا۔ اپنے ضدی تناؤ کو آرام دیں۔

فیصلہ کرنے سے پہلے، میں تقسیم پر ایک بصری نظر ڈالنا چاہتا تھا اور یہ دیکھنا چاہتا تھا کہ کیا ذرائع کوئی اہم چیز چھپا رہے ہیں۔

حکومت کو قریب سے دیکھیں

خلاصہ ٹیبل مجھے پہلے ہی بتاتا ہے کہ زیادہ تر مقامی نظام کے فریم ورک کمزور ہیں، لیکن میں پھر بھی کسی بھی چیز کو حذف کرنے سے پہلے رویے کو بصری طور پر دیکھنا چاہتا تھا۔

میں نے ایک سادہ چارٹ کے ساتھ آغاز کیا جس میں WTI خام تیل کو قیاس آرائی پر مبنی خالص پوزیشننگ تناسب کے ساتھ رکھا گیا تھا۔

plt.plot(merged_df["price_date"], merged_df["close"], label="wti close")
plt.plot(merged_df["price_date"], merged_df["net_position_ratio"] * 100, label="net position ratio x 100")
plt.title("WTI crude oil price vs speculative net positioning")
plt.xlabel("date")
plt.ylabel("value")
plt.legend()
plt.show()

ڈبلیو ٹی آئی خام تیل کی قیمت اور قیاس آرائی پر مبنی نیٹ پوزیشننگ

اس چارٹ کا مقصد ایک ہی پیمانے پر دو سیریز کا موازنہ کرنا نہیں ہے۔ یہ دیکھنے کا صرف ایک تیز طریقہ ہے کہ آیا خام تیل میں بڑی حرکتیں اس وقت ہوتی ہیں جب قیاس آرائی پر مبنی پوزیشننگ پھیلتی ہے۔

اس کے بعد، میں نے خود 104 ہفتے کی پوزیشننگ پرسنٹائلز کا گراف بنایا۔

plt.plot(merged_df["price_date"], merged_df["position_percentile_104"])
plt.axhline(0.8, linestyle="--", color="b")
plt.axhline(0.2, linestyle="--", color="b")
plt.title("104-week positioning percentile")
plt.xlabel("date")
plt.ylabel("percentile")
plt.show()

104 ہفتہ پوزیشننگ کا فیصد

اس سے حکومت کی منطق کو سمجھنا آسان ہو گیا۔ جب بھی پرسنٹائل 0.80 سے اوپر جاتا ہے، مارکیٹ تیزی کے انتہائی علاقے میں داخل ہو جاتی ہے۔ جب بھی یہ 0.20 سے نیچے آتا ہے، مارکیٹ مندی کے انتہائی زون میں داخل ہو جاتی ہے۔

پھر ہم نے دیکھا کہ ہر دور حکومت میں کتنے مشاہدات درحقیقت آئے۔

regime_counts = merged_df["regime"].value_counts()

plt.bar(regime_counts.index, regime_counts.values)
plt.title("Regime counts")
plt.xlabel("regime")
plt.ylabel("count")
plt.xticks(rotation=30)
plt.show()

حکومتوں کی تعداد

حکومتوں کی تعداد معقول معلوم ہوتی تھی۔ نیوٹرل اب بھی سب سے بڑی بالٹی تھی اور فور سگنل رجیم میں بہت زیادہ ویرل ہونے کے بغیر جانچ کرنے کے لیے کافی مشاہدات تھے۔

اس کے بعد، ہم نے ہر حکومت کے لیے اوسطاً 4 ہفتے کے معروف منافع کی منصوبہ بندی کی۔

avg_4w = regime_summary.set_index("regime")["avg_4w"].sort_values()

plt.bar(avg_4w.index, avg_4w.values)
plt.title("Average 4-week forward return by regime")
plt.xlabel("regime")
plt.ylabel("average return")
plt.xticks(rotation=30)
plt.show()

حکومت کے لحاظ سے اوسطاً 4 ہفتے کی پیشگی واپسی

یہ پہلا مضبوط اشارہ تھا کہ اصل فریم ورک بہت وسیع تھا۔ دونوں جمع حکومتیں کمزور دکھائی دیں۔ bullish_unwind یہ تھوڑا سا مثبت تھا، لیکن زیادہ نہیں. bearish_unwind یہ اوسطاً سب سے مضبوط لگ رہا تھا، جو کہ دلچسپ تھا، لیکن مجھے پھر بھی تقسیم کی جانچ کیے بغیر نتائج پر بھروسہ نہیں تھا۔

تو اگلا، ہم نے 4 ہفتے کی ہٹ ریٹ کو دیکھا۔

hit_4w = regime_summary.set_index("regime")["hit_rate_4w"].sort_values()

plt.bar(hit_4w.index, hit_4w.values)
plt.title("4-week hit rate by regime")
plt.xlabel("regime")
plt.ylabel("hit rate")
plt.xticks(rotation=30)
plt.show()

4-ہفتوں کی ہٹ ریٹ بذریعہ حکومت

ہٹ ریٹ نے بھی ایسی ہی کہانی سنائی۔ bullish_unwind یہ بہتر حکومتوں میں سے ایک تھی، لیکن پھر بھی اتنی طاقتور نہیں کہ اسے حکمت عملی کہنے کا جواز فراہم کر سکے۔ neutral یہ اب بھی بہت اچھی طرح سے کام کر رہا تھا، یعنی رجیم فلٹر ابھی تک صاف کناروں کو پیدا نہیں کر رہا تھا۔

اس وقت میں یہ دیکھنا چاہتا تھا کہ آیا اوسط کچھ بڑی چالوں سے متزلزل ہو رہے ہیں۔ لہذا، میں نے ہر سسٹم کے لیے 4 ہفتے کی واپسی کی تقسیم کا منصوبہ بنایا۔

plot_df = merged_df[["regime", "fwd_return_4w"]].dropna()

plot_df.boxplot(column="fwd_return_4w", by="regime", grid=False)
plt.title("4-week forward return distribution by regime")
plt.suptitle("")
plt.xlabel("regime")
plt.ylabel("4-week forward return")
plt.xticks(rotation=30)
plt.show()

سسٹم کے ذریعے 4 ہفتے کے معروف منافع کی تقسیم

اس چارٹ نے مسئلہ کو بہت واضح کر دیا ہے۔

bearish_unwind یہ اوسطاً مضبوط لگ رہا تھا، لیکن یہ طاقت بہت بڑی الٹا صلاحیت کے ساتھ کچھ بیرونی لوگوں سے آئی۔ یہ اسے پہلے سے طے شدہ حکمت عملی کے طور پر کم مجبور کرتا ہے۔

bullish_buildup اور bearish_buildup یہ سمری ٹیبلز اور ڈسٹری بیوشن دونوں میں کمزور تھا۔

bullish_unwind یہ واحد حکومت تھی جو کسی حد تک مستحکم دکھائی دیتی تھی اور چند لوگوں کے انتہائی مشاہدات پر زیادہ انحصار نہیں کرتی تھی۔

تو تعمیر کی سمت بدل گئی۔

اب تک کا خیال پورے رجیم فریم ورک کو جانچنا اور شاید متعدد راستوں کو برقرار رکھنا ہے۔ اس چارٹ کے بعد اب یہ کوئی معنی نہیں رکھتا۔ زیادہ تر فریم ورک پہلے ہی یہ بتا کر آپ کے لیے کام کر چکے ہیں کہ آپ کو کیا استعمال نہیں کرنا چاہیے۔

لہٰذا چاروں فریم ورکس کی پیروی کرنے کے بجائے، میں نے اپنی توجہ صرف ایک تک محدود کر دی: ڈی-ایکسائزنگ۔

اس موقع پر، bullish_unwind یہ پہلے ہی ایک قابل ذکر اور بڑی حکومت تھی۔ تعمیراتی نظام کمزور تھا bearish_unwind یہ کم قائل تھا کیونکہ اس کی بہت زیادہ طاقت چند بڑی چالوں سے آئی تھی۔

تو توجہ پہلے ہی کسی اور چیز کی طرف مبذول ہو رہی تھی۔ bullish_unwind.

بہر حال، میں نے مکمل طور پر کام کرنے سے پہلے موازنہ کے لیے درج ذیل مراحل میں دو اضافی unwind-based متغیرات رکھے ہیں۔

اس طرح، پہلا بیک ٹیسٹ یہ دکھا سکتا ہے کہ آیا: bullish_unwind کیا یہ عملی طور پر بہتر ہے، یا کیا وسیع تر ریلیز منطق مجموعی طور پر بہتر کام کرتی ہے؟

merged_df["long_bullish_unwind"] = (merged_df["regime"] == "bullish_unwind").astype(int)
merged_df["long_bearish_unwind"] = (merged_df["regime"] == "bearish_unwind").astype(int)
merged_df["long_any_unwind"] = merged_df["regime"].isin(["bullish_unwind", "bearish_unwind"]).astype(int)

print("number of trades:n", merged_df[["long_bullish_unwind", "long_bearish_unwind", "long_any_unwind"]].sum())
merged_df[["cot_date","price_date","regime","long_bullish_unwind","long_bearish_unwind","long_any_unwind"]].tail()

یہ تین سادہ بائنری سگنل پیدا کرتا ہے:

  • long_bullish_unwind یہ صرف 1 ہے جب حکومت مضبوط_ریلیز ہوتی ہے۔

  • long_bearish_unwind یہ صرف 1 ہے جب حکومت گرنے کے رجحان میں ہے۔

  • long_any_unwind 1 اگر ریلیز موڈ میں سے کوئی ایک موجود ہو۔

آؤٹ پٹ ہر سگنل کے لیے سگنل کے واقعات کی تعداد بھی فراہم کرتا ہے، جو اہم ہے کیونکہ اگلا مرحلہ مناسب بیک ٹیسٹنگ ہے۔ ایک سگنل تصوراتی طور پر دلچسپ لگ سکتا ہے، لیکن اگر یہ شاذ و نادر ہی ظاہر ہوتا ہے، تو جانچنے کے لیے بہت کچھ نہیں ہے۔

سگنل کے واقعات کی تعداد

لہذا، حکمت عملی کی تہہ میں جانا، Bullish_unwind پہلے سے ہی اہم راستہ تھا۔ دیگر دو کو ابھی تک برقرار رکھا گیا تھا، لیکن بنیادی طور پر اس کا موازنہ کرنے کے لیے استعمال کیا جاتا تھا کہ اصل میں عمل درآمد کے بعد تجارت کتنی کمزور یا مضبوط ہو گئی۔

اپنے پہلے تجارتی قوانین کی تعمیر

ایک بار جب تین ریلیز پر مبنی سگنل موجود تھے، اگلا مرحلہ انہیں حقیقی تجارت میں تبدیل کرنا تھا۔

میں نے جان بوجھ کر بیک ٹیسٹ کو سادہ رکھا۔

  • صرف طویل مدتی

  • برقرار رکھنے کی مدت 4 ہفتے

  • غیر نقلی لین دین

یہ ضروری ہے کہ حصے اوورلیپ نہ ہوں۔ اگر تجارت فی الحال فعال ہونے کے دوران کوئی نیا سگنل ظاہر ہوتا ہے، تو اسے چھوڑ دیا جاتا ہے۔ اس نے میری تجارتی فہرست کو صاف رکھا اور مجھے اوور لیپنگ پوزیشنز کو اسٹیک کرکے اپنی حکمت عملی کو پھولنے سے روک دیا۔

یہ بیک ٹیسٹنگ فنکشن ہے جو میں نے استعمال کیا ہے:

def run_fixed_hold_backtest(df, signal_col, hold_weeks=4):
    trades = []
    i = 0

    while i < len(df) - hold_weeks:
        if df.iloc[i][signal_col] == 1:
            entry_date = df.iloc[i]["price_date"]
            exit_date = df.iloc[i + hold_weeks]["price_date"]
            entry_price = df.iloc[i]["close"]
            exit_price = df.iloc[i + hold_weeks]["close"]
            trade_return = exit_price / entry_price - 1

            trades.append({
                "signal": signal_col,
                "entry_index": i,
                "exit_index": i + hold_weeks,
                "entry_date": entry_date,
                "exit_date": exit_date,
                "entry_price": entry_price,
                "exit_price": exit_price,
                "trade_return": trade_return
            })

            i += hold_weeks
        else:
            i += 1

    return pd.DataFrame(trades)

یہ فیچر ڈیٹا سیٹ کو اسکین کرتا ہے، چیک کرتا ہے کہ آیا سگنلز فعال ہیں، موجودہ ہفتہ وار بار میں داخل ہوتے ہیں اور 4 ہفتوں کے بعد باہر نکلتے ہیں، اور تجارتی نتائج کو ریکارڈ کرتے ہیں۔

پھر ہم نے اسے تینوں ریلیز پر مبنی سگنلز کے لیے چلایا۔

bullish_unwind_trades = run_fixed_hold_backtest(merged_df, "long_bullish_unwind", hold_weeks=4)
bearish_unwind_trades = run_fixed_hold_backtest(merged_df, "long_bearish_unwind", hold_weeks=4)
any_unwind_trades = run_fixed_hold_backtest(merged_df, "long_any_unwind", hold_weeks=4)

اس کے بعد، ہم نے چیک کیا کہ اصل میں کتنے لین دین ہوئے۔

print("executed bullish_unwind trades:", len(bullish_unwind_trades))
print("executed bearish_unwind trades:", len(bearish_unwind_trades))
print("executed any_unwind trades:", len(any_unwind_trades))

عملدرآمد ٹرانزیکشن

یہ پیداوار پچھلے حصے سے خام سگنل کی گنتی سے کم تھی۔ یہ متوقع ہے کیونکہ اوور لیپنگ سگنلز کو چھوڑ دیا جاتا ہے۔

اس کے بعد، ہم نے تجارتی نتائج کا خلاصہ کرنے کے لیے ایک چھوٹا مددگار فنکشن بنایا اور اسے تینوں حکمت عملیوں پر لاگو کیا۔

def summarize_trades(trades):
    return pd.Series({
        "trades": len(trades),
        "win_rate": (trades["trade_return"] > 0).mean(),
        "avg_trade_return": trades["trade_return"].mean(),
        "median_trade_return": trades["trade_return"].median(),
        "cumulative_return": (1 + trades["trade_return"]).prod() - 1
    })

trade_summary = pd.DataFrame({
    "bullish_unwind": summarize_trades(bullish_unwind_trades),
    "bearish_unwind": summarize_trades(bearish_unwind_trades),
    "any_unwind": summarize_trades(any_unwind_trades)
}).T

trade_summary

بیک ٹیسٹ کے نتائج

یہ ہمارا پہلا مجموعی حکمت عملی کا نتیجہ تھا اور ہم نے درجہ بندی کو بہت تیزی سے صاف کیا۔

bullish_unwind یہ اب بھی تینوں میں بہترین تھا۔ یہ ابھی تک اتنا مضبوط نہیں تھا، لیکن یہ یقینی طور پر دوسرے دو سے بہتر تھا۔

چند چیزیں جو نمایاں ہیں وہ یہ ہیں:

  • bullish_unwind جیت کی بہترین شرح تھی۔

  • bullish_unwind اس نے سب سے زیادہ اوسط اور درمیانی ٹرانزیکشن ریٹرن ریکارڈ کیا۔

  • bearish_unwind اور any_unwind دونوں نے مجموعی بنیادوں پر خراب کارکردگی کا مظاہرہ کیا۔

  • دو ڈی اسکیلیشن حکومتوں کو ملانے سے کوئی فائدہ نہیں ہوا اور صرف مضبوط حکومت کو کمزور کر دیا۔

میں یہ بھی دیکھنا چاہتا تھا کہ ان حکمت عملیوں نے وقت کے ساتھ کس طرح کارکردگی کا مظاہرہ کیا، نہ صرف خلاصہ جدولوں میں۔ لہذا میں نے ہر ایک کے لئے ایک سادہ اثاثہ وکر شامل کیا۔


bullish_unwind_trades["equity_curve"] = (1 + bullish_unwind_trades["trade_return"]).cumprod()
bearish_unwind_trades["equity_curve"] = (1 + bearish_unwind_trades["trade_return"]).cumprod()
any_unwind_trades["equity_curve"] = (1 + any_unwind_trades["trade_return"]).cumprod()

plt.plot(bullish_unwind_trades["exit_date"], bullish_unwind_trades["equity_curve"], label="bullish unwind")
plt.plot(bearish_unwind_trades["exit_date"], bearish_unwind_trades["equity_curve"], label="bearish unwind")
plt.plot(any_unwind_trades["exit_date"], any_unwind_trades["equity_curve"], label="any unwind")
plt.title("Equity curves for 4-week unwind strategies")
plt.xlabel("date")
plt.ylabel("equity multiple")
plt.legend()
plt.show()

اثاثہ کا وکر 4 ہفتے کی حکمت عملی کو کھولنا

اس چارٹ نے اسی نقطہ کو اور بھی واضح طور پر دکھایا۔ bullish_unwind یہ اب بھی مطلق شرائط میں کمزور تھا، لیکن اس نے دوسرے دو کے مقابلے میں بہت بہتر رکھا۔ bearish_unwind یہ حکومتی نظریے کو حقیقی حکمت عملی میں تبدیل کرنے کے عمل کو زندہ رکھنے میں ناکام رہا۔ any_unwind یہ بدتر تھا کیونکہ مجھے دونوں کی کمزوریاں وراثت میں ملی تھیں۔

لہذا، اس مرحلے کے اختتام تک، تصویر بہت واضح ہے.

وسیع تر unwinding خیال مجموعی طور پر اچھی طرح سے کام نہیں کیا. bearish_unwind یہ بیک ٹیسٹنگ کو صاف کرنے کے لیے کھڑا نہیں ہوا۔ any_unwind یہ اور بھی برا تھا۔ اب صرف ایک حکومت باقی رہ گئی ہے جس پر آگے بڑھنا ہے۔ bullish unwind.

لیکن یہاں تک کہ وہ نتائج ابھی تک اتنے مضبوط نہیں تھے۔ حکمت عملی متبادل سے بہتر تھی، لیکن وہاں رکنے کے لیے کافی اچھی نہیں تھی۔ درحقیقت، ہم نے ابھی تک کوئی منافع نہیں کمایا ہے۔

اگلا مرحلہ اس کا موازنہ خریدنے اور ہولڈ کرنے کے لیے تھا کہ آیا واقعی کوئی مفید چیز شامل کی گئی ہے۔

غیر منقطع طاقت اور خریدنے اور انعقاد کا موازنہ

اس موقع پر، bullish_unwind اس نے پہلے ہی حکومت پر مبنی دیگر اقسام کو شکست دی ہے۔ لیکن اس کا اپنے آپ میں ابھی زیادہ مطلب نہیں تھا۔

کمزور متبادل کے مقابلے میں ایک حکمت عملی مہذب معلوم ہو سکتی ہے، لیکن یہ پھر بھی بنیادی ٹیسٹوں میں ناکام ہو سکتی ہے۔ کیا یہ خام تیل رکھنے سے بہتر نتیجہ ہو سکتا ہے؟

تو اگلا مرحلہ اصل کا موازنہ کرنا تھا۔ bullish_unwind ایک سادہ خرید اور ہولڈ بینچ مارک کے خلاف حکمت عملی۔

میں نے ہفتہ وار WTI پرائس سیریز سے خرید و ہولڈ کریو بنا کر شروع کیا۔

buy_hold_df = weekly_price.copy()
buy_hold_df = buy_hold_df.sort_values("price_date").reset_index(drop=True)
buy_hold_df["buy_hold_curve"] = buy_hold_df["close"] / buy_hold_df["close"].iloc[0]

buy_hold_df[["price_date", "close", "buy_hold_curve"]].tail()

ڈیٹا کی خریداری/رٹینشن

پھر میں نے خاموں کو خریدنے اور پکڑنے کا منصوبہ بنایا۔ bullish_unwind حکمت عملی۔

plt.plot(buy_hold_df["price_date"], buy_hold_df["buy_hold_curve"], label="buy and hold wti", linewidth=2, alpha=0.5)
plt.plot(bullish_unwind_trades["exit_date"], bullish_unwind_trades["equity_curve"], label="bullish unwind strategy", color="b")
plt.title("Bullish unwind strategy vs buy and hold crude oil")
plt.xlabel("date")
plt.ylabel("equity multiple")
plt.legend()
plt.show()

طاقت میں تخفیف کی حکمت عملی اور خام تیل کی خریداری اور انعقاد

چارٹ مفید تھا کیونکہ اس نے خام سگنل کے ساتھ صحیح مسئلہ دکھایا۔ bullish_unwind خریدو اور ہولڈ سے زیادہ سلیکٹیوٹی تھی، لیکن اس سلیکٹیوٹی نے حقیقی فائدہ پیدا نہیں کیا۔ اس حکمت عملی میں کچھ حد تک اضافہ ہوا، لیکن مجموعی طور پر یہ اب بھی آسان معیارات سے پیچھے ہے۔

موازنہ کو واضح کرنے کے لیے، ہم نے اپنے نمونے کے لیے مجموعی طور پر خرید اور ہولڈ ریٹرن کا حساب لگایا اور پھر دونوں نتائج کو ایک چھوٹی سی سمری ٹیبل میں پیش کیا۔

buy_hold_return = buy_hold_df["buy_hold_curve"].iloc[-1] - 1

comparison_summary = pd.DataFrame({
    "strategy": ["bullish_unwind", "buy_and_hold"],
    "trades": [len(bullish_unwind_trades), np.nan],
    "win_rate": [(bullish_unwind_trades["trade_return"] > 0).mean(), np.nan],
    "avg_trade_return": [bullish_unwind_trades["trade_return"].mean(), np.nan],
    "cumulative_return": [
        (1 + bullish_unwind_trades["trade_return"]).prod() - 1,
        buy_hold_return
    ]
})

comparison_summary

حکمت عملی بمقابلہ b/h موازنہ لوٹاتا ہے۔

یہ مضمون کا اصل موڑ تھا۔

یہاں تک کہ اگر یہ ہے bullish_unwind اگرچہ وہ حکومت کی بنیاد پر اب تک کے بہترین امیدوار تھے، لیکن پھر بھی اس نے خریدو اور پکڑنے کی حکمت عملی کو کم کارکردگی کا مظاہرہ کیا۔ اس سے نتیجہ بہت واضح ہو جاتا ہے۔ خام سگنل ابھی اتنا مضبوط نہیں تھا۔

اس لیے اب حکومتوں کے درمیان انتخاب کا معاملہ نہیں رہا۔ وہ حصہ پہلے ہی حل ہو چکا ہے۔ اب اصل سوال یہ تھا کہ کیا حکمت عملی کو اوور انجینئرنگ کیے بغیر Bullish_unwind سیٹ اپ کو بہتر بنایا جا سکتا ہے۔

یہ اگلے مرحلے کی طرف لے گیا۔ آپ کو بس ایک سادہ ٹرینڈ فلٹر شامل کرنا ہے۔

ٹرینڈ فلٹر شامل کریں۔

اس مقام پر، کلیدی اشاروں کو محدود کر دیا گیا ہے: bullish_unwindلیکن خام ورژن اب بھی کافی اچھا نہیں تھا۔ خریدو اور ہولڈ کی کارکردگی خراب رہی ہے، جس کا مطلب ہے کہ سگنلز کو زیادہ سیاق و سباق کی ضرورت ہے۔

اگلا خیال سادہ تھا۔ تمام تیزی سے ریلیف کے ساتھ ایک جیسا سلوک نہیں کیا جانا چاہیے۔ اگر خام تیل کی قیمتیں پہلے سے ہی مندی کے رجحان میں ہونے کے دوران قیاس آرائی پر مبنی پوزیشننگ کھولنا شروع ہو جاتی ہے، تو یہ طویل مدتی سگنل لینے کے قابل نہیں ہو سکتا۔ تو میں نے ایک ڈیفالٹ فلٹر شامل کیا۔ bullish_unwind تجارت اس وقت ہوتی ہے جب WTI اپنی 26 ہفتے کی حرکت پذیری اوسط سے زیادہ ہو۔

سب سے پہلے میں نے ایک متحرک اوسط اور بائنری رجحان کا جھنڈا بنایا۔ پھر ہم نے اس فلٹر کو موجودہ فلٹر کے ساتھ جوڑ دیا۔ bullish_unwind طاقت

merged_df["ma_26"] = merged_df["close"].rolling(26).mean()
merged_df["above_ma_26"] = (merged_df["close"] > merged_df["ma_26"]).astype(int)
merged_df["long_bullish_unwind_tf"] = ((merged_df["regime"] == "bullish_unwind") & (merged_df["above_ma_26"] == 1)).astype(int)

یہ اصل سگنل کا فلٹر شدہ ورژن بناتا ہے۔ آؤٹ پٹ ٹرینڈ فلٹر لگانے کے بعد باقی رہ جانے والے تجارتی مواقع کی تعداد کو بھی دکھاتا ہے۔ جیسا کہ توقع کی جاتی ہے، تعداد کم ہوتی ہے. اس سے کوئی فرق نہیں پڑتا کہ باقی سودے بہتر ہیں۔

اگلا، ہم نے فلٹر شدہ سگنلز پر وہی چار ہفتوں کا غیر فالتو بیک ٹیسٹ چلایا۔

bullish_unwind_tf_trades = run_fixed_hold_backtest(
    merged_df,
    "long_bullish_unwind_tf",
    hold_weeks=4
)

filtered_summary = pd.DataFrame({
    "bullish_unwind": summarize_trades(bullish_unwind_trades),
    "bullish_unwind_tf": summarize_trades(bullish_unwind_tf_trades)
}).T

filtered_summary

اصل حکمت عملی اور بہتر حکمت عملی کارکردگی

یہ اس عمل میں پہلی بڑی بہتری تھی۔

فلٹر شدہ ورژن تھوڑا سا بہتر نہیں ہے۔ اس نے حکمت عملی کی پروفائل کو معنی خیز انداز میں تبدیل کر دیا۔

بالکل وہی جو میں فلٹر میں چاہتا تھا۔ اس نے سگنل کو زیادہ سلیکٹیو بنا دیا، بلکہ زیادہ واضح بھی۔

میں نے خام حکمت عملی کے لیے ایکویٹی منحنی خطوط شامل کیے ہیں، ایک فلٹر شدہ ورژن، اور اختلافات کو دیکھنے کے لیے خریدو اور پکڑو۔

bullish_unwind_tf_trades["equity_curve"] = (1 + bullish_unwind_tf_trades["trade_return"]).cumprod()

plt.plot(bullish_unwind_trades["exit_date"], bullish_unwind_trades["equity_curve"], label="bullish unwind")
plt.plot(bullish_unwind_tf_trades["exit_date"], bullish_unwind_tf_trades["equity_curve"], label="bullish unwind + trend filter")
plt.plot(buy_hold_df["price_date"], buy_hold_df["buy_hold_curve"], label="buy and hold wti")
plt.title("Bullish unwind strategy with and without trend filter")
plt.xlabel("date")
plt.ylabel("equity multiple")
plt.legend()
plt.show()

رجحان فلٹرز کے ساتھ اور اس کے بغیر تیزی سے تخفیف کی حکمت عملی

یہ چارٹ تبدیلیوں کو دیکھنا آسان بناتا ہے۔ جب خام حکمت عملی بڑھ گئی، فلٹر شدہ ورژن مکمل نمونے کے مقابلے میں بہت زیادہ مستحکم اور واضح طور پر زیادہ مضبوط تھا۔

تو یہ وہ نقطہ تھا جہاں سے حکمت عملی قابل استعمال ہونے لگی۔ سگنل اب نہیں رہا ہے "انتہائی تیزی کی پوزیشننگ کھولنا شروع ہو رہی ہے۔" کہ: خام تیل اب بھی وسیع تر اپ ٹرینڈ میں ہے جبکہ انتہائی تیزی کی پوزیشننگ کھولنا شروع ہو رہی ہے۔

یہ بہت زیادہ مخصوص اور بہت زیادہ موثر تھا۔

اگلا سوال یہ تھا کہ کیا یہ بہتر ورژن درحقیقت مستحکم تھا، یا یہ ایک خوش قسمت پیرامیٹر انتخاب کی وجہ سے کام کرتا ہے۔

تناؤ کا ٹیسٹ مرتب کریں۔

ٹرینڈ فلٹر نے حکمت عملی کو بہتر کرنے کے بعد بھی، میں اس ورژن کو حتمی نہیں سمجھنا چاہتا تھا یہ دیکھے بغیر کہ یہ کتنا نازک تھا۔

سیٹ اپ طاقتور معلوم ہو سکتا ہے کیونکہ پیرامیٹرز کے ایک درست امتزاج نے کام کیا۔ لہذا اگلا مرحلہ ارد گرد کی مختلف حالتوں کی جانچ کرنا تھا اور یہ دیکھنا تھا کہ آیا نتائج اب بھی برقرار ہیں۔

میں نے بنیادی خیال وہی رکھا۔

  • پر امید آرام

  • صرف طویل مدتی

  • ٹرینڈ فلٹر جاری رہتا ہے۔

پھر میں نے تین چیزوں کو تبدیل کیا:

سب سے پہلے، میں نے مختلف پرسنٹائل کالمز اور تھریشولڈ لیولز کا استعمال کرتے ہوئے تیزی سے ریلیکسیشن سگنل بنانے کے لیے ایک مددگار فنکشن بنایا، اور پھر 52 ہفتے کی مختصر مدت کا استعمال کرتے ہوئے دوسری پرسنٹائل سیریز بنائی۔

def add_bullish_unwind_signal(df, percentile_col, high_threshold, signal_name):
    df[signal_name] = (
        (df[percentile_col] > high_threshold) &
        (df["net_position_ratio_change"] < 0) &
        (df["above_ma_26"] == 1)
    ).astype(int)
    
def rolling_percentile(x):
    return pd.Series(x).rank(pct=True).iloc[-1]

merged_df["position_percentile_52"] = merged_df["net_position_ratio"].rolling(52).apply(rolling_percentile)

اس کی بنیاد پر، ہم نے چار سگنل کی شکلیں بنائیں۔

  • 80ویں پرسنٹائل کی حد کے ساتھ 104 واں پرسنٹائل

  • 85ویں پرسنٹائل کی حد کے ساتھ 104 واں پرسنٹائل

  • 80ویں پرسنٹائل کی حد کے ساتھ 52 ہفتے کا پرسنٹائل

  • 85ویں پرسنٹائل تھریشولڈ کے ساتھ 52 ہفتے کا پرسنٹائل

add_bullish_unwind_signal(merged_df, "position_percentile_104", 0.80, "sig_104_80")
add_bullish_unwind_signal(merged_df, "position_percentile_104", 0.85, "sig_104_85")
add_bullish_unwind_signal(merged_df, "position_percentile_52", 0.80, "sig_52_80")
add_bullish_unwind_signal(merged_df, "position_percentile_52", 0.85, "sig_52_85")

اس کے بعد ہم نے تین انعقاد کے ادوار میں ایک ہی بیک ٹیسٹ چلایا۔

results = []

for signal_col in ["sig_104_80", "sig_104_85", "sig_52_80", "sig_52_85"]:
    for hold_weeks in [2, 4, 8]:
        trades = run_fixed_hold_backtest(merged_df, signal_col, hold_weeks=hold_weeks)

        if len(trades) == 0:
            continue

        results.append({
            "signal": signal_col,
            "hold_weeks": hold_weeks,
            "trades": len(trades),
            "win_rate": (trades["trade_return"] > 0).mean(),
            "avg_trade_return": trades["trade_return"].mean(),
            "median_trade_return": trades["trade_return"].median(),
            "cumulative_return": (1 + trades["trade_return"]).prod() - 1
        })

stress_test = pd.DataFrame(results)
stress_test

تین انعقاد کے ادوار میں بیک ٹیسٹنگ

یہ نتیجہ پورے مضمون کے اہم ترین حصوں میں سے ایک تھا۔ ہم نے دکھایا کہ آیا بہتر حکمت عملی عملی طور پر مستحکم ہے، یا یہ صرف ایک تنگ ورژن میں کام کرتی ہے۔

چند چیزیں فوراً سامنے آ گئیں۔

کہ ہفتہ 104/80 فیصد ورژن واضح طور پر مضبوط ترین خاندان تھا۔ یہ تینوں انعقاد کے ادوار میں برقرار رہا۔

  • 2 ہفتوں کے لیے روکیں: مجموعی واپسی۔ 38.16%

  • 4 ہفتے ہولڈ کریں: مجموعی واپسی۔ 45.95%

  • 8 ہفتے ہولڈ کریں: مجموعی واپسی۔ 19.02%

وہ مستقل مزاجی اہم تھی۔ اس کا مطلب یہ تھا کہ ہولڈ پیریڈ تبدیل ہونے کے لمحے سگنل نہیں ٹوٹے گا۔

کہ 4 ہفتوں تک برقرار رکھا مجموعی طور پر یہ بہترین انتخاب نکلا۔ یہ مندرجہ ذیل ہے:

کہ 8 ہفتے منعقد ہوئے۔ کچھ معاملات میں، اوسط تجارتی منافع قدرے زیادہ تھے، لیکن تجارت کی تعداد کم تھی۔ یہ اسے پتلا اور مرکزی ورژن کے طور پر سنبھالنا مشکل بنا دیتا ہے۔

کہ ہفتہ 104/85 فیصد شارٹ ہولڈز کے لیے ترتیبات بہت محدود تھیں۔ 2-ہفتوں اور 4-ہفتوں کے ورژن منفی گئے، حالانکہ 8-ہفتوں کے انعقاد نے اب بھی معقول کارکردگی کا مظاہرہ کیا۔

کہ 52 ہفتے کی تبدیلی مجموعی طور پر، یہ بہت کم قائل تھا۔ ان میں سے کچھ مثبت تھے، لیکن 104ویں ہفتہ/80ویں پرسنٹائل ورژن کی طرح مستحکم نہیں تھے۔

لہذا، اس مرحلے کے اختتام تک، حتمی ڈھانچہ وہ ورژن نہیں تھا جو ایک بار اچھا لگتا تھا۔ یہ وہ ورژن تھا جو قریب کی اخترتی کی جانچ کے بعد بھی برقرار رہا۔

یہ ہمیں ایک واضح حتمی سیٹ اپ فراہم کرتا ہے۔

حتمی حکمت عملی

اس مرحلے پر، عمل پہلے ہی زیادہ تر فلٹرنگ کر چکا ہے۔

ابتدائی چار نظام کے فریم ورک نے حکمت عملی کے طور پر اچھی طرح سے کام نہیں کیا۔ وسیع تر unwinding خیال بھی کام نہیں کیا. دور اندیشی bullish_unwind سگنل متبادل سے بہتر تھا، لیکن پھر بھی Buy and Hold سے کمزور تھا۔

واحد ورژن جو اس سب سے بچ گیا وہ مندرجہ ذیل ورژن تھا:

  • پر امید آرام

  • 104 ہفتہ پوزیشننگ کا فیصد

  • 80ویں پرسنٹائل کی حد

  • 26 ہفتے کا متحرک اوسط فلٹر

  • 4 ہفتوں تک برقرار رکھا

  • غیر نقلی لین دین

اب تکرار کو روکنا اور حتمی نتیجہ واضح طور پر ظاہر کرنا سمجھ میں آتا ہے۔ ہم نے پہلے فائنل سگنل کو لاک کیا اور منتخب سیٹنگز کا استعمال کرتے ہوئے بیک ٹیسٹ دوبارہ چلایا۔

final_signal = "sig_104_80"
final_hold = 4
final_trades = run_fixed_hold_backtest(merged_df, final_signal, hold_weeks=final_hold)
final_trades["equity_curve"] = (1 + final_trades["trade_return"]).cumprod()

final_summary = pd.DataFrame({
    "metric": [
        "trades",
        "win_rate",
        "avg_trade_return",
        "median_trade_return",
        "cumulative_return"
    ],
    "value": [
        len(final_trades),
        (final_trades["trade_return"] > 0).mean(),
        final_trades["trade_return"].mean(),
        final_trades["trade_return"].median(),
        (1 + final_trades["trade_return"]).prod() - 1
    ]
})

final_summary

وہ آؤٹ پٹ حتمی کارکردگی کا پروفائل فراہم کرتا ہے۔

کارکردگی کا حتمی پروفائل

وہ نمبر پہلے سے ہی پچھلے خام ورژن کے مقابلے میں بہت بڑی بہتری ہیں، لیکن میں پھر بھی ان کا موازنہ ایک جگہ کرنا چاہتا تھا۔ تو میں نے دو بینچ مارکس کی بنیاد پر فائنل ٹیبل بنایا:

  • خریدو اور پکڑو

  • ضدی تناؤ کو آرام کریں۔

final_comparison = pd.DataFrame({
    "strategy": ["buy_and_hold", "bullish_unwind_raw", "bullish_unwind_filtered"],
    "trades": [
        np.nan,
        len(bullish_unwind_trades),
        len(final_trades)
    ],
    "win_rate": [
        np.nan,
        (bullish_unwind_trades["trade_return"] > 0).mean(),
        (final_trades["trade_return"] > 0).mean()
    ],
    "avg_trade_return": [
        np.nan,
        bullish_unwind_trades["trade_return"].mean(),
        final_trades["trade_return"].mean()
    ],
    "cumulative_return": [
        buy_hold_return,
        (1 + bullish_unwind_trades["trade_return"]).prod() - 1,
        (1 + final_trades["trade_return"]).prod() - 1
    ]
})

final_comparison

کارکردگی کا حتمی موازنہ ٹیبل

یہ تعمیر کا مجموعی انعام تھا۔

ٹرینڈ فلٹر صرف حکمت عملی کو تھوڑا سا ہموار نہیں کرتا ہے۔ نتائج بالکل بدل چکے ہیں۔

اس کو قابل توجہ بنانے کے لیے، میں نے تین منحنی خطوط ایک ساتھ بنائے۔

plt.plot(buy_hold_df["price_date"], buy_hold_df["buy_hold_curve"], label="buy and hold wti", linewidth=2, alpha=0.5)
plt.plot(bullish_unwind_trades["exit_date"], bullish_unwind_trades["equity_curve"], label="raw bullish unwind", color="indigo")
plt.plot(final_trades["exit_date"], final_trades["equity_curve"], label="filtered bullish unwind", color="b")
plt.title("Crude oil strategy comparison")
plt.xlabel("date")
plt.ylabel("equity multiple")
plt.legend()
plt.show()

خام تیل کی حکمت عملی کا موازنہ

یہ چارٹ ٹیبل سے مماثل ہے، لیکن اس کی مزید براہ راست وضاحت کرتا ہے۔ خام سگنل بہہ جاتا ہے۔ مجموعی نمونے کے مقابلے میں خریدو اور پکڑو مثبت ہے، لیکن بہت زیادہ شور ہے۔ فلٹر شدہ ورژن واحد ہے جو صاف ستھرا انداز میں ترکیب کرتا ہے۔

میں یہ بھی دکھانا چاہتا تھا کہ فلٹر شدہ لین دین دراصل WTI چارٹ پر کہاں ظاہر ہوتے ہیں۔

plt.plot(merged_df["price_date"], merged_df["close"], label="wti close", linewidth=2, alpha=0.5)
plt.scatter(merged_df.loc[merged_df[final_signal] == 1, "price_date"], merged_df.loc[merged_df[final_signal] == 1, "close"],
            s=25, label="filtered bullish unwind signal", color="b")
plt.title("Filtered bullish unwind signals on WTI crude oil")
plt.xlabel("date")
plt.ylabel("price")
plt.legend()
plt.show()

تیزی سے نرمی کا سگنل WTI خام تیل کے لیے فلٹر کیا گیا۔

یہ مفید ہے کیونکہ یہ ظاہر کرتا ہے کہ حکمت عملی منتخب ہے۔ یہ ہمیشہ فائر نہیں کرتا۔ پوزیشننگ انتہائی تیزی کے علاقے میں رہتی ہے اور تناؤ ختم ہونا شروع ہو جاتا ہے، صرف اس وقت فعال ہوتا ہے جب قیمت کا وسیع رجحان برقرار رہتا ہے۔

ہم نے پوزیشننگ کے معاملے میں بھی ایسا ہی کیا۔

plt.plot(merged_df["price_date"], merged_df["position_percentile_104"], label="104-week percentile", linewidth=2, alpha=0.5)
plt.axhline(0.8, linestyle="--", label="80th percentile")
plt.scatter(merged_df.loc[merged_df[final_signal] == 1, "price_date"], merged_df.loc[merged_df[final_signal] == 1, "position_percentile_104"],
            s=25, label="trade signals", color="indigo")
plt.title("Bullish unwind signals from COT positioning extremes")
plt.xlabel("date")
plt.ylabel("percentile")
plt.legend()
plt.show()

COT پوزیشننگ ایکسٹریم بلش ان وائنڈ سگنل

یہ حتمی چارٹ ان سب کو ایک ساتھ جوڑتا ہے۔ تجارت صرف اس وقت ظاہر ہوتی ہے جب صد فیصد پہلے ہی انتہائی علاقے میں ہو۔ اس کا مطلب یہ ہے کہ سگنل کام کرتا رہتا ہے جیسا کہ اصل میں ڈیزائن کیا گیا تھا۔ یہ خام حکومت کے فریم ورک سے کہیں زیادہ نظم و ضبط کے ساتھ کرنے کے بارے میں ہے۔

اضافی بہتری

ابھی بھی کچھ جگہیں ہیں جہاں ہم مزید آگے بڑھ سکتے ہیں۔

پہلا نفاذ حقیقت پسندی ہے۔ فی الحال، یہ حکمت عملی صاف ہفتہ وار داخلے اور خارجی قوانین کا استعمال کرتی ہے، لیکن اس میں پھسلن، اسپریڈز، یا معاہدے کی سطح پر عمل درآمد کی رکاوٹیں شامل نہیں ہیں۔ اس کو شامل کرنے سے نتائج مزید سخت ہو جاتے ہیں۔

دوسرا سگنل کی گہرائی ہے۔ یہ ورژن صرف غیر تجارتی پوزیشننگ، ٹرینڈ فلٹرز اور فکسڈ ہولڈنگ پیریڈ استعمال کرتا ہے۔ یہ جانچنے کے قابل ہو سکتا ہے کہ آیا تجارتی پوزیشننگ، اتار چڑھاؤ کے فلٹرز، یا ڈائنامک کرن سیٹ اپ کو زیادہ پیچیدہ کیے بغیر چیزوں کو بہتر بنا سکتے ہیں۔

نتیجہ

یہ ایک مکمل حکمت عملی کے بجائے ایک وسیع COT خیال کے طور پر شروع ہوا۔ پہلا رجیم فریم ورک معقول معلوم ہوتا تھا، لیکن ایک بار جب ہم نے ڈیٹا پر اس کا تجربہ کیا، تو اس میں سے زیادہ تر برقرار نہیں رہے۔ وہ حصہ اہم تھا۔ کیونکہ یہ حتمی سگنل کو زیادہ تنگ اور زیادہ صاف کرتا ہے۔

جو بچ گیا وہ ایک بہت ہی مخصوص ترتیب تھی۔ یہ ایک انتہائی تیزی کی پوزیشننگ تھی جو کھولنا شروع کر رہی تھی جب کہ ڈبلیو ٹی آئی اب بھی اپنی 26 ہفتے کی موونگ ایوریج سے اوپر تھی۔ اس ورژن نے جانچے گئے نمونوں کے مقابلے خام سگنلز اور خریدو اور ہولڈ دونوں کو پیچھے چھوڑ دیا۔

بڑی بات یہ ہے کہ آپ FinancialModelingPrep's COT اور Commodity Price Data API کا استعمال کرتے ہوئے شروع سے ہی ہر چیز کو بنا سکتے ہیں بغیر متعدد ڈیٹا کے ذرائع کو ایک ساتھ جوڑے۔ اس سے خیالات کو حقیقی دنیا کے ٹیسٹوں میں تبدیل کرنا بہت آسان ہو جاتا ہے۔

یہ ہمیں مضمون کے آخری حصے تک لے جاتا ہے۔ مجھے امید ہے کہ آپ نے کچھ نیا اور مفید سیکھا ہوگا۔ آپ کے وقت کے لئے آپ کا شکریہ.

اوپر تک سکرول کریں۔