XGBoost for Return Direction: Class Imbalance and Decision Thresholds
درّب مصنفا للتنبؤ بما إذا كان عائد الساعة التالية يتجاوز +0.5%، وستكتشف أنك لم تبن مسألة ثنائية متوازنة. في فترات هدوء العملات المشفرة، تقع 70--80% من الأشرطة تحت العتبة، ولذلك يستطيع النموذج تسجيل دقة 75% من دون توقع أي نتيجة موجبة. الدقة عديمة الفائدة هنا. وكذلك حد القرار الافتراضي 0.5، الذي لم يختره أحد عمدا — فهو ببساطة القيمة التي تستخدمها predict().
هناك ثلاث استجابات معيارية: إعادة وزن الخسارة باستخدام scale_pos_weight، أو استبدال الخسارة بخسارة بؤرية، أو إبقاء الخسارة كما هي وتحريك عتبة القرار بعد التدريب. عادة ما تقدم هذه الخيارات كأنها متبادلة. لكنها ليست كذلك. فهي تنتج مقايضات مختلفة بين الدقة والاستدعاء، والأهم أنها تنتج صافي ربح وخسارة مختلفا بعد التكاليف، لأن التداول يهتم بالدقة أكثر بكثير من الاستدعاء — فالتداول الفائت لا يكلف شيئا، أما التداول السيئ فيكلف السبريد والرسوم.
يقيس هذا المقال الطرق الثلاث على الطيات نفسها للتحقق المتحرك إلى الأمام بعد التطهير، باستخدام بيانات BTC وETH الساعية، ثم يتناول نتيجتين تترتبان على ذلك: لماذا يمثل min_child_weight عتبة لهسيان وليس عددا للصفوف (ولهذا تتغير قيمته المناسبة عند إعادة وزن الخسارة)، وأين تختلف XGBoost وLightGBM وCatBoost فعليا من الناحية الهندسية.
| النهج | الدقة | الاستدعاء | الصفقات | صافي الربح بعد التكاليف | Sharpe منكمش |
|---|---|---|---|---|---|
| خط الأساس (بلا تصحيح، thr=0.5) | — | — | — | — | — |
scale_pos_weight = n_neg/n_pos |
— | — | — | — | — |
| الخسارة البؤرية (γ=2, α=0.25) | — | — | — | — | — |
| تحسين العتبة (الحد الأدنى للدقة 0.55) | — | — | — | — | — |
الطيات والميزات ونموذج التكاليف نفسها لكل صف. جرى تقليص Sharpe بسبب عدد الإعدادات المجربة، وفق Sharpe المنكمش والاختبارات المتعددة، بينما تتبع التكاليف نماذج الانزلاق والتكلفة.
معالجة اختلال الفئات في التنبؤ بالعوائد

