AutoML สำหรับไปป์ไลน์การซื้อขายเชิงระบบ
บล็อกนี้ใช้เวลาส่วนใหญ่ใน วิธีการค้นหา: ตัวอย่างใดที่จะใช้และเมื่อใด (ครอสโอเวอร์คือต้นทุนการประเมิน) วิธีกำหนดราคาการค้นหาที่คุณรัน (อัตราส่วนความคมชัดที่ลดลง) วิธีให้คะแนนขั้นตอนการคัดเลือก (PBO) และตำแหน่งที่อุปกรณ์ทั้งหมดลงจอดอย่างตรงไปตรงมา (ไม่มีขอบที่แข็งแกร่ง)
สิ่งที่ไม่เคยครอบคลุมคือสิ่งที่คุณค้นหา มากกว่า บทความเหล่านั้นล้วนใช้ชุดคุณลักษณะและกลุ่มกลยุทธ์ตามที่กำหนดและปรับพารามิเตอร์ภายในให้เหมาะสม AutoML โจมตีเลเยอร์ด้านบน — มันทำให้การสร้างผู้สมัครโดยอัตโนมัติ สามส่วนที่ไม่ปรากฏที่อื่นในบล็อกนี้:
- การแยกคุณลักษณะอัตโนมัติ — tsfresh เปลี่ยนชุดราคาหนึ่งชุดให้เป็นคุณลักษณะที่มีคุณลักษณะทางสถิติประมาณ 794 รายการ และ Featuretools จะเขียนคุณลักษณะต่างๆ ในตารางตลาดที่เกี่ยวข้องผ่านการสังเคราะห์คุณลักษณะเชิงลึก
- AutoML ที่คำนึงถึงงบประมาณ — การเพิ่มประสิทธิภาพต้นทุนแบบประหยัดของ FLAML ซึ่งค้นหาโมเดลที่ดีที่สุด ภายในงบประมาณการประมวลผล แทนที่จะคำนึงถึงต้นทุน
- โรงงานอัลฟ่าสูตร — อัลฟ่าสูตร 101 รายการของ WorldQuant เป็นตัวดำเนินการไวยากรณ์ที่เครื่องสามารถระบุได้
อย่างอื่นทั้งหมด เช่น ฟังก์ชั่นวัตถุประสงค์ รูปแบบการตรวจสอบความถูกต้อง การแก้ไขการทดสอบหลายรายการ ได้รับการวัดแล้วที่อื่นที่นี่ ดังนั้นบทความนี้จึงเชื่อมโยงแทนที่จะสอนซ้ำ นั่นมีความสำคัญมากกว่าปกติ เนื่องจาก AutoML ทำให้จำนวนการทดลองใช้เพิ่มขึ้น และการจำนวนการทดลองก็ตรงกับส่วนที่เหลือของส่วนโค้ง

AutoML ใดที่ทำงานอัตโนมัติ
AutoML ไม่ใช่อัลกอริธึมเดียว เป็นตระกูลเทคนิคที่ทำให้ขั้นตอนต่างๆ ของไปป์ไลน์เป็นไปโดยอัตโนมัติ:
| เวที | วิธีการด้วยตนเอง | วิธี AutoML |
|---|---|---|
| วิศวกรรมคุณสมบัติ | ตัวชี้วัดงานฝีมือของผู้เชี่ยวชาญโดเมน | การสกัดอัตโนมัติ (tsfresh, Featuretools) |
| การเลือกคุณสมบัติ | การวิเคราะห์สหสัมพันธ์ สัญชาตญาณ | การทดสอบสมมติฐานทางสถิติ SHAP |
| การเลือกรุ่น | ลองสัก 2-3 รุ่น | ค้นหาผู้เรียนหลายสิบคน |
| การปรับไฮเปอร์พารามิเตอร์ | ค้นหาตาราง ปรับแต่งด้วยตนเอง | การค้นหาแบบเบย์ / ประหยัดต้นทุน (Optuna, FLAML) |
| การออกแบบสถาปัตยกรรม | แก้ไขโทโพโลยีโครงข่ายประสาทเทียม | การค้นหาสถาปัตยกรรมประสาท (NAS) |
| การก่อสร้างทั้งมวล | การซ้อนด้วยตนเอง | การเลือกวงดนตรีอัตโนมัติ |
หลักฐานก็คือในการซื้อขายอย่างเป็นระบบ พื้นที่ของผู้สมัครมีขนาดใหญ่มาก — ซีรีส์ OHLCV ชุดเดียวสร้างคุณสมบัติทางเทคนิคหลายร้อยรายการ และสินทรัพย์หลายรายการ ข้อมูลการสั่งซื้อ และแหล่งข้อมูลทางเลือกทำให้สามารถรวมกันได้ หลักฐานก็เป็นอันตรายเช่นกัน ผู้สมัครทุกคนคือการทดลอง และการทดลองคือสิ่งที่ทำให้เกิดการค้นพบที่ผิดพลาด
จุดเฉพาะของ AutoML หนึ่งจุดในวัตถุประสงค์: เมตริกที่การค้นหาเพิ่มสูงสุดจะต้องเป็นเมตริกทางการเงิน เนื่องจากการค้นหาจะปรับให้เหมาะสมตามสิ่งที่คุณจดไว้ทุกประการและไม่ได้หมายถึงอะไร สเกลาร์ที่คุณเลือกจะเป็นการเลือกกลยุทธ์ของคุณ — ดู Objective-Function Design โดยที่ Sharpe ที่ไร้เดียงสาต่อการซื้อขายจะครองลอตเตอรีที่มีความเสี่ยงต่ำกว่า 5% ใน 56% ของ 600 เมล็ด และโพสต์ Sharpe ในตัวอย่างจำนวน 21 รายการที่ลดลงเหลือ 0.13 จากตัวอย่าง เชื่อมโยงตัวชี้วัดทางการเงินเข้ากับตัวบันทึกบัญชี AutoML ตั้งแต่เริ่มต้น ไม่ปรับให้เหมาะสม roc_auc และหวังว่าจะถ่ายโอน
วิศวกรรมคุณสมบัติอัตโนมัติ
วิศวกรรมฟีเจอร์คือจุดที่อัลฟ่าส่วนใหญ่อาศัยอยู่ ข้อมูลราคาดิบจะเหมือนกันสำหรับทุกคน การแปลงข้อมูลดังกล่าวให้เป็นสัญญาณคาดการณ์คือสิ่งที่แยกกลยุทธ์ที่ทำกำไรออกจากสัญญาณรบกวน

