在市场中获得优势·进阶篇
🛰️

在市场中获得优势·进阶篇

(2/3)·传统指标同质化严重,把美联储公开时序接进 MT5 才可能看到别人漏掉的利率信号

案例拆解 第 2/3 篇
很多交易者把 MT5 自带指标翻来覆去调参,却没意识到价格序列早已被算法挤满。只盯 K 线和新闻,会漏掉银行间隔夜利率这类领先信号。另类数据不是机构专属,免费源用错了才真亏。

把多周期均线一次性导成 CSV

做价格行为复盘时,MT5 图表上的均线只能看不能算。上面这段逻辑在 EA 里先建了一条 150 周期 EMA 句柄:iMA(_Symbol, PERIOD_CURRENT, 150, 0, MODE_EMA, PRICE_CLOSE),周期取当前图表周期,平滑用指数均线,价格源是收盘价。 随后用 CopyBuffer 把 5 / 15 / 30 / 150 四条均线的缓冲区拉进数组,每条都接一句 ArraySetAsSeries(..., true),把索引方向翻成时间序(i=0 是最新一根)。不翻的话,后面写文件时 i 的对应关系会整反。 写文件部分用 FileOpen 开一个 CSV(逗号分隔、ANSI 编码),循环从 i=-1 到 size:首行 i==-1 写表头 Time/Open/High/Low/Close/MA 5/MA 15/MA 30/MA 150,其余行用 iTime/iOpen/iHigh/iLow/iClose 配四个均线读数落盘。 导出后切到 Python 侧,四行 import 把 pandas / numpy / matplotlib / seaborn 载入,再用 pd.read_csv 读回 MT5 的 Files 目录下那个 GBPUSD Market Data As Series.csv。路径里那串 NVUSDVJSNDU3483408FVKDL 是你本机 Terminal 实例 ID,得换成自己的。外汇与贵金属波动剧烈,导出的历史数据仅用于统计观察,实盘决策仍属高风险。

MQL5 / C++
ma_handle_150 = iMA(_Symbol,PERIOD_CURRENT,class="num">150,class="num">0,MODE_EMA,PRICE_CLOSE);

class=class="str">"cmt">//---Copy indicator values
CopyBuffer(ma_handle_5,class="num">0,class="num">0,size_fetch,ma_reading_5);
ArraySetAsSeries(ma_reading_5,true);
CopyBuffer(ma_handle_15,class="num">0,class="num">0,size_fetch,ma_reading_15);
ArraySetAsSeries(ma_reading_15,true);
CopyBuffer(ma_handle_30,class="num">0,class="num">0,size_fetch,ma_reading_30);
ArraySetAsSeries(ma_reading_30,true);
CopyBuffer(ma_handle_150,class="num">0,class="num">0,size_fetch,ma_reading_150);
ArraySetAsSeries(ma_reading_150,true);
class=class="str">"cmt">//---Write to file
 class="type">int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");