النهج 1: scale_pos_weight
أبسط طريقة هي ضبط scale_pos_weight = n_negative / n_positive:
n_pos = y_train.sum()
n_neg = len(y_train) - n_pos
scale_pos_weight = n_neg / n_pos # e.g., 3.0 if 75% negative
model = xgb.XGBClassifier(
scale_pos_weight=scale_pos_weight,
...
)
يُكبّر هذا تدرج عينات الفئة الموجبة، ويخبر النموذج بأن تصنيف مثال موجب خطأ أسوأ مرات من تصنيف مثال سالب خطأ.
من السهل تفويت الأثر الجانبي: فهو يكبّر أيضا هسيانات الدالة. وبما أن min_child_weight عتبة على مجموع الهسيانات في الورقة (انظر أدناه)، فإن إعادة وزن الخسارة تغير بصمت مدى شدة انقسام الشجرة. فقيمة min_child_weight المضبوطة عند scale_pos_weight=1 لا تعني الشيء نفسه عند scale_pos_weight=3. أعد ضبط الاثنين معا، لا بالتتابع.
الأثر الجانبي الثاني أن احتمالات الخرج لا تعود معايرة. تعيد predict_proba قيمة رتيبة في الاحتمال الحقيقي لكنها لا تساويه، مما يجعل أي تحجيم لاحق للمراكز مبنيا على هذه الأرقام خاطئا.
النهج 2: الخسارة البؤرية
تخفض الخسارة البؤرية وزن الأمثلة السهلة بغض النظر عن الفئة، وتركز التدريب على العينات الصعبة والملتبسة. وهذا إطار جذاب للتنبؤ بالعوائد، حيث يكون الحد بين شريط +0.5% وشريط +0.4% ضجيجا في الغالب — مع أن "التركيز على الحالات الملتبسة" و"التركيز على الحالات غير القابلة للتعلم" هما التعليمات نفسها عندما تكون التسميات بهذا القدر من الضجيج، ولهذا يجب القياس لا الافتراض.
حيث إن هو الاحتمال المتوقع للفئة الحقيقية، و يوازن أوزان الفئات، و (عادة 1--3) يتحكم في شدة تخفيض وزن الأمثلة السهلة.
def focal_loss_objective(y_true, y_pred, gamma=2.0, alpha=0.25):
"""
Custom focal loss for XGBoost. Returns gradient and hessian.
"""
p = 1.0 / (1.0 + np.exp(-y_pred)) # sigmoid
g1 = alpha * y_true * (1 - p)**gamma * (gamma * p * np.log(p + 1e-9) + p - 1)
g2 = (1 - alpha) * (1 - y_true) * p**gamma * (
-gamma * (1 - p) * np.log(1 - p + 1e-9) - p
)
grad = -(g1 + g2)
hess = np.maximum(grad * (1 - grad), 1e-6)
return grad, hess
model = xgb.XGBClassifier(objective=focal_loss_objective, ...)
تحتاج الأهداف المخصصة إلى تدرج وهسيان موجب حتى تكون صيغة وزن الورقة خطوة هبوط. المشتق الثاني الدقيق للخسارة البؤرية ليس موجبا في كل المواضع، ولذلك تلجأ التطبيقات إلى بديل — لكن البديل الذي يختلف بعامل مقياس يغير أحجام الخطوات، ومن خلال مجموع الهسيان يغير ما الذي تقصه min_child_weight. وحده جدول المقارنة يبين ما إذا كان ذلك يكلف شيئا عمليا.
النهج 3: تحسين العتبة
اترك الخسارة كما هي، ودرب نموذجا معايرا، ثم حرك عتبة القرار على مجموعة التحقق:
from sklearn.metrics import precision_recall_curve
def optimize_threshold(y_true, y_proba, min_precision=0.55):
"""
Find the threshold maximizing F1 subject to a minimum precision.
"""
precisions, recalls, thresholds = precision_recall_curve(y_true, y_proba)
f1_scores = 2 * (precisions * recalls) / (precisions + recalls + 1e-9)
valid = precisions[:-1] >= min_precision
if not valid.any():
return 0.5 # fallback
best_idx = np.argmax(f1_scores[:-1] * valid)
return thresholds[best_idx]
أرضية الدقة هي ما يهم التداول. تعظيم F1 وحده يبادل الدقة بالاستدعاء، وهو ما يعني من ناحية صافي الربح مزيدا من الصفقات الهامشية التي تدفع السبريد كل مرة. إن تقييد الدقة وتعظيم الاستدعاء تحت هذا القيد يترجم إلى "تداول أقل، لكن مع الصواب عند التداول".
تحافظ قاعدتان على نزاهة ذلك: اضبط العتبة على بيانات تحقق تقع زمنيا بعد نافذة التدريب، وأعد ضبطها لكل طية. العتبة المختارة مرة واحدة على العينة كلها تسرب استباقي من نوع ما هو مصنف في تصنيف انحياز النظر إلى المستقبل. كما أن إعادة الضبط لكل طية تقدم تشخيصا مجانيا — فإذا قفزت العتبة المثلى بين الطيات، فالمعايرة غير مستقرة والعتبة تلائم الضجيج.
لماذا min_child_weight هو مجموع هسيان لا عدد صفوف

