XGBoost for Return Direction: Class Imbalance and Decision Thresholds
Bir sonraki saatin getirisinin +0.5%'i aşıp aşmadığını tahmin eden bir sınıflandırıcı eğittiğinizde dengeli bir ikili problem kurmuş olmazsınız. Sakin kripto rejimlerinde barların %70--80'i eşik altında kalır; model tek bir pozitif tahmin etmeden %75 doğruluk elde edebilir. Burada doğruluk işe yaramaz. Kimsenin bilinçli olarak seçmediği varsayılan 0.5 karar sınırı da öyle — bu yalnızca predict() fonksiyonunun kullandığı değerdir.
Üç standart yaklaşım vardır: loss'u scale_pos_weight ile yeniden ağırlıklandırmak, loss'u focal loss ile değiştirmek veya loss'u olduğu gibi bırakıp karar eşiğini sonradan taşımak. Genellikle birbirinin yerine geçebilir çözümler gibi sunulurlar. Değildirler. Farklı precision/recall trade-off'ları ve daha önemlisi farklı maliyet sonrası PnL üretirler; çünkü trading recall'dan çok precision'a önem verir — kaçırılan işlem hiçbir şeye mal olmaz, kötü işlem spread artı komisyon maliyeti yaratır.
Bu makale üçünü de saatlik BTC ve ETH verilerinin aynı purged walk-forward fold'larında ölçer, ardından bundan çıkan iki konuyu ele alır: min_child_weight neden satır sayısı değil de Hessian eşiğidir (bu yüzden loss'u yeniden ağırlıklandırdığınızda "doğru" değer değişir) ve XGBoost, LightGBM ile CatBoost'un gerçekte nerelerde ayrıştığı.
| Yaklaşım | Precision | Recall | İşlemler | Maliyet sonrası PnL | Deflated Sharpe |
|---|---|---|---|---|---|
| Baseline (düzeltme yok, thr=0.5) | — | — | — | — | — |
scale_pos_weight = n_neg/n_pos |
— | — | — | — | — |
| Focal loss (γ=2, α=0.25) | — | — | — | — | — |
| Eşik optimizasyonu (minimum precision 0.55) | — | — | — | — | — |
Aynı fold'lar, aynı özellikler ve her satır için aynı maliyet modeli. Sharpe, denenen konfigürasyon sayısına göre deflated Sharpe ve multiple testing uyarınca deflate edilmiştir; maliyetler slippage ve maliyet modellerini izler.
Getiri Tahmininde Sınıf Dengesizliğini Ele Alma

