تعلم الآلة على العوائد
يلائم التعلم الخاضع للإشراف دالة من السمات X إلى هدف y. وفي المالية يكون الهدف عادة عائدًا مستقبليًا والسمات أمورًا معروفة مسبقًا: عوائد سابقة وتقلب وحجم تداول. ويوفر scikit-learn واجهة موحدة: لكل نموذج fit(X, y) وpredict(X). والفرق الجوهري عن مسائل الكتب هو نسبة الإشارة إلى الضجيج: العوائد شبه ضجيج خالص، فحتى النموذج الجيد يفسر جزءًا ضئيلًا، وإعداد مهمل يجد أنماطًا غير موجودة.
import numpy as np
returns = np.array([0.01, -0.02, 0.015, 0.005, -0.01, 0.02])
n_lags = 2
# Row i holds the two returns before the target; y[i] is the return that follows them
windows = np.lib.stride_tricks.sliding_window_view(returns, n_lags)
X, y = windows[:-1], returns[n_lags:]
print(X)
print(y) # the targets are one step ahead of the featuresسمات من التأخيرات
أبسط سمات العائد هي العوائد التي تسبقه. ابنِ مصفوفة تأخيرات: الصف i يحوي returns[i : i + n_lags] والهدف هو returns[i + n_lags] أي القيمة التالية. وتعطيها نافذة منزلقة بلا حلقة. ويجب أن يكون كل ما في الصف معروفًا وقت التنبؤ، وهي قاعدة النظر إلى المستقبل مطبقة على نموذج: سمة أُزيحت موضعًا في الاتجاه الخاطئ تتيح للنموذج رؤية الجواب.
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
# Pipeline: scale each feature, then a ridge regression (a linear model with a penalty on large coefficients)
pipe = Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=10.0))])
print([name for name, _ in pipe.steps])
rng = np.random.default_rng(1)
returns = np.zeros(400)
noise = rng.normal(0, 0.01, size=400)
for t in range(1, 400):
returns[t] = 0.3 * returns[t - 1] + noise[t] # a series with a little memory
windows = np.lib.stride_tricks.sliding_window_view(returns, 3)
X, y = windows[:-1], returns[3:]
pipe.fit(X[:300], y[:300]) # fit on the first 300 rows
print(np.round(pipe.named_steps["model"].coef_, 4)) # the last lag weighs mostخطوط المعالجة وتسرب المعلومات
يجمع Pipeline المقياس والنموذج بحيث يتعلم fit إحصاءات التحجيم (متوسط كل سمة وانحرافها) من صفوف التدريب وحدها ويطبقها predict دون تغيير على الصفوف الجديدة. أما تحجيم مجموعة البيانات كلها ثم تقسيمها فيسرّب متوسط فترة الاختبار وتشتتها إلى التدريب: انحياز النظر إلى المستقبل نفسه في موضع جديد. وفي السلسلة الزمنية يكون التقسيم زمنيًا أيضًا: تدريب على الصفوف المبكرة واختبار على اللاحقة، لا خلط عشوائي أبدًا.
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
def build_pipeline(alpha):
return Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=alpha))])
rng = np.random.default_rng(1)
returns = np.zeros(600)
noise = rng.normal(0, 0.01, size=600)
for t in range(1, 600):
returns[t] = 0.3 * returns[t - 1] + noise[t]
windows = np.lib.stride_tricks.sliding_window_view(returns, 3)
X, y = windows[:-1], returns[3:]
model = build_pipeline(10.0).fit(X[:400], y[:400]) # chronological split: early rows to train
pred = model.predict(X[400:]) # later rows to test
print(round(float(np.mean(np.sign(y[400:]) == np.sign(pred))), 3)) # direction accuracy on unseen dataالحكم على النتيجة بأمانة
في إشارة التداول كثيرًا ما يهم الاتجاه، فمقياس بسيط هو دقة الاتجاه: نسبة الأيام التي لإشارة العائد المتنبأ به والفعلي فيها الإشارة نفسها. و50% يعادل رمي عملة. وتبلغ متنبئات العوائد الحقيقية 51 إلى 53%، ودرجة 60% على بيانات الاختبار أرجح أن تكون تسربًا لا اكتشافًا. قارن بالخطوط الأساسية البسيطة (التنبؤ بالصعود دائمًا أو بالصفر) قبل تصديق أي شيء.
import numpy as np
y_true = np.array([0.01, -0.02, 0.015, -0.005, 0.02, -0.01])
y_pred = np.array([0.004, 0.001, 0.002, -0.001, 0.003, 0.002]) # the model is right on 4 of 6 signs
accuracy = float(np.mean(np.sign(y_true) == np.sign(y_pred)))
always_up = float(np.mean(np.sign(y_true) == 1)) # the trivial baseline: always predict a rise
print(round(accuracy, 3), round(always_up, 3)) # compare the model with the baselinealpha مقبض يُضبط، لكن على فترة تحقق لا على فترة الاختبار أبدًا. وتبني المواضيع التالية السمات والاختبار المتدرج (walk-forward) اللذين يجعلان ذلك أمينًا.- تعطي مصفوفة التأخيرات (
sliding_window_view) سمات من العوائد السابقة لكل هدف؛ ويجب أن يكون كل ما في الصف معروفًا وقت التنبؤ. - يتعلم
Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=alpha))])التحجيم من صفوف التدريب وحدها؛ قسّم زمنيًا لا عشوائيًا أبدًا. - قيّم الاتجاه (نسبة الإشارات المتساوية) مقابل خطوط أساسية بسيطة؛ 51 إلى 53% واقعي و60% يوحي بتسرب؛ والنماذج الخطية المنتظمة تلائم البيانات الصاخبة.
التالي: هندسة السمات للبيانات المالية، بناء مدخلات أفضل من العوائد المتأخرة الخام.
المختبر: ما الذي يُقيَّم
- حوّل السلسلة إلى سمات باستخدام
lag_matrix25 نقطة - اربط الخطوات باستخدام
build_pipeline25 نقطة - درّب على الماضي وتنبأ بالباقي باستخدام
fit_predict25 نقطة - قيّم الاتجاهات باستخدام
direction_accuracy25 نقطة