يبني التعزيز بالتدرج مجموعة إضافية. عند الخطوة يضيف شجرة تقلل هدفا منظما، وتقربه XGBoost بتوسع تايلور من الرتبة الثانية:
مع ، و، و
حيث إن هو عدد الأوراق، و وزن الورقة، و عقوبة كل ورقة، و حد L2. ويعطي حل وزن الورقة الأمثل .
يوجد مجموع الهسيان في المقام، وهذه هي النقطة. يضع min_child_weight عتبة على ذلك المجموع، لا على عدد الصفوف في الورقة. في الخسارة اللوغاريتمية، ، وهو أكبر ما يكون عند وينهار نحو الصفر كلما اقتربت التنبؤات من اليقين. لذلك تقص ورقة مليئة بأشرطة مصنفة بثقة بسبب مجموع هسيان صغير، بينما قد تبقى ورقة تحتوي على عدد قليل من الأشرطة الملتبسة فعليا.
بالنسبة إلى التسميات المالية المشوشة، هذا هو السلوك المطلوب، وهو يفسر النتائج العملية:
- رفع
min_child_weightيقص الأوراق القائمة على ملاحظات قليلة غير مؤكدة — وهي بالضبط الأكثر عرضة لمواءمة الضجيج. وهذا أدق من عد الصفوف على طريقةmin_child_samples. - أي شيء يعيد تحجيم الخسارة يعيد تحجيم الهسيانات. فـ
scale_pos_weightوالأهداف المخصصة وأوزان العينات كلها تغيرmin_child_weightالفعلي، رغم أن الرقم الذي كتبته لم يتغير. - مع استمرار التعزيز وازدياد حدة التنبؤات، تنكمش الهسيانات عالميا، ولذلك تقص قيمة
min_child_weightالثابتة بقوة أكبر في الجولات اللاحقة. وهذا أثر تلدين مدمج، وهو سبب اختلاف معدلات التعلم المنخفضة مع عدد أكبر من الأشجار عن خطوات أقل وأكبر.
إن min_child_samples في LightGBM هو عدد صفوف، أي معلمة مختلفة فعلا رغم تشابه الاسم. ونظيره القائم على الهسيان هو min_sum_hessian_in_leaf. إن نقل الإعداد بين المكتبتين بمطابقة الأسماء طريقة شائعة لتغيير النموذج خطأ.
XGBoost مقابل LightGBM مقابل CatBoost: الفروق الهندسية