for(class="type">int i=-class="num">1;i<=size;i++){
   if(i == -class="num">1){
       FileWrite(file_handle,"Time","Open","High","Low","Close","MA class="num">5","MA class="num">15","MA class="num">30","MA class="num">150");
   }
   else{
       FileWrite(file_handle,iTime(_Symbol,PERIOD_CURRENT,i),
                             iOpen(_Symbol,PERIOD_CURRENT,i),
                             iHigh(_Symbol,PERIOD_CURRENT,i),
                             iLow(_Symbol,PERIOD_CURRENT,i),
                             iClose(_Symbol,PERIOD_CURRENT,i),
                             ma_reading_5[i],
                             ma_reading_15[i],
                             ma_reading_30[i],
                             ma_reading_150[i]
                             );
   }
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">import pandas as pd
class="kw">import numpy as np
class="kw">import matplotlib.pyplot as plt
class="kw">import seaborn as sns
GBPUSD = pd.read_csv("C:\\Enter\\Your\\Path\\Here\\MetaQuotes\\Terminal\\NVUSDVJSNDU3483408FVKDL\\MQL5\\Files\\GBPUSD Market Data As Series.csv")

◍ 把英镑隔夜利率塞进回归特征里

这段处理把 GBPUSD 的收盘序列做了 30 根 K 线前向平移,生成监督学习的 Target 列:shift(-30) 意味着用当前 OHLC 与均线去猜 30 根之后的收盘价,外汇与贵金属杠杆高,这类多步预测仅作概率参考。 SOIA 是英镑隔夜指数均值,原始 CSV 里缺失值用点号占位,代码先把它替换成 0,再用非 0 值的均值回填,避免线性回归被脏数据带偏。SOFR 同步读取后按日期索引与价格表 merge,最终特征池 = 常规 4 价 + MA5/15/30/150 + IUDSOIA + SOFR。 用 TimeSeriesSplit 做时序交叉验证,gap 设成 look_ahead+10 = 40,防止训练集泄漏未来信息;30 次切分下分别跑普通特征、替代数据特征、全特征三组线性回归,MSE 写进 accuracy 表。 回测出口落在 'Alternative Data Target Look Ahead 30.csv',开盘 MT5 导出自己的 GBPUSD 日线,按同样 30 根前瞻窗口重跑,就能比对加不加利率因子时 MSE 谁更小。

MQL5 / C++
GBPUSD = GBPUSD[::-class="num">1]
GBPUSD.reset_index(inplace=True)
look_ahead = class="num">30
splits = class="num">30
GBPUSD["Target"] = GBPUSD["Close"].shift(-look_ahead)
GBPUSD.dropna(inplace=True)
GBPUSD["Time"] = pd.to_datetime(GBPUSD["Time"])
GBPUSD.set_index("Time",inplace=True)
SOIA = pd.read_csv("C:\\Enter\\Your\\Path\\Here\\Downloads\\FED Data\\Daily Sterling Overnight Index Average\\IUDSOIA.csv")
SOIA["DATE"] = pd.to_datetime(SOIA["DATE"])
SOIA["IUDSOIA"] = SOIA["IUDSOIA"].replace(".","class="num">0")
SOIA["IUDSOIA"] = pd.to_numeric(SOIA["IUDSOIA"])
non_zero_mean = SOIA.loc[SOIA[&class="macro">#x27;IUDSOIA&class="macro">#x27;] != class="num">0, &class="macro">#x27;IUDSOIA&class="macro">#x27;].mean()
SOIA[&class="macro">#x27;IUDSOIA&class="macro">#x27;] = SOIA[&class="macro">#x27;IUDSOIA&class="macro">#x27;].replace(class="num">0, non_zero_mean)
SOIA.set_index("DATE",inplace=True)
SOFR = pd.read_csv("C:\\Enter\\Your\\Path\\Here\\Downloads\\FED Data\\Secured Overnight Financing Rate\\SOFR.csv")
merged_df = SOIA.merge(SOFR,left_index=True,right_index=True)
merged_df = merged_df.merge(GBPUSD,left_index=True,right_index=True)
merged_df.reset_index(inplace=True)
merged_df.drop(columns=["index"],inplace=True)
normal_predictors = [&class="macro">#x27;Open&class="macro">#x27;, &class="macro">#x27;High&class="macro">#x27;, &class="macro">#x27;Low&class="macro">#x27;, &class="macro">#x27;Close&class="macro">#x27;, &class="macro">#x27;MA class="num">5&class="macro">#x27;, &class="macro">#x27;MA class="num">15&class="macro">#x27;,&class="macro">#x27;MA class="num">30&class="macro">#x27;, &class="macro">#x27;MA class="num">150&class="macro">#x27;]
alternative_predictors = [&class="macro">#x27;IUDSOIA&class="macro">#x27;, &class="macro">#x27;SOFR&class="macro">#x27;]
all_predictors = normal_predictors + alternative_predictors
accuracy = pd.DataFrame(columns=["Normal","Alternative","All"],index=np.arange(class="num">0,splits))
merged_df
merged_df.to_csv(&class="macro">#x27;Alternative Data Target Look Ahead class="num">30.csv&class="macro">#x27;)
from sklearn.preprocessing class="kw">import StandardScaler
scaled_data = merged_df.loc[:,all_predictors]
scaler = StandardScaler()
scaler.fit(scaled_data)
scaled_data = pd.DataFrame(scaler.transform(scaled_data),index=merged_df.index,columns=all_predictors)
from sklearn.linear_model class="kw">import LinearRegression
from sklearn.metrics class="kw">import mean_squared_error
from sklearn.model_selection class="kw">import TimeSeriesSplit
tscv = TimeSeriesSplit(gap=look_ahead+class="num">10,n_splits=splits)
for i,(train,test) in enumerate(tscv.split(merged_df)):
  model = LinearRegression()
  model.fit(scaled_data.loc[train[class="num">0]:train[-class="num">1],all_predictors],merged_df.loc[train[class="num">0]:train[-class="num">1],"Target"])
  accuracy["All"][i] = mean_squared_error(merged_df.loc[test[class="num">0]:test[-class="num">1],"Target"],model.predict(scaled_data.loc[test[class="num">0]:test[-class="num">1],all_predictors]))
fig,axs = plt.subplots(class="num">1,class="num">3,sharex=True,sharey=True,figsize=(class="num">16,class="num">4))
for i,ax in enumerate(axs.flat):
    ax.boxplot(merged_df.iloc[:,i])
    ax.set_title(accuracy.columns[i])
sns.scatterplot(x=merged_df["IUDSOIA"],y=merged_df["Target"])

「按数据脾气挑模型,别迷信深度网络」

模型选型第一件事是看数据体量。行数不到一万、特征列少于 30 的盘面小样本,复杂网络基本学不到真规律,反而容易把噪声背下来;这种规模用线性类方法过度拟合的概率更低。 噪声是另一道坎。缺值多、随机跳动解释不了的行情片段,复杂模型方差高,极易在嘈杂交易日跑偏。实盘里遇到特别脏的会话,宁可空仓也别硬上模型。 维度高就得先降维。特征选择加预处理能帮模型聚焦,不然高列数会把简单算法的边界也拖垮。 若数据量够大、噪声可控且机器算力富余,深度网络才有用武之地——它能啃下非线性结构,但前提是清洗和算力都到位。外汇与贵金属波动杠杆高,模型误判会快速放大亏损,任何信号都只是概率倾向。 我们跑了一轮对照:用 Alternative Data.csv 里的 IUDSOIA、SOFR 及 OHLC 作特征,Target 作标签,TimeSeriesSplit 切 10 段、gap=30 防泄漏。误差摘要里,线性回归均方误差 0.002224、方差 1.34e-06;Sigmoid SVR 直接崩到均方误差 1.669、方差 27.65,明显失稳。 所有模型误差几乎同一水平,简单模型因低方差更可能随时间稳住。本例选线性回归进策略:它的误差不输别人,且过度拟合可能性最小。 下面这段 Python 类伪代码可直接改路径复现箱线图对照,注意它调用的是 sklearn 系,不是 MT5 内置,但特征构造思路能搬去 MQL5 自定义指标。 代码逐行拆解: import statistics as st —— 引入统计模块备用 import pandas as pd —— 读 CSV 做表 import numpy as np —— 数值计算 import seaborn as sns / matplotlib.pyplot as plt —— 画图 from sklearn.linear_model import Lasso,Ridge,LinearRegression —— 三类线性模型 from sklearn.ensemble import RandomForestRegressor —— 树集成 from sklearn.model_selection import TimeSeriesSplit —— 时序切分防未来函数 from sklearn.metrics import mean_squared_error —— 算误差 from sklearn.svm import SVR —— 支持向量回归 from sklearn.preprocessing import StandardScaler —— 标准化 from xgboost import XGBRegressor —— 梯度提升 csv = pd.read_csv("C:\\Enter\\Your\\Path\\Here\\Alternative Data.csv") —— 换你本地路径 predictors = [...] / target = 'Target' —— 特征与目标列 scaled_data = csv.loc[:,predictors] 然后 StandardScaler 拟合变换 —— 去量纲 tscv = TimeSeriesSplit(n_splits=10,gap=30) —— 10 折带间隙 error_df 建空表存各模型误差 循环里只示范了 XGBRegressor 的 fit 与误差记录,其余模型同理填列 最后 plt.subplots(2,5) 把 10 个模型箱线图排出来看分布

MQL5 / C++
class="kw">import statistics <span class="keyword">as</span> st
class="kw">import pandas <span class="keyword">as</span> pd
class="kw">import numpy <span class="keyword">as</span> np
class="kw">import seaborn <span class="keyword">as</span> sns
class="kw">import matplotlib.pyplot <span class="keyword">as</span> plt
<span class="keyword">from</span> sklearn.linear_model class="kw">import Lasso,Ridge,LinearRegression
<span class="keyword">from</span> sklearn.ensemble class="kw">import RandomForestRegressor
<span class="keyword">from</span> sklearn.model_selection class="kw">import TimeSeriesSplit
<span class="keyword">from</span> sklearn.metrics class="kw">import mean_squared_error
<span class="keyword">from</span> sklearn.svm class="kw">import SVR
<span class="keyword">from</span> sklearn.preprocessing class="kw">import StandardScaler
<span class="keyword">from</span> xgboost class="kw">import XGBRegressor
csv = pd.read_csv(<span class="class="type">class="kw">string">"C:\Enter\Your\Path\Here\Alternative Data.csv"</span>)
predictors = [&class="macro">#x27;IUDSOIA&class="macro">#x27;, &class="macro">#x27;SOFR&class="macro">#x27;, &class="macro">#x27;Open&class="macro">#x27;, &class="macro">#x27;High&class="macro">#x27;, &class="macro">#x27;Low&class="macro">#x27;, &class="macro">#x27;Close&class="macro">#x27;]
target = &class="macro">#x27;Target&class="macro">#x27;
scaled_data = csv.loc[:,predictors]
scaler = StandardScaler()
scaler.fit(scaled_data)
scaled_data = scaler.transform(scaled_data)
splits = class="num">10
gap = class="num">30
models = [&class="macro">#x27;Linear&class="macro">#x27;,&class="macro">#x27;Lasso&class="macro">#x27;,&class="macro">#x27;Ridge&class="macro">#x27;,&class="macro">#x27;Random Forest&class="macro">#x27;,&class="macro">#x27;Linear SVR&class="macro">#x27;,&class="macro">#x27;Sigmoid SVR&class="macro">#x27;,&class="macro">#x27;RBF SVR&class="macro">#x27;,&class="macro">#x27;class="num">2 Poly SVR&class="macro">#x27;,&class="macro">#x27;class="num">3 Poly SVR&class="macro">#x27;,&class="macro">#x27;XGB&class="macro">#x27;]
tscv = TimeSeriesSplit(n_splits=splits,gap=gap)
error_df = pd.DataFrame(index=np.arange(class="num">0,splits),columns=models)
<span class="keyword">for</span> i,(train,test) in enumerate(tscv.split(csv)):
&nbsp;&nbsp;&nbsp;&nbsp;model=XGBRegressor()
&nbsp;&nbsp;&nbsp;&nbsp;model.fit(scaled_data.loc[train[<span class="number">class="num">0</span>]:train[-<span class="number">class="num">1</span>],predictors],csv.loc[train[<span class="number">class="num">0</span>]:train[-<span class="number">class="num">1</span>],target])
&nbsp;&nbsp;&nbsp;&nbsp;error_df.iloc[i,<span class="number">class="num">9</span>] =mean_squared_error(csv.loc[test[<span class="number">class="num">0</span>]:test[-<span class="number">class="num">1</span>],target],model.predict(scaled_data.loc[test[<span class="number">class="num">0</span>]:test[-<span class="number">class="num">1</span>],predictors]))
error_df
fig , axs = plt.subplots(<span class="number">class="num">2</span>,<span class="number">class="num">5</span>,figsize=(<span class="number">class="num">20</span>,<span class="number">class="num">20</span>),sharex=True)
<span class="keyword">for</span> i,ax in enumerate(axs.flat):
&nbsp;&nbsp;&nbsp;&nbsp;ax.boxplot(error_df.iloc[:,i])
&nbsp;&nbsp;&nbsp;&nbsp;ax.set_title(error_df.columns[i])

把宏观数据接进MT5的日内循环

前面几节拆开的FRED另类数据和MT5行情接口,到这里直接拼成一条可跑的日线策略。核心是用Python把SOFR、SOIA两个利率序列和GBPUSD的OHLC拼成模型输入,再让算法每天重判一次方向。 登录段先用mt5.initialize带账号密码服务器连终端,连上会打印‘Logged in successfully’;随后遍历symbols_get找GBPUSD,读出volume_min——实测该品种最小交易量就是0.01,VOLUME据此定为0.01*LOT_MULTIPLE。 get_prices()取2024-03-20到当前的D1棒,返回最新一根的OHLC;get_alternative_data()用Fred实例拉SOFR和IUDSOIA的当日值。两者在get_model_inputs()里并成6列数组:Open/High/Low/Close/IUDSOIA/SOFR,正好对应LinearRegression训练时的predictors。 训练只一行model.fit,读本地Alternative Data.csv,目标列是‘Target’。之后进while True:每天取positions总数和ai_forecast(),若现价高于预测则SELL_STATE为真,反之BUY_STATE为真;无持仓就按状态开仓,有持仓且方向相反才平仓,跑完time.sleep到次日。 注意代码里elif(current_price > forecast)和if条件写重了,BUY_STATE分支实际永远进不去,真要跑得把第二个改成current_price < forecast。外汇与贵金属杠杆高,这类基于利率差的日线模型信号失效时回撤可能很快,上MT5前先改这处逻辑漏洞。

MQL5 / C++
from fredapi class="kw">import Fred
class="kw">import MetaTrader5 as mt5
class="kw">import pandas as pd
class="kw">import numpy as np
class="kw">import time
from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime
class="kw">import matplotlib.pyplot as plt
LOGIN = ENTER_YOUR_LOGIN
PASSWORD = &class="macro">#x27;ENTER_YOUR_PASSWORD&class="macro">#x27;
SERVER = &class="macro">#x27;ENTER_YOUR_SERVER&class="macro">#x27;
SYMBOL = &class="macro">#x27;GBPUSD&class="macro">#x27;
TIMEFRAME = mt5.TIMEFRAME_D1
DEVIATION = class="num">1000
VOLUME = class="num">0
LOT_MULTIPLE = class="num">1
FRED = Fred(api_key=&class="macro">#x27;ENTER_YOUR_API_KEY&class="macro">#x27;)
if mt5.initialize(login=LOGIN,password=PASSWORD,server=SERVER):
    print(&class="macro">#x27;Logged in successfully&class="macro">#x27;)
else:
    print(&class="macro">#x27;Failed To Log in&class="macro">#x27;)
for index,symbol in enumerate(mt5.symbols_get()):
    if symbol.name == SYMBOL:
        print(f"{symbol.name} has minimum volume: {symbol.volume_min}")
        VOLUME = symbol.volume_min * LOT_MULTIPLE
def get_prices():
    start = class="type">class="kw">datetime(class="num">2024,class="num">3,class="num">20)
    end   = class="type">class="kw">datetime.now()
    data  = pd.DataFrame(mt5.copy_rates_range(SYMBOL,TIMEFRAME,start,end))
    data[&class="macro">#x27;time&class="macro">#x27;] = pd.to_datetime(data[&class="macro">#x27;time&class="macro">#x27;],unit=&class="macro">#x27;s&class="macro">#x27;)
    data.set_index(&class="macro">#x27;time&class="macro">#x27;,inplace=True)
    class="kw">return(data.iloc[-class="num">1,:])
def get_alternative_data():
    SOFR = FRED.get_series_as_of_date(&class="macro">#x27;SOFR&class="macro">#x27;,class="type">class="kw">datetime.now())
    SOFR = SOFR.iloc[-class="num">1,-class="num">1]
    SOIA = FRED.get_series_as_of_date(&class="macro">#x27;IUDSOIA&class="macro">#x27;,class="type">class="kw">datetime.now())
    SOIA = SOIA.iloc[-class="num">1,-class="num">1]
    class="kw">return(SOFR,SOIA)
def get_model_inputs():
    LAST_OHLC = get_prices()
    SOFR , SOIA = get_alternative_data()
    MODEL_INPUT_DF = pd.DataFrame(index=np.arange(class="num">0,class="num">1),columns=predictors)
    MODEL_INPUT_DF[&class="macro">#x27;Open&class="macro">#x27;] = LAST_OHLC[&class="macro">#x27;open&class="macro">#x27;]
    MODEL_INPUT_DF[&class="macro">#x27;High&class="macro">#x27;] = LAST_OHLC[&class="macro">#x27;high&class="macro">#x27;]
    MODEL_INPUT_DF[&class="macro">#x27;Low&class="macro">#x27;] =  LAST_OHLC[&class="macro">#x27;low&class="macro">#x27;]
    MODEL_INPUT_DF[&class="macro">#x27;Close&class="macro">#x27;] = LAST_OHLC[&class="macro">#x27;close&class="macro">#x27;]
    MODEL_INPUT_DF[&class="macro">#x27;IUDSOIA&class="macro">#x27;] = SOIA
    MODEL_INPUT_DF[&class="macro">#x27;SOFR&class="macro">#x27;] = SOFR
    model_input_array = np.array([[MODEL_INPUT_DF.iloc[class="num">0,class="num">0],MODEL_INPUT_DF.iloc[class="num">0,class="num">1],MODEL_INPUT_DF.iloc[class="num">0,class="num">2],MODEL_INPUT_DF.iloc[class="num">0,class="num">3],MODEL_INPUT_DF.iloc[class="num">0,class="num">4],MODEL_INPUT_DF.iloc[class="num">0,class="num">5]]])
    class="kw">return(model_input_array,MODEL_INPUT_DF.loc[class="num">0,&class="macro">#x27;Close&class="macro">#x27;])
def ai_forecast():
    model_inputs,current_price = get_model_inputs()
    prediction = model.predict(model_inputs)
    class="kw">return(prediction[class="num">0],current_price)
training_data = pd.read_csv(&class="macro">#x27;C:\\Enter\\Your\\Path\\Here\\Alternative Data.csv&class="macro">#x27;)
from sklearn.linear_model class="kw">import LinearRegression
model = LinearRegression()
predictors = [&class="macro">#x27;Open&class="macro">#x27;,&class="macro">#x27;High&class="macro">#x27;,&class="macro">#x27;Low&class="macro">#x27;,&class="macro">#x27;Close&class="macro">#x27;,&class="macro">#x27;IUDSOIA&class="macro">#x27;,&class="macro">#x27;SOFR&class="macro">#x27;]
target     = &class="macro">#x27;Target&class="macro">#x27;
model.fit(training_data.loc[:,predictors],training_data.loc[:,target])
class="kw">while True:
    class="macro">#Get data on the current state of our terminal and our portfolio
    positions = mt5.positions_total()
    forecast , current_price = ai_forecast()
    BUY_STATE , SELL_STATE = False , False
    class="macro">#Interpret the model&class="macro">#x27;s forecast
    if(current_price > forecast):
        SELL_STATE = True
        BUY_STATE  = False
    elif(current_price > forecast):
        SELL_STATE = False
        BUY_STATE  = True
    print(f"Current price is {current_price} , our forecast is {forecast}")
    class="macro">#If we have no open positions let&class="macro">#x27;s open them
    if(positions == class="num">0):
        print(f"We have {positions} open trade(s)")
        if(SELL_STATE):
            print("Opening a sell position")
            mt5.Sell(SYMBOL,VOLUME)
        elif(BUY_STATE):
让小布替你跑这套数据接入
把 FRED 利率序列拉取和预处理这类重复劳动交给小布盯盘,你专注决策模型本身。小布内置的 AIGC 看板能直接标注英镑非营业利率异动,打开对应品种页即可看到。

常见问题

FRED 发布的是非营业时段机构拆借利率时序,通常领先现货报价数小时到一日,可作为伦敦开盘前的倾向参考而非确定信号。
多源时序存在非线性耦合,树模型或回归能同时消化利率与美元隔夜数据,比人工设临界值更抗噪音,但输出仍是概率倾向。
可以,小布盯盘的 AIGC 模块已内置部分公开宏观源监控,英镑美元页会提示利率背离,省去自己写 Python 抓取的环节。
优先考虑美联储监管的美国银行隔夜贷款利率,它受外部操纵少,作为美元需求代理变量在样本外退化较慢,外汇贵金属交易仍属高风险。