Yaklaşım 1: scale_pos_weight
En basit yöntem. scale_pos_weight = n_negative / n_positive olarak ayarlayın:
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,
...
)
Bu, pozitif sınıf örneklerinin gradient'ini ölçekler ve modele pozitif bir örneği yanlış sınıflandırmanın negatif bir örneği yanlış sınıflandırmaktan kat daha kötü olduğunu söyler.
Gözden kaçması kolay yan etki şudur: Hessian'ları da ölçekler. min_child_weight bir yapraktaki Hessian toplamı üzerinde eşik olduğundan (aşağıya bakın), loss'u yeniden ağırlıklandırmak ağacın ne kadar agresif bölüneceğini sessizce değiştirir. scale_pos_weight=1 için ayarlanmış bir min_child_weight, scale_pos_weight=3 için aynı anlama gelmez. İkisini ardışık değil birlikte yeniden ayarlayın.
İkinci yan etki: çıktı olasılıkları artık kalibre değildir. predict_proba, gerçek olasılıkla monoton ilişkili ancak ona eşit olmayan bir değer döndürür; bu sayılara dayanan downstream pozisyon boyutlandırmasını hatalı kılar.
Yaklaşım 2: Focal Loss
Focal loss, sınıftan bağımsız olarak kolay örneklerin ağırlığını azaltır ve eğitimi zor, belirsiz örneklere yoğunlaştırır. Bu, +0.5% bar ile +0.4% bar arasındaki sınırın çoğunlukla gürültü olduğu getiri tahmini için çekici bir çerçevedir — ancak etiketler bu kadar gürültülüyken "belirsiz vakalara odaklan" ile "öğrenilemeyen vakalara odaklan" aynı talimattır; bu nedenle varsaymak yerine ölçmek gerekir.
burada gerçek sınıf için tahmin edilen olasılık, sınıf ağırlıklarını dengeleyen değer ve (genellikle 1--3) kolay örneklerin ağırlığının ne kadar azaltılacağını kontrol eder.
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, ...)
Özel objective'ler, leaf-weight formülünün descent adımı olması için gradient ve pozitif Hessian gerektirir. Focal loss'un exact ikinci türevi her yerde pozitif değildir; bu nedenle implementasyonlar surrogate kullanır — ancak ölçek faktörü hatalı bir surrogate adım boyutlarını ve Hessian toplamı üzerinden min_child_weight'in neyi budadığını değiştirir. Bunun pratikte maliyet yaratıp yaratmadığını yalnızca karşılaştırma tablosu gösterir.
Yaklaşım 3: Eşik Optimizasyonu
Loss'u olduğu gibi bırakın, kalibre bir model eğitin ve karar eşiğini validation setinde taşıyın:
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]
Trading için önemli olan precision tabanıdır. Yalnızca F1'i maksimize etmek precision'ı recall karşılığında takas eder; bu da PnL açısından her biri spread ödeyen daha fazla marjinal işlem demektir. Precision'ı kısıtlayıp altında recall'u maksimize etmek "daha az trade et ama ettiğinde doğru ol" yaklaşımına karşılık gelir.
İki kural bunu dürüst tutar: eşiği kronolojik olarak eğitim window'undan sonra gelen validation verisine fit edin ve her fold için yeniden fit edin. Tüm örneklem üzerinde bir kez seçilen eşik, look-ahead bias taxonomy içinde kataloglanan türden bir look-ahead leakage'dir. Fold başına yeniden fit etmek ayrıca ücretsiz bir diagnostik sağlar — optimal eşik fold'lar arasında çok değişiyorsa calibration kararsızdır ve eşik gürültüye fit oluyordur.
min_child_weight Neden Satır Sayısı Değil Hessian Toplamıdır?

Gradient boosting toplamsal bir ensemble kurar. adımında, XGBoost'un ikinci dereceden Taylor açılımıyla yaklaştırdığı regularized objective'i minimize eden ağacını ekler:
burada , ve
yaprak sayısı, yaprak ağırlığı, yaprak başına ceza ve L2 terimidir. Optimal yaprak ağırlığı için çözüm verir.
Hessian toplamı paydada yer alır; asıl nokta budur. min_child_weight, yapraktaki satır sayısını değil bu toplamı eşikler. Log loss için , değerinde en büyük ve tahminler kesinliğe yaklaştıkça sıfıra yaklaşır. Bu nedenle kendinden emin biçimde sınıflandırılmış barlarla dolu bir yaprağın Hessian toplamı küçüktür ve budanır; gerçekten belirsiz birkaç bar içeren bir yaprak ise hayatta kalabilir.
Gürültülü finansal etiketler için istediğiniz davranış budur ve pratik sonuçları açıklar:
min_child_weightdeğerini yükseltmek, az sayıda belirsiz gözleme dayanan yaprakları budar — bunlar gürültüye fit olma ihtimali en yüksek olanlardır. Bu,min_child_samplestarzı satır sayımından daha hassas bir araçtır.- Loss'u yeniden ölçekleyen her şey Hessian'ları da yeniden ölçekler.
scale_pos_weight, özel objective'ler ve sample weight'ler, yazdığınız sayı değişmese bile effectivemin_child_weightdeğerini kaydırır. - Boosting ilerledikçe ve tahminler keskinleştikçe Hessian'lar genel olarak küçülür; bu nedenle sabit
min_child_weightsonraki turlarda daha agresif budama yapar. Bu yerleşik bir annealing etkisidir ve daha fazla ağaçla daha düşük learning rate'in neden daha az sayıda büyük adımdan farklı davrandığını açıklar.
LightGBM'nin min_child_samples değeri satır sayısıdır; yani benzer ada sahip gerçekten farklı bir parametredir. Hessian karşılığı min_sum_hessian_in_leaf'dir. İki kütüphane arasında yapılandırmayı yalnızca isimleri eşleştirerek taşımak, modeli yanlışlıkla değiştirmenin yaygın bir yoludur.
XGBoost ve LightGBM ve CatBoost: Mühendislik Farkları

