使用Python和MQL5开发机器人(第一部分):数据预处理·综合运用
📘

使用Python和MQL5开发机器人(第一部分):数据预处理·综合运用

第 3/3 篇

◍ 用随机算子自动造特征

做特征工程不必全靠人工挑变量。让算法从已有行情数据里自动组合出一批新特征,往往能挖到人工容易忽略的非线性关联,这对后续训练价格行为模型有实际帮助。 下面这段 Python 函数演示了自动生成思路:给定原始数据、要造的特征数和随机种子,它会在加减乘除、位移以及 2~20 窗口的滚动统计等算子间随机抽选,把两个已有列组合成新列。种子固定后,每次跑出来的特征集完全一致,方便你复现实验。 random_seed 不是摆设。原文示例里先用了 1,后来改成 42;种子一变,抽到的列索引和算子就全变,生成结果不可比对。想做交叉验证或给小布这类 AIGC 工具喂稳定样本,种子必须锁死。 代码跑完会打印尾部 100 行新特征。原文提到一次生成 100 个新特征后进入聚类阶段——你也可以把 num_features 调到 200 看维度爆炸后模型拟合时间的变化。外汇与贵金属波动受事件驱动,自动特征仅提高信息覆盖概率,不预示方向,实盘前务必在 MT5 历史数据上回测。