tsfresh: จากซีรีย์ราคาเดียวไปจนถึงฟีเจอร์มากกว่า 800 รายการ
tsfresh (การแยกคุณลักษณะอนุกรมเวลาตามการทดสอบสมมุติฐานที่ปรับขนาดได้) สร้างขึ้นโดยมีจุดประสงค์เพื่อการแยกคุณลักษณะอนุกรมเวลา มันคำนวณวิธีการกำหนดลักษณะ 63 วิธีซึ่งขยายเป็นคุณสมบัติ ~ 794 ตามค่าเริ่มต้น ซึ่งรวมถึง:
- ช่วงเวลาทางสถิติ (ค่าเฉลี่ย ความแปรปรวน ความเบ้ ความโด่ง)
- ความสัมพันธ์อัตโนมัติที่ความล่าช้าหลายครั้ง
- ค่าสัมประสิทธิ์ฟูริเยร์และพลังงานสเปกตรัม
- มาตรการความซับซ้อน (เอนโทรปีโดยประมาณ, เอนโทรปีตัวอย่าง)
- คุณสมบัติไม่เชิงเส้น (ค่าสัมประสิทธิ์ฟรีดริช, จุดคงที่ Langevin สูงสุด)
- เปลี่ยนปริมาณและการนับช่วง
สำหรับข้อมูลทางการเงิน หมายความว่าชุดราคาเดียวจะสร้างคุณสมบัติที่เป็นตัวเลือกได้หลายร้อยรายการ ซึ่งหลายๆ รายการจะบันทึกการเปลี่ยนแปลงที่วิศวกรรมคุณสมบัติแบบแมนนวลอาจพลาดไป นี่เป็นแนวทางที่แตกต่างอย่างมากจากชุดฟีเจอร์ที่มาจากโดเมนซึ่งสร้างขึ้นด้วยมือซึ่งใช้ใน การสร้างแบบจำลองสเปรด และ DeepLOB: มีมนุษย์ตัดสินใจว่าความไม่สมดุลของสมุดคำสั่งซื้อมีความสำคัญ ที่นี่ห้องสมุดจะแจกแจงทุกอย่างและให้การทดสอบสมมติฐานตัดสินใจ
import pandas as pd
import numpy as np
from tsfresh import extract_features, select_features
from tsfresh.utilities.dataframe_functions import impute
def prepare_rolling_windows(df: pd.DataFrame, window_size: int = 20) -> pd.DataFrame:
"""Convert OHLCV DataFrame into rolling windows for tsfresh."""
records = []
for i in range(window_size, len(df)):
window = df.iloc[i - window_size:i].copy()
window["id"] = i # window identifier
window["time"] = range(window_size)
records.append(window[["id", "time", "close", "volume", "high", "low"]])
return pd.concat(records, ignore_index=True)
df_ohlcv = pd.read_csv("btc_1h.csv", parse_dates=["timestamp"])
df_rolled = prepare_rolling_windows(df_ohlcv, window_size=24)
features = extract_features(
df_rolled,
column_id="id",
column_sort="time",
n_jobs=8, # parallel extraction
disable_progressbar=False,
)
impute(features)
y = df_ohlcv["close"].pct_change(4).shift(-4).iloc[24:].reset_index(drop=True)
y_binary = (y > 0).astype(int)
features_selected = select_features(features, y_binary, fdr_level=0.05)
print(f"Selected {features_selected.shape[1]} features from {features.shape[1]}")
ที่ select_features ฟังก์ชั่นใช้ขั้นตอน Benjamini-Yekutieli เพื่อควบคุมอัตราการค้นพบที่ผิดพลาดในแบตเตอรี่ฟีเจอร์ทั้งหมด ซึ่งเป็นสัญชาตญาณที่ถูกต้อง และสัญชาตญาณเดียวกันกับ บทความ Sharpe ที่ยุบตัว นำไปใช้กับการค้นหากลยุทธ์ โปรดทราบว่าสิ่งที่ ไม่ ทำ: การควบคุม FDR ในขั้นตอนการเลือกคุณสมบัติไม่ได้พูดอะไรเกี่ยวกับต้นทุนการทดลองใช้การค้นหาแบบจำลองที่ตามมา สิ่งเหล่านี้เป็นค่าทวีคูณสองค่าที่แยกจากกัน และต้องจ่ายทั้งคู่
Featuretools: การสังเคราะห์คุณสมบัติเชิงลึก
ในขณะที่ tsfresh มุ่งเน้นไปที่การกำหนดลักษณะอนุกรมเวลา Featuretools เป็นเลิศในด้านวิศวกรรมฟีเจอร์เชิงสัมพันธ์ — การสร้างฟีเจอร์โดยการเขียนการดำเนินการดั้งเดิมในตารางที่เกี่ยวข้องกัน
สำหรับการเทรด สิ่งนี้จะมีประสิทธิภาพเมื่อคุณมีข้อมูลหลายตาราง: ประวัติการซื้อขาย, สแนปชอตสมุดคำสั่งซื้อ, อัตราเงินทุน และตัวชี้วัดแบบออนไลน์ Featuretools ใช้การสังเคราะห์คุณสมบัติเชิงลึก (DFS) ซึ่งจะซ้อนการเปลี่ยนแปลงและการรวมกลุ่มแบบดั้งเดิม:
import featuretools as ft
es = ft.EntitySet(id="crypto_trading")
es = es.add_dataframe(
dataframe_name="candles",
dataframe=df_candles,
index="candle_id",
time_index="timestamp",
)
es = es.add_dataframe(
dataframe_name="orderbook",
dataframe=df_orderbook,
index="snapshot_id",
time_index="timestamp",
)
es = es.add_dataframe(
dataframe_name="funding",
dataframe=df_funding,
index="funding_id",
time_index="timestamp",
)
feature_matrix, feature_defs = ft.dfs(
entityset=es,
target_dataframe_name="candles",
agg_primitives=["mean", "std", "max", "min", "skew", "trend"],
trans_primitives=["percentile", "diff", "cum_mean"],
max_depth=2, # compose up to 2 primitives deep
features_only=False,
)
print(f"Generated {len(feature_defs)} features via DFS")
ที่ time_index การประกาศเป็นแบบรับน้ำหนัก: เป็นสิ่งที่ช่วยให้ Featuretools คำนวณการรวมกลุ่มโดยใช้เฉพาะแถวที่มีอยู่ที่จุดตัดเท่านั้น หากทำผิดแล้ว DFS จะสร้างฟีเจอร์ที่มาจากอนาคตอย่างมีความสุข — อินสแตนซ์ข้อมูลเชิงสัมพันธ์ของการรั่วไหลที่จัดอยู่ในรายการ อนุกรมวิธานอคติแบบมองไปข้างหน้า
การรวมกันของ tsfresh (การกำหนดลักษณะอนุกรมเวลา) และ Featuretools (การสังเคราะห์เชิงสัมพันธ์) ช่วยให้คุณมีฟีเจอร์แฟคทอรีที่สร้างสัญญาณที่เป็นตัวเลือกหลายพันรายการจากข้อมูลตลาดดิบ โดยไม่ต้องใช้การเข้ารหัสตัวบ่งชี้ด้วยตนเอง
AutoML ที่คำนึงถึงงบประมาณ: FLAML
เราถือว่า Optuna และ Parzen Estimators ที่มีโครงสร้างแบบต้นไม้เป็นพื้นหลัง — การได้มา การเปรียบเทียบตัวอย่าง และเกณฑ์มาตรฐานอยู่ใน Coordinate Descent เทียบกับ Bayesian Optimization ซึ่ง ตัวอย่างที่จะเข้าถึง การวัดของบล็อกนี้เป็นข้อมูลอ้างอิงและไม่ใช่คำตอบตามคติพื้นบ้าน: ครอสโอเวอร์ถูกควบคุมโดยต้นทุนการประเมิน ไม่ใช่ความฉลาดของอัลกอริทึม — เมื่อการทดสอบย้อนกลับเกือบจะฟรี Sobol ที่มีสัญญาณรบกวนจะชนะตามปริมาณงาน (~2,830 cfg/s เทียบกับ ~154 สำหรับ TPE) และมีเพียงการประเมินราคาแพงเท่านั้นที่ทำให้ต้องเสียประสิทธิภาพของตัวอย่าง (สุ่มเทียบกับอัจฉริยะ ค้นหา)