Üçü de gradient boosted decision tree uygular. Farklar ağaç yapısındadır ve eğitim süresi ile out-of-sample skorda gerçekten görünen de budur.
XGBoost level-wise (breadth-first) büyür: belirli bir derinlikteki tüm yapraklar daha derine inilmeden bölünür. Bu dengeli ağaçlar üretir, max_depth'i anlamlı bir karmaşıklık kontrolü yapar ve ayarlanması daha öngörülebilirdir. Ayrıca azaltılacak loss'u az kalan yaprakları bölmek için emek harcar.
LightGBM leaf-wise büyür: yaprağın konumundan bağımsız olarak en büyük loss azalmasını sunan yaprağı böler. Aynı training loss'a daha az bölünmeyle ulaşır, ancak ağaçlar derin ve dengesiz hale gelir; bu nedenle doğru kontrol düğmesi max_depth değil num_leaves olur. Hızını iki ek teknik artırır:
- GOSS (Gradient-based One-Side Sampling) tüm büyük-gradient örneklerini korur, küçük-gradient olanları rastgele alt örnekler ve gradient tahminini yansız tutmak için kalanları yeniden ağırlıklandırır. Finansal veride büyük-gradient örnekleri modelin şu anda yanlış yaptığı barlardır — etiket gürültüsü de burada bulunduğu için GOSS örneklemeyi gürültünün en kötü olduğu yere yoğunlaştırır. Varsaymak yerine düz subsampling ile karşılaştırmaya değer.
- EFB (Exclusive Feature Bundling) birbirini dışlayan seyrek özellikleri tek bir bin-space özelliğinde paketler. One-hot encoding'lerde etkilidir; teknik özellik matrisinin çoğunu oluşturan yoğun sürekli özelliklerde neredeyse işe yaramaz.
CatBoost symmetric (oblivious) ağaçlar büyütür: belirli bir derinlikteki her düğüm aynı split koşulunu kullanır. Bu güçlü bir regularizer'dır ve inference'ı çok hızlandırır — ağaç bir index lookup'a dönüşür — ancak ağaç başına ifade gücü azalır. İki ayırt edici mekanizması vardır:
- Ordered boosting. Standart boosting, bir örnek için residual'ları o örnek üzerinde eğitilmiş bir modelle hesaplar ve residual'ı yanlılaştırır — "prediction shift." CatBoost her örnek için residual'ları rastgele permütasyonda yalnızca ondan önceki örneklerle fit edilmiş bir model kullanarak tahmin eder. Bu yapı time-series düşüncesine doğal biçimde uyar ve veri sınırlı olduğunda en çok önem taşır.
- Kategorik encoding için ordered target statistics, target istatistiklerini yalnızca önceki örneklerden hesaplar ve naive mean-encoding'in oluşturduğu target leakage'i önler. Feature set exchange, asset tier veya regime etiketleri taşıdığında CatBoost kullanmanın dürüst nedeni budur.
| Özellik | XGBoost | LightGBM | CatBoost |
|---|---|---|---|
| Ağaç büyümesi | Level-wise | Leaf-wise | Symmetric (oblivious) |
| Karmaşıklık düğmesi | max_depth |
num_leaves |
depth |
| Yaprak boyutu koruması | min_child_weight (Hessian) |
min_child_samples (satırlar) |
min_data_in_leaf (satırlar) |
| Kategorik özellikler | Manuel encoding | Temel destek | Native, ordered TS |
| Regularization | L1/L2 + gamma | L1/L2 + num_leaves | L2 + random strength |
| Özel loss | Esnek | Esnek | Biraz sınırlı |
| Eğitim süresi, bu veri kümesi | — | — | |
| OOS log loss, aynı fold'lar | — | — |
Niteliksel satırlar kütüphane gerçekleridir. Son iki satır "hangisini kullanmalıyım" sorusunu yanıtlayan tek satırlardır ve kendi veriniz üzerinde yapılan bir çalışmadan gelmelidir — iyi ayarlanmış implementasyonlar arasındaki farklar genellikle veri kümesinin şeklinin belirleyici olacağı kadar küçüktür.
Kütüphane değiştirmek çoğunlukla yeniden adlandırmadır. Farklı parametreleri üç kez kopyalamak yerine listeleyen tek bir training function:
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
| Kavram | XGBoost | LightGBM | CatBoost |
|---|---|---|---|
| Ağaç sayısı | n_estimators |
n_estimators |
iterations |
| L2 cezası | reg_lambda |
reg_lambda |
l2_leaf_reg |
| Sütun örnekleme | colsample_bytree |
colsample_bytree |
rsm |
| Sınıf dengesizliği | scale_pos_weight |
scale_pos_weight |
auto_class_weights='Balanced' |
| Early stopping | early_stopping_rounds |
lgb.early_stopping() callback |
early_stopping_rounds |
Alpha Decay Dedektörü Olarak Fold'lar Arasında SHAP Önemi