MQL5 / C++
def generate_new_features(data, num_features=class="num">200, random_seed=class="num">1):
    random.seed(random_seed)
    new_features = {}
    for _ in range(num_features):
        feature_name = f&class="macro">#x27;feature_{len(new_features)}&class="macro">#x27;
        col1_idx, col2_idx = random.sample(range(len(data.columns)), class="num">2)
        col1, col2 = data.columns[col1_idx], data.columns[col2_idx]
        operation = random.choice([&class="macro">#x27;add&class="macro">#x27;, &class="macro">#x27;subtract&class="macro">#x27;, &class="macro">#x27;multiply&class="macro">#x27;, &class="macro">#x27;divide&class="macro">#x27;, &class="macro">#x27;shift&class="macro">#x27;, &class="macro">#x27;rolling_mean&class="macro">#x27;, &class="macro">#x27;rolling_std&class="macro">#x27;, &class="macro">#x27;rolling_max&class="macro">#x27;, &class="macro">#x27;rolling_min&class="macro">#x27;, &class="macro">#x27;rolling_sum&class="macro">#x27;])
        if operation == &class="macro">#x27;add&class="macro">#x27;:
            new_features[feature_name] = data[col1] + data[col2]
        elif operation == &class="macro">#x27;subtract&class="macro">#x27;:
            new_features[feature_name] = data[col1] - data[col2]
        elif operation == &class="macro">#x27;multiply&class="macro">#x27;:
            new_features[feature_name] = data[col1] * data[col2]
        elif operation == &class="macro">#x27;divide&class="macro">#x27;:
            new_features[feature_name] = data[col1] / data[col2]
        elif operation == &class="macro">#x27;shift&class="macro">#x27;:
            shift = random.randint(class="num">1, class="num">10)
            new_features[feature_name] = data[col1].shift(shift)
        elif operation == &class="macro">#x27;rolling_mean&class="macro">#x27;:
            window = random.randint(class="num">2, class="num">20)
            new_features[feature_name] = data[col1].rolling(window).mean()
        elif operation == &class="macro">#x27;rolling_std&class="macro">#x27;:
            window = random.randint(class="num">2, class="num">20)
            new_features[feature_name] = data[col1].rolling(window).std()
        elif operation == &class="macro">#x27;rolling_max&class="macro">#x27;:
            window = random.randint(class="num">2, class="num">20)
            new_features[feature_name] = data[col1].rolling(window).max()
        elif operation == &class="macro">#x27;rolling_min&class="macro">#x27;:
            window = random.randint(class="num">2, class="num">20)
            new_features[feature_name] = data[col1].rolling(window).min()
        elif operation == &class="macro">#x27;rolling_sum&class="macro">#x27;:
            window = random.randint(class="num">2, class="num">20)
            new_features[feature_name] = data[col1].rolling(window).sum()
    new_data = pd.concat([data, pd.DataFrame(new_features)], axis=class="num">1)
    print("\nGenerated features:")
    print(new_data[list(new_features.keys())].tail(class="num">100))
    class="kw">return data
random_seed=class="num">42

用高斯混合模型给特征做软分组

把高度相关的指标塞进同一个模型,往往只是堆噪声。特征聚类的用意是把相似维度并到一组,压低冗余、缓解多重共线性,让后续训练不容易过拟合。 比起 k-means 强制球形划分,高斯混合模型(GMM)假设数据由若干个正态分布叠加生成,每个分布即一个聚类,能容纳形状歪斜、边界交叠的特征群,这对价格行为里那些纠缠在一起的动量/波动率类因子更友好。 下面这段 Python 实现直接可用于 MT5 导出的特征表(label 列单独剥离): [CODE] from sklearn.mixture import GaussianMixture def cluster_features_by_gmm(data, n_components=4): X = data.drop(['label', 'labels'], axis=1) X = X.replace([np.inf, -np.inf], np.nan) X = X.fillna(X.median()) gmm = GaussianMixture(n_components=n_components, random_state=1) gmm.fit(X) data['cluster'] = gmm.predict(X) print("\nFeature clusters:") print(data[['cluster']].tail(100)) return data [/CODE] 逐行看:第 3 行去掉标签列,避免把分类目标混进无监督聚类;第 4–5 行把无穷和缺失用中位数填掉,MT5 导出的价差类字段常出 inf;第 6 行设 4 个聚类、random_state=1 保证可复现;第 7 行拟合,第 8 行打回聚类编号;第 9–11 行只打印末 100 行便于快速瞄分布。 实跑时把 n_components 从 3 调到 6 对比一次,若某组聚类内特征数低于 2 个,说明该维度在样本里倾向是孤立噪声,可直接弃用。外汇与贵金属杠杆高、跳空频繁,这类特征筛选只降低过拟合概率,不承诺任何方向收益。

MQL5 / C++
from sklearn.mixture class="kw">import GaussianMixture
def cluster_features_by_gmm(data, n_components=class="num">4):
    X = data.drop([&class="macro">#x27;label&class="macro">#x27;, &class="macro">#x27;labels&class="macro">#x27;], axis=class="num">1)
    X = X.replace([np.inf, -np.inf], np.nan)
    X = X.fillna(X.median())
    gmm = GaussianMixture(n_components=n_components, random_state=class="num">1)
    gmm.fit(X)
    data[&class="macro">#x27;cluster&class="macro">#x27;] = gmm.predict(X)
    print("\nFeature clusters:")
    print(data[[&class="macro">#x27;cluster&class="macro">#x27;]].tail(class="num">100))
    class="kw">return data

「用随机森林筛掉无效特征」

做价格行为模型时,最容易被忽略的一步是特征清洗。把原始聚类的类别标签先摘掉,它们不参与预测变量评估,留着只会干扰选择过程。 核心思路是搬出随机森林:在随机特征子集上建上百棵决策树,训练完后每棵树都会给出特征对分类的贡献度。RFECV 再拿交叉验证(这里 cv=5)反复剔掉最弱变量,直到剩你指定的数量——代码里默认留 10 个。 实测跑下来,用于价格方向预测最顶的特征居然是开盘价本身,移动均线、价格增量、标准差、日/月价差也都排在前排。那些自动生成的花哨特征被证明显得无信息量,直接拖慢训练。 别把正态当圣经 特征重要性高不代表因果,外汇和贵金属波动受事件冲击,随机森林筛出的前十名在另一段行情可能换位,上 MT5 把不同品种的历史都跑一遍再信。 让小布替你跑这套 把下面函数丢进你的 Python 环境,传进打过标签的 DataFrame,终端会打印选中的特征表,复制回 MT5 做指标组合就能少踩很多坑。外汇贵金属杠杆高,模型胜率只是概率倾向,实盘务必轻仓验证。

MQL5 / C++
from sklearn.feature_selection class="kw">import RFECV
from sklearn.ensemble class="kw">import RandomForestClassifier
class="kw">import pandas as pd
def feature_engineering(data, n_features_to_select=class="num">10):
    # Remove the &class="macro">#x27;label&class="macro">#x27; column as it is not a feature
    X = data.drop([&class="macro">#x27;label&class="macro">#x27;, &class="macro">#x27;labels&class="macro">#x27;], axis=class="num">1)
    y = data[&class="macro">#x27;labels&class="macro">#x27;]
    # Create a RandomForestClassifier model
    clf = RandomForestClassifier(n_estimators=class="num">100, random_state=class="num">1)
    # Use RFECV to select n_features_to_select best features
    rfecv = RFECV(estimator=clf, step=class="num">1, cv=class="num">5, scoring=&class="macro">#x27;accuracy&class="macro">#x27;, n_jobs=-class="num">1, verbose=class="num">1,
                  min_features_to_select=n_features_to_select)
    rfecv.fit(X, y)
    # Return a DataFrame with the best features, &class="macro">#x27;label&class="macro">#x27; column, and &class="macro">#x27;labels&class="macro">#x27; column
    selected_features = X.columns[rfecv.get_support(indices=True)]
    selected_data = data[selected_features.tolist() + [&class="macro">#x27;label&class="macro">#x27;, &class="macro">#x27;labels&class="macro">#x27;]]  # Convert selected_features to a list
    # Print the table of best features
    print("\nBest features:")
    print(pd.DataFrame({&class="macro">#x27;Feature&class="macro">#x27;: selected_features}))
    class="kw">return selected_data
labeled_data_engineered = feature_engineering(labeled_data_clustered, n_features_to_select=class="num">10)

◍ 数据集成型后接 Python 训练的路径

整套流程跑完,EURUSD 原始报价先从 MT5 抽出来,再靠加噪、位移、缩放这类随机扰动把样本量做大,否则后面机器学习很容易过拟合。 价格列被打上‘增长 / 下降’的趋势标签,标签生成时把止损止盈一起算进去;冗余样本直接剔掉,类别才算平衡。 特征工程分三步:从时间序列自动扩出几百个衍生变量,用高斯混合聚类抓冗余,再拿随机森林排重要性、筛出信息量最高的子集。下面这段代码就是聚类前丢掉标签列的动作,X 里只留特征。 [CODE] 里那行 X = data.drop(['label','labels'], axis=1) 说明聚类阶段不把监督标签带进去,避免信息泄露。 最终拿到的是一份特征最优、样本厚实的数据集,下一步进 Python 训分类模型、写策略,再回 MT5 集成。外汇与贵金属杠杆高,这套管线只是研究基础,实盘前必须用历史数据交叉验证。

MQL5 / C++
def cluster_features_by_gmm(data, n_components=class="num">4):
    X = data.drop([&class="macro">#x27;label&class="macro">#x27;, &class="macro">#x27;labels&class="macro">#x27;], axis=class="num">1)

常见问题

写循环随机组合价格、成交量等基础量并做数学变换,批量生成几百个候选特征,再靠后续筛选剔除冗余。
软分组让一个特征按概率归属多个簇,保留模糊边界信息,比硬分类更适合处理价格行为里的连续状态。
可以,小布能接入你的特征表直接跑随机森林并标出低重要度特征,省去你手写脚本和反复调参的时间。
看交叉验证曲线,重要度累计到 95% 附近的维度通常够用,再少就盯训练集和验证集落差是否骤增。
卡在格式对齐和缺失值填补,建议预处理阶段就统一时间戳频率和空值策略,否则 Python 端加载必报错。