การจัดเฟรมนั้นเป็นสิ่งที่ทำให้ FLAML มีแกนที่แตกต่างออกไป แทนที่จะเป็นตัวเก็บตัวอย่างที่แข่งขันกัน Optuna ถาม ว่าจะเก็บตัวอย่างต่อไปที่ไหน; แฟลม (Fast Lightweight AutoML) ถามว่า ฉันสามารถหาตัวอย่างอะไรได้บ้าง ปรับให้เหมาะสมสำหรับโมเดลที่ดีที่สุดภายในเวลาที่กำหนดหรือคำนวณงบประมาณโดยใช้ CFO (การเพิ่มประสิทธิภาพต้นทุน) ซึ่งจัดลำดับความสำคัญของการกำหนดค่าราคาถูกเพื่อประเมินตั้งแต่เนิ่นๆ และเพิ่มระดับเฉพาะเมื่อกำไรส่วนเพิ่มโดยประมาณที่สมเหตุสมผลในการใช้จ่าย
from flaml import AutoML
from sklearn.metrics import make_scorer
import numpy as np
def sharpe_score(y_true, y_pred):
"""Custom scorer: Sharpe ratio of predicted signals."""
signal = np.sign(y_pred - 0.5)
returns = signal * y_true # y_true contains forward returns
if returns.std() == 0:
return 0.0
return np.sqrt(252) * returns.mean() / returns.std()
sharpe_scorer = make_scorer(sharpe_score, greater_is_better=True)
automl = AutoML()
automl_settings = {
"time_budget": 300, # 5 minutes
"metric": sharpe_scorer, # optimize the financial objective, not accuracy
"task": "classification",
"estimator_list": [
"lgbm", "xgboost", "rf", "extra_tree", "catboost",
],
"eval_method": "cv",
"split_type": "time", # time series split (no future leakage)
"n_splits": 5,
"log_file_name": "flaml_trading.log",
"seed": 42,
}
automl.fit(
X_train=X_train,
y_train=y_train,
**automl_settings,
)
print(f"Best model: {automl.best_estimator}")
print(f"Best config: {automl.best_config}")
from flaml.data import get_output_from_log
time_history, best_valid_loss_history, valid_loss_history, config_history, metric_history = \
get_output_from_log(filename="flaml_trading.log", time_budget=300)
ข้อได้เปรียบของ FLAML สำหรับการซื้อขายคือการจัดสรรการประมวลผลข้ามประเภทโมเดลที่ต่างกัน หากการกำหนดค่า LightGBM ประเมินได้เร็วกว่า CatBoost ถึง 10 เท่า FLAML จะสำรวจ LightGBM มากขึ้นตั้งแต่เนิ่นๆ และเปลี่ยนเฉพาะเมื่อประเมินว่าการปรับปรุงเล็กน้อยจะช่วยลดต้นทุนได้ นั่นคือข้อมูลเชิงลึกเกี่ยวกับระบบราคาถูกจากบทความแบบครอสโอเวอร์ ซึ่งมีเนื้อหาทั่วไปว่า ปริมาณงานเป็นคำระดับเฟิร์สคลาสในงบประมาณการค้นหา ไม่ใช่รายละเอียดการใช้งาน
บันทึกวิถีเป็นส่วนที่ควรค่าแก่การรักษา get_output_from_log ให้ประวัติการกำหนดค่าทั้งหมดแก่คุณ ซึ่งเป็นจำนวนการทดลองใช้ของคุณ — และจำนวนการทดลองใช้คือข้อมูลอินพุตของภาวะเงินฝืดทุกครั้งที่คุณเป็นหนี้
ค้นหาสถาปัตยกรรมประสาทสำหรับโมเดลการซื้อขาย
การค้นหาสถาปัตยกรรมประสาททำให้การออกแบบโทโพโลยีเครือข่ายเป็นไปโดยอัตโนมัติ แทนที่จะปรับแต่งโทโพโลยีแบบตายตัว ตัวเลือกสถาปัตยกรรมสำหรับซีรีส์ทางการเงินนั้นไม่ชัดเจนอย่างแท้จริง - การไม่คงที่ สัญญาณต่อเสียงรบกวนต่ำ ความถี่ผสม และการพึ่งพาซึ่งครอบคลุมตั้งแต่นาทีถึงเดือน ล้วนดึงไปในทิศทางที่ต่างกัน - แต่แรงจูงใจนั้นถูกโต้แย้งอย่างเป็นรูปธรรมในที่อื่นแล้ว: ดู Temporal Fusion Transformer เมื่อ TFT เต้น LSTM เต้น vanilla Transformer และ DeepLOB เกี่ยวกับข้อดีข้อเสียของ CNN+Inception+LSTM สำหรับสิ่งที่สถาปัตยกรรมคงที่ที่ออกแบบด้วยมืออย่างระมัดระวังบรรลุผลสำเร็จแล้ว NAS เป็นข้อโต้แย้งว่าควรค้นหาตัวเลือกนี้เอง
การวิจัยเกี่ยวกับ Neuroevolution NAS สำหรับการทำนายการคืนสินค้า รายงานว่า RNN ที่พัฒนาผ่าน EXAMM (Evolutionary eXploration of Augmenting Memory Models) มีประสิทธิภาพเหนือกว่า DJI และ S&P 500 ในตลาดหมี (2022) และตลาดกระทิง (2023) โดยใช้กลยุทธ์ระยะสั้นแบบง่ายๆ ให้ถือว่าเป็นการกล่าวอ้างของบุคคลที่สาม ไม่ใช่เป็นผลจากการทำซ้ำที่นี่ เนื่องจากเป็นตัวเลขที่มีการค้นหาดีที่สุด โดยไม่มีการรายงานภาวะเงินฝืด
พื้นที่ค้นหา NAS เพื่อการค้าขาย
พื้นที่ค้นหา NAS ที่ใช้งานได้จริงสำหรับโมเดลการซื้อขายประกอบด้วย:
Search Space:
- Cell types: {LSTM, GRU, Temporal Conv, Transformer block, Linear}
- Number of layers: [1, 8]
- Hidden dimensions: {32, 64, 128, 256}
- Attention heads (if Transformer): {2, 4, 8}
- Dropout rate: [0.0, 0.5]
- Skip connections: {True, False}
- Normalization: {BatchNorm, LayerNorm, None}
- Activation: {ReLU, GELU, SiLU, Mish}
- Lookback window: {20, 60, 120, 252}
ด้วย NNI (Neural Network Intelligence) จาก Microsoft คุณสามารถกำหนดและค้นหาพื้นที่นี้:
import nni
from nni.nas.nn.pytorch import LayerChoice, InputChoice
import torch
import torch.nn as nn
class TradingNASModel(nn.Module):
"""NAS-searchable model for financial time series."""
def __init__(self, input_dim: int, seq_len: int):
super().__init__()
self.input_proj = nn.Linear(input_dim, 128)
self.temporal_block = LayerChoice([
nn.LSTM(128, 128, num_layers=2, batch_first=True, dropout=0.1),
nn.GRU(128, 128, num_layers=2, batch_first=True, dropout=0.1),
nn.TransformerEncoder(
nn.TransformerEncoderLayer(d_model=128, nhead=4, batch_first=True),
num_layers=2,
),
], label="temporal_cell")
self.head = LayerChoice([
nn.Sequential(nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 1)),
nn.Sequential(nn.Linear(128, 32), nn.GELU(), nn.Linear(32, 1)),
nn.Linear(128, 1),
], label="prediction_head")
self.skip = InputChoice(n_candidates=2, n_chosen=1, label="skip_conn")
self.skip_proj = nn.Linear(input_dim, 128)
def forward(self, x: torch.Tensor) -> torch.Tensor:
h = self.input_proj(x)
out = self.temporal_block(h)
if isinstance(out, tuple):
out = out[0]
last = out[:, -1, :]
skip_val = self.skip_proj(x[:, -1, :])
last = self.skip([last, last + skip_val])
return self.head(last).squeeze(-1)
อัลกอริธึมการค้นหา (ENAS, DARTS หรือวิวัฒนาการ) ประเมินสถาปัตยกรรมของผู้สมัครในชุดการตรวจสอบความถูกต้องโดยใช้วัตถุประสงค์ทางการเงิน จากนั้นค่อยๆ แคบลงไปสู่โทโพโลยีที่มีประสิทธิภาพสูง โปรดสังเกตความไม่สมดุลของต้นทุนเมื่อเทียบกับสิ่งอื่นๆ ในบทความนี้: NAS อยู่ที่ปลายสุดของแกนประเมินต้นทุนที่มีราคาแพงมาก ซึ่งเป็นระบบหนึ่งที่ผู้เก็บตัวอย่างที่มีประสิทธิภาพในการสุ่มตัวอย่างและการตัดหลายความเที่ยงตรงจ่ายเองอย่างแท้จริง
แนวทางโรงงาน WorldQuant Alpha
แนวทางการค้นพบอัลฟ่าของ WorldQuant ถือเป็นการนำหลักการ AutoML ไปใช้ในด้านการเงินในระดับอุตสาหกรรมมากที่สุด WorldQuant ก่อตั้งโดย Igor Tulchinsky ดำเนินธุรกิจที่เรียกว่า "โรงงานอัลฟ่า" ซึ่งเป็นกระบวนการที่เป็นระบบในการสร้าง ทดสอบ และรวมสัญญาณคาดการณ์หลายล้านสัญญาณ
ปรัชญา: การสร้างอัลฟ่าแบบเอ็กซ์โพเนนเชียลและการแก้ไข
WorldQuant ตั้งเป้าหมายในปี 2010 ที่จะเข้าถึงอัลฟ่าหนึ่งล้านตัว โดยในช่วงเวลาหนึ่งที่ผลิตได้เพียงหลายพันตัวต่อปี พวกเขาบรรลุเป้าหมายนั้นในปี 2559 ปรัชญาตั้งอยู่บนกฎพื้นฐานของการจัดการเชิงรุก:
ที่ไหน คืออัตราส่วนข้อมูล ค่าสัมประสิทธิ์ข้อมูลและ ความกว้าง — จำนวนการเดิมพัน อิสระ แต่ละคน เป็นตัวทำนายที่อ่อนแอ ข้อกล่าวอ้างก็คือการรวมตัวที่มีความสัมพันธ์กันอย่างอ่อนเพียงพอจะรวมไอซีขนาดเล็กเข้ากับผลตอบแทนที่ปรับความเสี่ยงอย่างมีความหมาย
การแก้ไขที่สร้างหรือฝ่าฝืน และการอ่านสูตรแบบไร้เดียงสาซ่อนอยู่: ความกว้างไม่ใช่การนับการทดลอง นับการเดิมพันแบบอิสระ และอัลฟ่าที่สร้างขึ้นนั้นไม่เป็นอิสระ — พวกมันประกอบด้วยตัวดำเนินการเดียวกันในช่องราคาและปริมาณเดียวกัน ดังนั้นจึงสืบทอดโครงสร้างความสัมพันธ์ของกันและกัน บล็อกนี้วัดผลกระทบโดยตรงใน Signal Correlation: ความกว้างที่มีประสิทธิภาพคือ และด้วยปัจจัยความสัมพันธ์ของสกุลเงินดิจิทัลทั่วไป สัญญาณสิบสัญญาณมีค่าประมาณ 3.3 สัญญาณอิสระ ที่ คำจะไม่เพิ่มขึ้นตามจำนวนอัลฟ่าที่คุณสร้าง มันเติบโตขึ้นตามจำนวนคนที่ไม่เกี่ยวข้องที่รอดชีวิต อัลฟ่าที่สัมพันธ์กันนับล้านซื้อคุณน้อยกว่าสูตรที่โฆษณาไว้มาก
นี่คือเหตุผลว่าทำไมขั้นตอนที่ 3 ด้านล่าง — ความสัมพันธ์กับห้องสมุดที่มีอยู่ — ไม่ใช่ขั้นตอนด้านสุขอนามัยในโรงงานอัลฟ่า เป็นขั้นตอนที่สร้างความกว้างตั้งแต่แรก
อัลฟ่าสูตร 101 ตัว
บทความ "101 Formulaic Alphas" โดย Kakushadze (ที่เกี่ยวข้องกับการวิจัยของ WorldQuant) ตีพิมพ์ชุดนิพจน์อัลฟ่าที่เป็นสูตร — นิพจน์ทางคณิตศาสตร์เหนือราคา ปริมาณ และข้อมูลพื้นฐาน:
Alpha#1: (rank(Ts_ArgMax(SignedPower(((returns < 0) ? stddev(returns, 20)
: close), 2.), 5)) - 0.5)
Alpha#7: ((adv20 < volume) ? ((-1 * ts_rank(abs(delta(close, 7)), 60))
* sign(delta(close, 7))) : (-1 * 1))
Alpha#42: (rank(vwap - close) / rank(vwap + close))
สิ่งที่ทำให้สิ่งนี้น่าสนใจสำหรับ AutoML ก็คือ มันเป็น ไวยากรณ์ อัลฟาเป็นองค์ประกอบของชุดตัวดำเนินการขนาดเล็ก (rank, delta, ts_rank, stddev, correlation, signedpower) บนชุดฟิลด์ขนาดเล็ก (close, volume, vwap, returns, adv20). ไวยากรณ์สามารถนับได้ ซึ่งหมายความว่าพื้นที่การค้นหานั้นสามารถสร้างโดยเครื่องได้ในลักษณะที่ "คิดถึงคุณลักษณะที่ดี" ไม่ใช่ ระบบ AutoML สามารถ:
- สร้าง นิพจน์ของผู้สมัครโดยการเขียนตัวดำเนินการบนช่องข้อมูล
- ประเมิน IC, มูลค่าการซื้อขาย และ Drawdown ของแต่ละนิพจน์
- ตัวกรอง สำหรับนิพจน์ที่รอดจากการทดสอบทางสถิติ และ ไม่มีความสัมพันธ์กับไลบรารีที่มีอยู่อย่างเพียงพอ
- รวม ผู้รอดชีวิตไว้ในพอร์ตโฟลิโอ
โปรดทราบว่าขั้นตอนที่ 1 และ 2 เป็นขั้นตอนที่ง่าย และขั้นตอนที่ 3 คือตำแหน่งของค่า — ดูการแก้ไขความกว้างด้านบน
การค้นพบอัลฟ่าที่ปรับปรุงแล้วของ LLM
WorldQuant ได้เริ่มบูรณาการโมเดลภาษาขนาดใหญ่เข้ากับโรงงานอัลฟ่า ตามที่รายงานในสื่อทางการเงิน บริษัทกำลังสำรวจว่า LLM สามารถ "แปลงและค้นพบอัลฟ่าในโดเมนต่างๆ" ได้อย่างไร — สร้างการแสดงออกใหม่ๆ แปลเอกสารการวิจัยเป็นสมมติฐานที่ทดสอบได้ และแสดงความสัมพันธ์ข้ามโดเมนที่การค้นหาแบบนับล้วนๆ จะพลาดไป นี่เป็นกลไกที่แตกต่างจากการแยกสัญญาณออกจากข้อความ ซึ่งบล็อกนี้กล่าวถึงใน LLM Alpha Mining on Earnings Calls; ในที่นี้ LLM จะเขียน สูตร ไม่ใช่ คุณลักษณะ
สร้างโรงงาน Mini Alpha ของคุณเอง
นี่คือโรงงานอัลฟ่าขนาดกะทัดรัดแต่ใช้งานได้ดีเหนือไวยากรณ์ของผู้ปฏิบัติงาน:
import itertools
from dataclasses import dataclass
from typing import Callable, List
import pandas as pd
import numpy as np
from scipy.stats import spearmanr
@dataclass
class Alpha:
name: str
expression: Callable[[pd.DataFrame], pd.Series]
ic: float = 0.0
turnover: float = 0.0
sharpe: float = 0.0
def ts_rank(series: pd.Series, window: int) -> pd.Series:
return series.rolling(window).apply(lambda x: pd.Series(x).rank().iloc[-1] / len(x))
def ts_delta(series: pd.Series, period: int) -> pd.Series:
return series.diff(period)
def ts_std(series: pd.Series, window: int) -> pd.Series:
return series.rolling(window).std()
def cs_rank(series: pd.Series) -> pd.Series:
return series.rank(pct=True)
def ts_mean(series: pd.Series, window: int) -> pd.Series:
return series.rolling(window).mean()
ALPHA_TEMPLATES = [
("momentum_{w}",
lambda df, w: cs_rank(ts_delta(df["close"], w))),
("mean_reversion_{w}",
lambda df, w: -cs_rank(df["close"] / ts_mean(df["close"], w) - 1)),
("vol_surprise_{w}",
lambda df, w: cs_rank(df["volume"] / ts_mean(df["volume"], w))),
("price_vol_divergence_{w}",
lambda df, w: cs_rank(ts_delta(df["close"], w)) * -cs_rank(ts_delta(df["volume"], w))),
("volatility_rank_{w}",
lambda df, w: -cs_rank(ts_std(df["close"].pct_change(), w))),
("high_low_range_{w}",
lambda df, w: cs_rank(ts_mean(df["high"] - df["low"], w) / df["close"])),
]
WINDOWS = [5, 10, 20, 60, 120]
def generate_alphas(df: pd.DataFrame, forward_returns: pd.Series) -> List[Alpha]:
"""Generate and evaluate all alpha candidates."""
alphas = []
for (name_tpl, expr_fn), window in itertools.product(ALPHA_TEMPLATES, WINDOWS):
name = name_tpl.format(w=window)
try:
signal = expr_fn(df, window)
signal = signal.replace([np.inf, -np.inf], np.nan)
valid = signal.notna() & forward_returns.notna()
if valid.sum() < 100:
continue
ic, _ = spearmanr(signal[valid], forward_returns[valid])
turnover = signal.diff().abs().mean()
ret = (signal * forward_returns).dropna()
sharpe = np.sqrt(252) * ret.mean() / (ret.std() + 1e-9)
alphas.append(Alpha(
name=name,
expression=lambda df, fn=expr_fn, w=window: fn(df, w),
ic=ic,
turnover=turnover,
sharpe=sharpe,
))
except Exception:
continue
alphas.sort(key=lambda a: abs(a.ic), reverse=True)
return alphas
เทมเพลตหกเทมเพลตบนห้าหน้าต่างเป็นการกวาดล้างการกำหนดค่า 30 รายการ รายงานที่ผลิตมีรูปแบบ แผนผัง — ช่องต่างๆ คือสิ่งที่โรงงานส่งออก และไม่มีการกรอกตัวเลข เนื่องจากไม่มีการวัดที่นี่:
SCHEMATIC — illustrative format only, not a measurement
Generated <N> alphas from <N> candidate configurations
Top by |IC|:
<alpha_name> IC=<±0.0xxx> Sharpe=<±x.xxx> Turnover=<0.0xxx>
...
Sharpe แบบดิบของอัลฟ่าระดับบนสุดจากการกำหนดค่า 30 แบบ ไม่ใช่ผลลัพธ์ — มันคือสูงสุดที่ 30 เสมอ และในกลยุทธ์แบบศูนย์ขอบ 1,000 แบบ ค่าเฉลี่ย Sharpe ต่อปีที่ดีที่สุดอยู่ที่ 1.63 ในขณะที่การทดสอบแบบไร้เดียงสาประกาศการค้นพบ 100% ของเวลา (Deflated Sharpe Ratio) การส่งมอบจากโรงงานโดยสุจริตจึงไม่ใช่ "เราพบอัลฟ่า" แต่เป็น จำนวนอัลฟ่าที่รอดจากภาวะเงินฝืด: การกระจาย IC จำนวนการทดลอง DSR ของผู้ชนะเทียบกับจำนวนนั้น และ PBO ของการคัดเลือก นั่นคือการวัดส่วนนี้ยังคงเป็นหนี้อยู่
Guardrails: สิ่งที่คุณเป็นหนี้อยู่แล้ว
AutoML ขยายขอบเขตการค้นพบและการปรับแต่งมากเกินไปในขนาดที่เท่ากัน และจะทำเช่นนั้นในช่วงทดลองใช้โดยที่ความแตกต่างนั้นไม่สำคัญอีกต่อไป แต่ละสิ่งเหล่านี้จะกล่าวถึงในเชิงลึกในที่อื่น ประเด็นก็คือ AutoML ทำให้ทั้งหมดนี้เป็นเรื่องบังคับแทนที่จะแนะนำ
กำหนดราคาการค้นหา อย่าเพิ่งแก้ไขให้ถูกต้อง การกวาดคุณลักษณะโมเดล-ไฮเปอร์พารามิเตอร์เป็นการนับการทดลองใช้ และจำนวนการทดลองใช้ย้ายแถบ — ดู The Deflated Sharpe Ratio
ตรวจสอบความถูกต้องชั่วคราว โดยมีการล้างข้อมูลและคว่ำบาตร ดู Walk-Forward Optimization และ Look-Ahead Bias Taxonomy เพื่อดูการรั่วไหลที่รอดพ้นจากการแยกทางชั่วคราวอย่างไร้เดียงสา
ให้คะแนนขั้นตอน ไม่ใช่เฉพาะผู้ชนะ ดู ความน่าจะเป็นของ Backtest Overfitting และดำเนินการแก้ไขการรับน้ำหนัก: ค่าว่างของ PBO คือ 0.5 ไม่ใช่ 1 — ครึ่งหนึ่งไม่ใช่ "ครึ่งพอดี" แต่เป็นพอดีเกินพอดี พลิกเหรียญ
เฝ้าดูยอดเขาที่ไม่มีอยู่ตรงนั้น การค้นหาพื้นผิวที่มีเสียงรบกวนอย่างมีประสิทธิภาพจะพบภาพลวงตาที่คมชัดยิ่งขึ้นเร็วขึ้น (การวิเคราะห์ที่ราบสูง); สิ่งนี้กัดได้ยากที่สุดกับพารามิเตอร์กลยุทธ์และน้อยที่สุดในไฮเปอร์พารามิเตอร์ของ ML ซึ่งเป็นกรณีที่ FLAML และ Optuna ถูกสร้างขึ้นมาเพื่อจริง ๆ
จำกัดพื้นที่การค้นหา ตัวอย่างลีฟขั้นต่ำ ความลึกที่ต่อยอดและจำนวนตัวประมาณค่า งบประมาณฟีเจอร์ การลงโทษการหมุนเวียน — การทำให้การค้นหาเป็นปกติมีราคาถูกกว่าการทำให้การค้นหาจางลงในภายหลัง
บทสรุป
AutoML ย้ายการทำงานอัตโนมัติขึ้นไปอีกระดับ: ตั้งแต่การปรับพารามิเตอร์ภายในตัวเลือกคงที่ไปจนถึงการสร้างตัวเลือกด้วยตนเอง องค์ประกอบสามอย่างคุ้มค่ากับความพยายาม ได้แก่ tsfresh และ Featuretools สำหรับการสร้างฟีเจอร์ การค้นหาแบบประหยัดต้นทุนของ FLAML สำหรับการเลือกโมเดลที่คำนึงถึงงบประมาณ และไวยากรณ์ตัวดำเนินการตามสูตรสำหรับการสร้างอัลฟ่าที่นับได้ NAS จะคุ้มค่ากับการประมวลผลก็ต่อเมื่ออัตราส่วนสัญญาณต่อสัญญาณรบกวนของคุณเหมาะสมเท่านั้น และจะอยู่ที่ปลายสุดที่มีราคาแพงของแกนประเมินต้นทุน ซึ่งท้ายที่สุดแล้วประสิทธิภาพของตัวอย่างจะต้องจ่าย
อาร์กิวเมนต์ความกว้างของ WorldQuant ยังคงติดต่อกับการวัดของบล็อกนี้ในรูปแบบที่ถูกต้องเท่านั้น: ที่ไหน นับการเดิมพัน อิสระ และผลผลิตของโรงงานแทบไม่มีความเป็นอิสระเลย การสร้างอัลฟ่าล้านตัวนั้นง่ายเพียงครึ่งเดียว การพิจารณาว่าไม่มีความสัมพันธ์กันมากพอที่จะนับเป็นความกว้างคือครึ่งหนึ่งที่กำหนดว่าสูตรมีความหมายหรือไม่
และคำเตือนที่ซื่อสัตย์ไม่ใช่คุณธรรม แต่เป็นผลที่วัดได้ บล็อกนี้กล่าวถึงส่วนโค้งของการค้นหาและการปรับแต่งจนเสร็จสิ้นใน The Honest Negative: การทดสอบย้อนหลังนับหมื่นครั้งในห้าสาขาหลัก, DSR = 0.00 ที่การทดลอง ~ 37,000 ครั้ง, PBO 0.264 และ 0.327 และไม่มีขอบที่แข็งแกร่ง AutoML เพิ่มจำนวนการทดลองใช้ตามลำดับความสำคัญ ไม่ว่าจะพบอะไรก็ตาม มันจะติดหนี้ภาวะเงินฝืดที่มากขึ้นตามลำดับ และผลลัพธ์ที่เป็นประโยชน์ของโรงงานอัลฟ่าก็คืออัตราการรอดชีวิตหลังจากภาวะเงินฝืดนั้น ไม่ใช่ประเด็นสำคัญก่อนหน้านั้น
ผู้เขียน
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.