Blog, gradient-boosting modeli üzerinde SHAP konusunu — TreeExplainer, summary plot'lar ve bunların nasıl okunacağını — zaten ele alıyor. Ele alınmayan konu SHAP'i longitudinal kullanmak: her walk-forward fold için bir explainer ve her özelliğin zaman içindeki ortalama mutlak attribution'ını izlemek.
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)
Çıktı fold × feature matrisidir ve içinde ayırt edilebilen üç şekil vardır:
- Monoton düşüş — özelliğin edge'i zayıflıyor. Kaldırılmaya veya piyasa yapısında neyin değiştiğini araştırmaya adaydır.
- Yüksek varyans, trend yok — özellik, modelin bazı rejimlerde yakalandığı gürültüdür. Bu, plateau analysis'in parametreler için ölçtüğü sinyalin özelliklere uygulanmış halidir.
- Regime değişimli step change — importance belirli bir fold'da düşer ve düşük kalır. Genellikle alpha decay'den değil exchange, listing veya fee-structure olayından kaynaklanır.
Tuzak şudur: modelin genel confidence'ı değiştiğinde mean absolute SHAP fold'lar arasında karşılaştırılamaz; her yerde 0.5'e yakın tahmin yapan model tüm özellikler için aynı anda daha küçük attribution'lar üretir. Karşılaştırmadan önce her fold'un importance değerlerini toplamı 1 olacak şekilde normalize edin; böylece confidence değişimlerini değil relative importance değişimlerini okuyabilirsiniz.
Ağaçlar Neden? Kısaca

Grinsztajn, Oyallon ve Varoquaux (NeurIPS 2022), 45 tabular veri kümesinde tree ensemble'ları deep learning'e karşı test etti ve ağaçları destekleyen üç yapısal özellik belirledi — üçünün de finansal veride karşılığı vardır:
- Düzensiz target fonksiyonları. Getiriler smooth değildir; süreksizlikleri, regime değişimleri ve eşik etkileri vardır. Axis-aligned split'ler smooth yüzey yaklaştırmadan bunları yakalar.
- Bilgi taşımayan özellikler. Bir alpha pipeline yüzlerce aday üretir ve çoğu gürültüdür. Ağaçlar her split'te seçim yapar; sinir ağları kapasiteyi tüm girdilere yayarak parametreleri gürültüye harcar.
- Dönel değişmezliğin olmaması. Volume volatiliteyle birbirinin yerine kullanılamaz. Sinir ağları varsayılan olarak rotationally invariant'tır ve özelliklerin linear combination'larını orijinallerle eşdeğer görür — farklı anlamsal anlamları olan özellikler için bu açıkça yanlıştır.
Bu trade-off'un pratik tarafı — veri boyutu, latency, feature-engineering eforu, yorumlanabilirlik ve regime adaptation — için blogda makine öğrenmesiyle spread modeling içinde tam bir karar tablosu zaten var.
Özellik Mühendisliği

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
Yukarıdaki her özellik yapısı gereği causaldır — yalnızca rolling ve expanding işlemleri kullanılır, whole-sample istatistiği yoktur. Bu bilinçlidir: whole-series z-scoring bu tür pipeline'larda en yaygın leak'tir ve raporlanan Sharpe üzerindeki etkisi look-ahead bias taxonomy içinde ölçülmüştür.
Finansal Veride XGBoost için Hyperparameter Aralıkları