تنفذ المكتبات الثلاث أشجار قرار معززة بالتدرج. الاختلافات في بناء الشجرة، وهي التي تظهر فعليا في زمن التدريب ونتيجة خارج العينة.
XGBoost ينمو مستوى بمستوى (من الاتساع إلى العمق): تقسم جميع الأوراق عند عمق معين قبل الانتقال إلى عمق أكبر. ينتج هذا أشجارا متوازنة، ويجعل max_depth أداة ذات معنى للتحكم في التعقيد، ويجعل الضبط أكثر قابلية للتنبؤ. لكنه يهدر العمل أيضا في تقسيم أوراق لم يعد فيها الكثير من الخسارة القابلة للتقليل.
LightGBM ينمو ورقة بورقة: يقسم الورقة التي تقدم أكبر خفض للخسارة، أيا كان موضعها. تصل انقسامات أقل إلى خسارة التدريب نفسها، لكن الأشجار تصبح عميقة وغير متوازنة، لذلك يتوقف max_depth عن كونه أداة التحكم الصحيحة — وتصبح num_leaves هي الأداة. وهناك حيلتان إضافيتان تقودان سرعته:
- GOSS (أخذ عينات أحادي الجانب قائم على التدرج) يحتفظ بكل الحالات ذات التدرج الكبير، ويأخذ عينة عشوائية من الحالات ذات التدرج الصغير، مع زيادة أوزان الناجين لإبقاء تقدير التدرج غير متحيز. في البيانات المالية، الحالات ذات التدرج الكبير هي الأشرطة التي يخطئ فيها النموذج حاليا — وهي أيضا حيث يوجد ضجيج التسميات، لذلك يركز GOSS أخذ العينات حيث يكون الضجيج أسوأ. يجدر فحصه مقابل أخذ العينات العادي بدلا من افتراض تفوقه.
- EFB (تجميع الميزات الحصرية) يحزم الميزات المتناثرة والمتنافية في ميزة واحدة ضمن فضاء الصناديق. وهو فعال في ترميزات one-hot، وعديم الفائدة تقريبا في الميزات المستمرة الكثيفة، وهي معظم مصفوفة الميزات الفنية.
CatBoost ينمو أشجارا متماثلة (غافلة): يستخدم كل عقدة عند عمق معين شرط الانقسام نفسه. وهذا منظم قوي ويجعل الاستدلال سريعا جدا — إذ تصبح الشجرة بحثا في فهرس — على حساب القدرة التعبيرية لكل شجرة. وآليتاه المميزتان هما:
- التعزيز المرتب. يحسب التعزيز العادي البواقي لعينة باستخدام نموذج درب على تلك العينة، فيحرف الباقي — وهو "تحول التنبؤ". يقدر CatBoost البواقي لكل عينة باستخدام نموذج ملائم فقط للعينات السابقة لها في تبديل عشوائي. والبنية مناسبة طبيعيا للتفكير في السلاسل الزمنية، وتهم أكثر عندما تكون البيانات محدودة.
- إحصاءات الهدف المرتبة للترميز الفئوي، إذ تحسب إحصاءات الهدف من العينات السابقة فقط، وتتجنب تسرب الهدف الذي يسببه ترميز المتوسط الساذج. وهذا هو السبب الصادق لاستخدام CatBoost عندما تحمل مجموعة الميزات تسميات للمنصة أو فئة الأصل أو النظام.
| الخاصية | XGBoost | LightGBM | CatBoost |
|---|---|---|---|
| نمو الشجرة | مستوى بمستوى | ورقة بورقة | متماثل (غافل) |
| أداة التعقيد | max_depth |
num_leaves |
depth |
| حارس حجم الورقة | min_child_weight (هسيان) |
min_child_samples (صفوف) |
min_data_in_leaf (صفوف) |
| الميزات الفئوية | ترميز يدوي | دعم أساسي | أصلي، TS مرتب |
| التنظيم | L1/L2 + gamma | L1/L2 + num_leaves | L2 + قوة عشوائية |
| الخسارة المخصصة | مرنة | مرنة | محدودة نسبيا |
| زمن التدريب، هذه البيانات | — | — | |
| خسارة OOS، الطيات نفسها | — | — |
الأسطر الوصفية حقائق عن المكتبات. أما السطران الأخيران فهما الوحيدان اللذان يجيبان عن سؤال "أي مكتبة أستخدم؟"، ولا بد أن يأتي الجواب من تشغيل على بياناتك — فعادة ما تكون الفروق بين التطبيقات المضبوطة جيدا صغيرة بما يكفي لأن شكل مجموعة البيانات هو الذي يحسم.
تبديل المكتبات في الغالب مجرد إعادة تسمية. دالة تدريب واحدة، مع إدراج المعلمات المختلفة بدلا من تكرارها:
import xgboost as xgb
def train_xgb_model(X_train, y_train, X_val, y_val, class_weight_ratio=1.0):
"""Train XGBoost classifier for return direction prediction."""
model = xgb.XGBClassifier(
n_estimators=2000,
max_depth=5,
learning_rate=0.01,
subsample=0.7,
colsample_bytree=0.7,
min_child_weight=10, # Hessian sum, not row count
gamma=1.0,
reg_alpha=0.1,
reg_lambda=1.0,
scale_pos_weight=class_weight_ratio,
objective='binary:logistic',
eval_metric='logloss',
tree_method='hist',
random_state=42,
early_stopping_rounds=50,
)
model.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)
return model
| المفهوم | XGBoost | LightGBM | CatBoost |
|---|---|---|---|
| عدد الأشجار | n_estimators |
n_estimators |
iterations |
| عقوبة L2 | reg_lambda |
reg_lambda |
l2_leaf_reg |
| أخذ عينات الأعمدة | colsample_bytree |
colsample_bytree |
rsm |
| اختلال الفئات | scale_pos_weight |
scale_pos_weight |
auto_class_weights='Balanced' |
| الإيقاف المبكر | early_stopping_rounds |
رد نداء lgb.early_stopping() |
early_stopping_rounds |
أهمية SHAP عبر الطيات بوصفها كاشفا لتلاشي ألفا