Amaç maksimum in-sample performans değil, maksimum out-of-sample istikrarıdır. Önce regularization, sonra karmaşıklık:
| Parametre | Tipik aralık | Amaç |
|---|---|---|
max_depth |
3--7 | Etkileşim derecesini sınırlar. Derin ağaçlar daha yüksek dereceli etkileşimleri modeller ancak daha hızlı overfit olur. 4'ten başlayın. |
min_child_weight |
5--100 | Bir yapraktaki minimum Hessian toplamı. scale_pos_weight veya objective'i her değiştirdiğinizde yeniden ayarlayın. |
learning_rate |
0.005--0.05 | Shrinkage. Düşük değerler daha fazla ağaç gerektirir ancak daha iyi geneller. |
subsample |
0.5--0.8 | Ağaç başına satır örnekleme. Rastgelelik ekler, overfitting'i azaltır. |
colsample_bytree |
0.5--0.8 | Ağaç başına sütun örnekleme. Çok sayıda korelasyonlu özellikte kritiktir. |
gamma |
0.5--5.0 | Split için minimum loss azalması. Budama eşiği olarak çalışır. |
reg_alpha (L1) |
0.01--1.0 | Yaprak ağırlıkları üzerinde L1. Sparsity'yi teşvik eder. |
reg_lambda (L2) |
0.1--10.0 | Yaprak ağırlıkları üzerinde L2. Büyük yaprak değerlerini önler. |
Arama prosedürünün kendisi — TPE, study persistence ve Bayesian search'ün coordinate descent'i neden geçtiği — için Optuna ve coordinate descent makalesine bakın. Orada kullandığınız trial count daha sonra Sharpe deflation hesabına taşınmalıdır.
Bu Makalenin Bilinçli Olarak Diğer Yazılara Bıraktıkları

Gradient boosting modeli etrafındaki altyapı, ölçümleriyle birlikte burada zaten ele alınıyor:
- Validation split'leri. Anchored ve rolling window'lar ile degradation rate için walk-forward optimization; gerçek purge/embargo gap'li splitter için makine öğrenmesiyle spread modeling içindeki
purged_walk_forward. Purge olmadan yayınlamayın — örtüşen forward-window hedefleri fold sınırları arasına sızar. - Overfitting kontrolleri. Plateau analysis ve backtest overfitting olasılığı içinde ölçülen train/test gap ve cross-fold feature stability.
- Tahminlerden PnL'ye. Sabit bps maliyetli vectorized
signal * shifted-returnbacktest'i bir avuç şanslı işlemden Sharpe üretir — objective function design ve aktif zaman başına PnL makalelerine bakın. Arama sonrası her Sharpe'ı trial count için deflate edin: deflated Sharpe ve edge gerçek olmadığında bunun nasıl göründüğü için dürüst negatif. - Transaction cost. Slippage cost models ve maker-taker fees. %53 doğruluklu bir yön modeli, seçtiğiniz cost model'ine göre yaşar veya ölür.
Üç ek tuzak burada sonuç değil hipotez olarak belirtiliyor: yalnızca listelenen varlıklarla eğitimden kaynaklanan survivorship bias; beta-residual getirilerin daha iyi davranabileceği ham getiri hedeflerinin non-stationarity'si; ve SHAP-over-folds diagnostik'inin doğal drift monitor'ü olduğu retraining cadence.
Sonuç

Getiri yönü sınıflandırması, kimsenin seçmediği bir karar eşiğine sahip dengesiz bir problemdir ve üç standart çözüm birbirinin yerine geçemez. scale_pos_weight tek satırdır ancak olasılıkların calibration'ını bozar ve min_child_weight'i sessizce kaydırır. Focal loss, çoğu implementasyonun yaklaştırdığı bir Hessian gerektirir. Threshold optimization modeli olduğu gibi bırakır ve trading açısından önemli kısıtı — precision'ı — olması gereken yere koyar; ancak eşik eğitimden sonra gelen veriyle fold başına yeniden fit edilirse.
Hangisinin kazandığı veriniz, eşiğiniz ve cost model'iniz hakkında ampirik bir sorudur. Üstteki tablo tek bir veri kümesinin cevabıdır; kendi verinizde çalıştırmak tahmin ederek geçireceğiniz tuning süresinden daha ucuzdur.
İleri okuma:
- Grinsztajn et al., "Why do tree-based models still outperform deep learning on typical tabular data?" (NeurIPS 2022)
- Wang et al., "Imbalance-XGBoost: leveraging weighted and focal losses for binary label-imbalanced classification"
- Chen & Guestrin, "XGBoost: A Scalable Tree Boosting System" (KDD 2016)
- Ke et al., "LightGBM: A Highly Efficient Gradient Boosting Decision Tree" (NeurIPS 2017)
- Prokhorenkova et al., "CatBoost: unbiased boosting with categorical features" (NeurIPS 2018)
- XGBoost documentation: Notes on Parameter Tuning
Yazarlar
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.