يغطي المدونة بالفعل SHAP على نموذج تعزيز بالتدرج — TreeExplainer ومخططات الملخص وكيفية قراءتها. ما لم يُغطَّ هو استخدام SHAP طوليا: مفسر واحد لكل طية من طيات المشي إلى الأمام، مع تتبع متوسط الإسناد المطلق لكل ميزة عبر الزمن.
def shap_over_time(models, test_sets, feature_names) -> pd.DataFrame:
"""
Track SHAP-based feature importance across walk-forward folds.
Rows are folds, columns are features.
"""
importance_over_time = []
for fold_idx, (model, X_test) in enumerate(zip(models, test_sets)):
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)
mean_abs_shap = np.abs(shap_values).mean(axis=0)
importance_over_time.append(
pd.Series(mean_abs_shap, index=feature_names, name=fold_idx)
)
return pd.DataFrame(importance_over_time)
الخرج مصفوفة طيات × ميزات، وتظهر فيها ثلاثة أشكال مميزة:
- انخفاض رتيب — ميزة تتلاشى ميزتها. مرشحة للحذف أو للتحقيق في ما تغير في بنية السوق.
- تباين مرتفع بلا اتجاه — ميزة ضجيج يتعلق بها النموذج في بعض الأنظمة. وهذه الإشارة نفسها التي يقيسها تحليل الهضبة للمعلمات، مطبقة هنا على الميزات.
- تغير مرحلي بسبب النظام — تهبط الأهمية عند طية محددة وتبقى منخفضة. وغالبا ما يمكن ربط ذلك بحدث في منصة أو إدراج أو هيكل رسوم، لا بتلاشي ألفا.
الفخ أن SHAP المطلق المتوسط لا يقارن بين الطيات عندما تتغير ثقة النموذج العامة: فالنموذج الذي يتنبأ بقيم أقرب إلى 0.5 في كل مكان ينتج إسنادات أصغر لكل ميزة دفعة واحدة. طبّع أهمية كل طية بحيث يكون مجموعها 1 قبل المقارنة، حتى تقرأ تحولات الأهمية النسبية لا تحولات الثقة.
لماذا الأشجار، باختصار

اختبر Grinsztajn وOyallon وVaroquaux (NeurIPS 2022) مجموعات الأشجار مقابل التعلم العميق عبر 45 مجموعة بيانات جدولية، وعزلوا ثلاث خصائص بنيوية تفضل الأشجار — وهي تصف البيانات المالية كلها:
- دوال هدف غير منتظمة. العوائد ليست ملساء؛ فيها انقطاعات وتغيرات أنظمة وتأثيرات عتبة. تلتقط الانقسامات المحاذية للمحاور هذه الخصائص من دون تقريب سطح أملس.
- ميزات غير مفيدة. يولد خط أنابيب ألفا مئات المرشحين ومعظمها ضجيج. تختار الأشجار في كل انقسام؛ بينما توزع الشبكات العصبية سعتها على كل المدخلات وتنفق المعلمات على الضجيج.
- عدم الثبات الدوراني. الحجم ليس قابلا للاستبدال بالتقلب. الشبكات العصبية ثابتة دورانيا افتراضيا، فتعامل التركيبات الخطية للميزات كأنها مكافئة للأصول — وهذا خطأ ببساطة للميزات ذات المعنى الدلالي المختلف.
بالنسبة إلى الجانب العملي لهذه المفاضلة — حجم البيانات، وزمن الاستجابة، وجهد هندسة الميزات، وقابلية التفسير، والتكيف مع الأنظمة — يملك المدونة بالفعل جدول قرار كاملا في نمذجة السبريد بالتعلم الآلي.
هندسة الميزات

import pandas as pd
import numpy as np
def build_features(df: pd.DataFrame) -> pd.DataFrame:
"""
Build trading features from OHLCV data.
Expects columns: open, high, low, close, volume, timestamp
"""
feat = pd.DataFrame(index=df.index)
feat['return_1'] = df['close'].pct_change(1)
feat['return_5'] = df['close'].pct_change(5)
feat['return_15'] = df['close'].pct_change(15)
feat['return_60'] = df['close'].pct_change(60)
log_ret = np.log(df['close'] / df['close'].shift(1))
feat['volatility_20'] = log_ret.rolling(20).std()
feat['volatility_60'] = log_ret.rolling(60).std()
feat['vol_ratio'] = feat['volatility_20'] / feat['volatility_60']
feat['parkinson_vol'] = np.sqrt(
(1 / (4 * np.log(2)))
* (np.log(df['high'] / df['low']) ** 2).rolling(20).mean()
)
feat['volume_sma_ratio'] = df['volume'] / df['volume'].rolling(20).mean()
feat['volume_std_20'] = df['volume'].rolling(20).std()
feat['obv'] = (np.sign(df['close'].diff()) * df['volume']).cumsum()
feat['obv_slope'] = feat['obv'].diff(5) / feat['obv'].shift(5)
feat['high_low_range'] = (df['high'] - df['low']) / df['close']
feat['close_position'] = (df['close'] - df['low']) / (df['high'] - df['low'])
feat['gap'] = df['open'] / df['close'].shift(1) - 1
delta = df['close'].diff()
gain = delta.clip(lower=0).rolling(14).mean()
loss = (-delta.clip(upper=0)).rolling(14).mean()
feat['rsi_14'] = 100 - 100 / (1 + gain / loss)
ema_12 = df['close'].ewm(span=12).mean()
ema_26 = df['close'].ewm(span=26).mean()
feat['macd'] = (ema_12 - ema_26) / df['close']
feat['macd_signal'] = feat['macd'].ewm(span=9).mean()
feat['macd_hist'] = feat['macd'] - feat['macd_signal']
for window in [10, 20, 50]:
sma = df['close'].rolling(window).mean()
feat[f'distance_sma_{window}'] = (df['close'] - sma) / sma
std = df['close'].rolling(window).std()
feat[f'bb_position_{window}'] = (df['close'] - sma) / (2 * std)
return feat
كل ميزة أعلاه سببية بحكم بنائها — عمليات متحركة وموسعة فقط، بلا إحصاءات على العينة كلها. وهذا مقصود: توحيد القياس على السلسلة كلها هو التسرب الأكثر شيوعا في هذا النوع من خطوط الأنابيب، وتأثيره في Sharpe المبلغ عنه مقاس في تصنيف انحياز النظر إلى المستقبل.
نطاقات المعلمات الفائقة لـ XGBoost على البيانات المالية

الهدف ليس أقصى أداء داخل العينة، بل أقصى استقرار خارج العينة. التنظيم أولا، والتعقيد ثانيا:
| المعلمة | النطاق المعتاد | الغرض |
|---|---|---|
max_depth |
3--7 | يحد ترتيب التفاعلات. الأشجار الأعمق تنمذج تفاعلات أعلى رتبة لكنها تفرط في المواءمة أسرع. ابدأ بـ 4. |
min_child_weight |
5--100 | الحد الأدنى لمجموع الهسيان في الورقة. أعد ضبطه كلما غيرت scale_pos_weight أو الهدف. |
learning_rate |
0.005--0.05 | الانكماش. القيم الأقل تحتاج إلى أشجار أكثر لكنها تعمم أفضل. |
subsample |
0.5--0.8 | أخذ عينات من الصفوف لكل شجرة. يضيف عشوائية ويقلل فرط المواءمة. |
colsample_bytree |
0.5--0.8 | أخذ عينات من الأعمدة لكل شجرة. مهم مع كثرة الميزات المترابطة. |
gamma |
0.5--5.0 | الحد الأدنى لخفض الخسارة من أجل انقسام. يعمل كعتبة تقليم. |
reg_alpha (L1) |
0.01--1.0 | L1 على أوزان الأوراق. يشجع التناثر. |
reg_lambda (L2) |
0.1--10.0 | L2 على أوزان الأوراق. يمنع قيم الأوراق الكبيرة. |
بالنسبة إلى إجراء البحث نفسه — TPE، واستمرار الدراسة، ولماذا يتفوق البحث البايزي على الانحدار الإحداثي — انظر Optuna مقابل الانحدار الإحداثي. ويجب نقل عدد التجارب الذي تستخدمه هناك إلى تقليص Sharpe بعد ذلك.
ما يتركه هذا المقال عمدا للمقالات الأخرى

البنية المحيطة بنموذج تعزيز التدرج مغطاة هنا بالفعل مع القياسات:
- تقسيمات التحقق. يشرح التحسين المتحرك إلى الأمام النوافذ المثبتة مقابل المتحركة ومعدل التدهور؛ بينما يقدم
purged_walk_forwardفي نمذجة السبريد بالتعلم الآلي مقسما بفجوة تطهير وحظر حقيقية. لا تنشر واحدا بلا تطهير — فالأهداف المتداخلة في النوافذ المستقبلية تتسرب عبر حدود الطيات. - ضوابط فرط المواءمة. فجوة التدريب/الاختبار واستقرار الميزات بين الطيات، كما جرى قياسهما في تحليل الهضبة واحتمال فرط مواءمة الاختبار الخلفي.
- من التنبؤات إلى صافي الربح والخسارة. اختبار خلفي متجه
signal * shifted-returnمع تكاليف ثابتة بنقاط الأساس يصنع Sharpe من حفنة صفقات محظوظة — انظر تصميم دالة الهدف وصافي الربح لكل وقت نشط. قلّص أي Sharpe بعد البحث بحسب عدد التجارب: Sharpe المنكمش، والسلبية الصادقة لمعرفة شكل الأمر عندما لا تكون الميزة حقيقية. - تكاليف المعاملات. نماذج تكلفة الانزلاق ورسوم الصانع والآخذ والخصومات. نموذج اتجاه بدقة 53% يعيش أو يموت بحسب نموذج التكلفة الذي تختاره.
تُذكر ثلاثة مخاطر إضافية هنا بوصفها فرضيات لا نتائج: انحياز البقاء الناتج عن التدريب على الأصول المدرجة فقط؛ وعدم استقرارية أهداف العوائد الخام، حيث قد تتصرف العوائد المتبقية من بيتا على نحو أفضل؛ ودورية إعادة التدريب، حيث يكون تشخيص SHAP عبر الطيات مراقب الانجراف الطبيعي.
الخلاصة

تصنيف اتجاه العوائد مسألة مختلة الفئات، مع عتبة قرار لم يخترها أحد، والإصلاحات الثلاثة المعيارية ليست متبادلة. إن scale_pos_weight سطر واحد، لكنه يزيل معايرة الاحتمالات ويحرك min_child_weight بصمت. والخسارة البؤرية تحتاج إلى هسيان تقربه معظم التطبيقات. أما تحسين العتبة فيترك النموذج كما هو ويضع القيد المهم للتداول — الدقة — في موضعه الصحيح، لكن فقط إذا أعيد ضبط العتبة لكل طية على بيانات تأتي بعد التدريب.
أيها يفوز سؤال تجريبي عن بياناتك وعتبتك ونموذج تكلفتك. الجدول في الأعلى هو الإجابة لمجموعة بيانات واحدة؛ وتشغيله على بياناتك يكلف أقل من وقت الضبط الذي تنفقه في التخمين.
قراءات إضافية:
- Grinsztajn وآخرون، "Why do tree-based models still outperform deep learning on typical tabular data?" (NeurIPS 2022)
- Wang وآخرون، "Imbalance-XGBoost: leveraging weighted and focal losses for binary label-imbalanced classification"
- Chen وGuestrin، "XGBoost: A Scalable Tree Boosting System" (KDD 2016)
- Ke وآخرون، "LightGBM: A Highly Efficient Gradient Boosting Decision Tree" (NeurIPS 2017)
- Prokhorenkova وآخرون، "CatBoost: unbiased boosting with categorical features" (NeurIPS 2018)
- توثيق XGBoost: Notes on Parameter Tuning
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.