
利用Python进行季节性过滤并为EA的ONNX深度学习模型选择时间周期
简介
在阅读了《利用外汇市场季节性因素获利》一文后,我决定撰写另一篇文章,对比加入季节性因素与未加入季节性因素的交易策略(Expert Advisor,简称EA),以探究季节性因素是否能带来优势。
我早已知晓市场会受到季节性因素的影响。当我了解到马克·扎克伯格用一位投资者的资金为Facebook提供资金时,这一点变得尤为明显。这位投资者曾用他在Bar Mitzvah(犹太男孩13岁时的成年礼)收到的钱投资石油股票,预测加勒比海地区预期的飓风会导致油价上涨。他分析了天气预报,指出那段时间将有恶劣天气。
我非常自豪且有兴趣撰写这篇文章,旨在探讨市场和季节性因素是相互关联的。将两者结合起来的一个好方法是将它们融入一个EA中,但我们已经有一篇关于此的文章,链接如下:如何在mql5中集成ONNX模型的示例。
首先,我们将使用EA对比有数据过滤和无数据过滤的模型,以观察数据过滤是否会产生影响。之后,我们将通过图表讨论季节性因素,并以2024年2月为例,展示加入和未加入季节性因素的实际效果。在文章的最后部分(我认为非常有趣),我们将讨论对已有EA的其他方法:如何在MQL5中使用ONNX模型,并探讨我们是否可以通过微调这些EA和ONNX模型来获得优势。我现在就可以告诉你,答案是肯定的,我们可以。
为了实现这一目标,我们将首先使用以下脚本下载数据(所有价格变动):从某个交易品种下载所有数据。我们只需将该脚本添加到需要研究的交易品种图表上,一段时间后(不到一小时),我们就能在该交易品种的“文件”文件夹中下载到所有历史价格变动数据。
当我们下载完所有价格变动数据后,我们将处理该CSV文件,并仅提取我们所需或想要的数据(在本例中,为2015年1月至2023年2月期间的数据)。
季节性
交易中的季节性主要是指识别资产价格在一年中可预测地发生的规律性起伏。这就像认识到某些股票在特定时间往往表现更好一样。让我们来详细探讨一下这个概念。
理解交易中的季节性:
- 定义:季节性是指注意到价格如何根据一年中的时间以重复的模式波动。这可能与实际季节(如夏季或冬季)、商业季节(如节日购物旺季)甚至特定月份有关。
- 示例:聪明的投资者会关注这些模式,因为它们通常可靠且有利可图。以下是几个例子:
- 与天气相关的季节性:就像天气影响农作物的生长季节一样,它也影响着商品价格和相关股票。例如,一家销售海滩装备的公司可能在夏季销量大增,但在较冷的月份销量下降,从而影响其股价。
- 节日季节性:零售股在节日购物热潮期间通常会上涨。那些依赖节日销售的公司,如礼品店,往往在这段时间内表现出色。
- 季度收益季节性:上市公司每季度公布收益,而这些季节期间其股价的反应往往是可预测的。
- 税收季节性:与税收相关的事件可能会扰乱市场,特别是对于那些与金融相关的行业。
- 自然周期:像旅游或能源等行业有自己的季节性需求模式,如夏季度假或冬季供暖需求。
基于季节性的交易策略:
- 交易者可以通过以下几种方式利用季节性:
- 识别季节性模式:深入研究过去的数据,以发现一年中重复出现的趋势。
- 把握交易时机:根据这些季节性趋势进行买卖操作。
- 把握交易时机:根据这些季节性趋势进行买卖操作。
- 板块轮动:在一年中不同时间表现更好的板块之间进行投资切换。
过滤数据
我们采用低通滤波器。根据维基百科:
低通滤波器是一种滤波器,它允许频率低于选定截止频率的信号通过,并衰减频率高于截止频率的信号。滤波器的确切频率响应取决于滤波器的设计。在音频应用中,该滤波器有时被称为高频消减滤波器或高音削减滤波器。低通滤波器是高通滤波器的互补滤波器。
在算法交易中,我们为什么选择低通滤波器而不是高通滤波器?算法交易中对低通滤波器的偏好源于以下几个关键优势:- 信号平滑:低通滤波器能够有效地平滑掉价格波动中的噪声波动,强调长期趋势而非短期波动。
- 减少高频噪声:它们有助于衰减高频噪声,这些噪声对于交易策略可能不提供有意义的信息。
- 降低交易成本:通过关注长期趋势,低通滤波器可以导致更少、更具战略性的交易,从而可能降低交易成本。
- 更好的风险管理:低通滤波器有助于制定更稳定、可预测的交易策略,减少短期市场波动的影响。
- 与投资期限相符:它们非常适合具有长期投资期限的策略,能够有效地捕捉长期趋势。
我个人使用低通滤波器来过滤高频信号。在这里使用高通滤波器没有太多意义。
这是我们将要使用的(注:我在文章最后一部分将顺序(order)和截止频率(cutoff_frequency)参数更改为0.1的截止频率和顺序等于1,因为这样的设置最终给出了更好的结果。此外,用于滤波的正确.py文件是文章最后一部分中提到的那些(在那里,我不仅使用了更好的参数,还使用了minmaxscaler进行拟合和逆变换)。
# Low-pass filter parameters cutoff_frequency = 0.01 # Cutoff frequency as a proportion of the Nyquist frequency order = 4 # Apply the low-pass filter def butter_lowpass_filter(data, cutoff, fs, order): nyquist = 0.5 * fs normal_cutoff = cutoff / nyquist b, a = butter(order, normal_cutoff, btype='low', analog=False) print("Filter coefficients - b:", b) print("Filter coefficients - a:", a) y = lfilter(b, a, data) return y filtered_data_low = butter_lowpass_filter(df2['close'], cutoff_frequency, fs=1, order=order)
我们将使用“onnx_LSTM_simple_filtered.py”和“onnx_LSTM_simple_not_filtered.py”来制作ONNX模型并进行比较。
注意:我使用了具有不同低通滤波器参数的模型v1和模型v2。
结果如下:
我们对这些EA采用相同的输入参数
研究的时间段将从2月1日到3月1日。
对于未经过滤的模型:
RMSE : 0.0010798043714784716 MSE : 1.165977480664017e-06 R2 score : 0.8799146678247277
过滤 v1
# Parámetros del filtro pasa bajo cutoff_frequency = 0.01 # Frecuencia de corte en proporción a la frecuencia de Nyquist order = 4
RMSE : 0.0010228999869332884 MSE : 1.0463243832681218e-06 R2 score : 0.8922378749062259
过滤 v2
cutoff_frequency = 0.1 # Frecuencia de corte en proporción a la frecuencia de Nyquist order = 2
RMSE : 0.0010899163515744447 MSE : 1.1879176534293484e-06 R2 score : 0.8775550550819025
关于过滤的结论
因此,没错。使用过滤是很方便的。
使用的代码和模型:
- ONNX.eurusd.H1.120.Prediction_FILTERED.mq5
- ONNX.eurusd.H1.120.Prediction_FILTERED_v2.mq5 ONNX.eurusd.H1.120.Prediction_NOT_FILTERED.mq5
- onnx_LSTM_simple_EURUSD_filtered.py onnx_LSTM_simple_EURUSD_not_filtered.py
- 下载一个货币对的所有数据 EURUSD_LSTM_120_1h_not_filtered.onnx EURUSD_LSTM_120_1h_filtered_v1.onnx EURUSD_LSTM_120_1h_filtered_v2.onnx
货币对们有季节性规律吗?
对于这一部分,我们首先将通过图表来观察,我们将获取从2015年至2023年2月份的数据,并将其添加到图表中,以查看这些数据在那些周如何波动。
这是我们在那个时间段内可以观察到的:
结论:
我们可以看到它有一些趋势,或者至少我们没有看到一条水平的黑线(总和线)。每条线之间都有间隔,因为该符号全年都在交易,其价格会波动。这就是为什么在文章的下一部分中,我们将按年份将所有货币对的2月份数据连接起来,并且这就是为什么我们需要使用过滤器的原因。过滤器可以防止高频数据(例如,从2022年的最后一天到2023年2月的第一天)的传递,并且当AI训练时(例如,训练星期五的收盘价和星期一的开盘价),它不会研究这些变化,而是寻求更平滑的数据。
使用的脚本和数据:
这些货币对的数据之间有相关性吗?
自相关是数据的一种特性,它显示了连续时间间隔内数值之间的相似程度。
接近1的值表示存在很大的正相关。
以下是我们使用autocorrelation.py获得的结果。
[1. 0.99736147 0.99472432 0.99206626 0.98937664 0.98671649 0.98405706 0.98144222 0.9787753 0.97615525 0.97356318 0.97099777 0.96848029 0.96602671 0.96360361 0.96113539 0.95865344 0.95615626 0.95362417 0.95108177 0.94854957 0.94599045 0.94346076 0.94091564 0.93837742 0.93583734 0.9332909 0.93074655 0.92826504 0.92579028 0.92330505 0.92084645 0.91834403 0.91581296 0.91328091 0.91076099 0.90826447]
为二月创建ONNX模型
对于这项任务,我们只需将所有数据合并到一个CSV文件中,并用它来创建一个模型。
我们将使用创建的concat_seasonal.py脚本来生成一个CSV文件,该文件将被添加到zip文件seasonal_feb_concat中。然后,我们将使用onnx_LSTM_..._seasonals.py脚本来训练和创建模型。
使用的脚本和数据(全部附在下面):
季节性模型的测试结果以及与120天(1小时)过滤模型的比较。
季节性模型
RMSE : 0.013137568368684325 MSE : 0.00017259570264185493 R2 score : 0.7166764010650979
尽管并不惊人,但总体来看结果还不错(夏普比率为负值的并不多)。
如果我们把它同带过滤的模型相比较,
我发现一个有趣的现象是,在过滤模型的优化结果中,负夏普比率值占据了表格的一半,而在季节性模型中,负夏普比率值只占据了表格的五分之一左右。这一点值得注意,因为即使r2值较低,它似乎也是一个能够产生盈利回报的稳健模型。
我本来也可以测试不使用止损(SL)和止盈(TP)的EA,但我认为在EA中始终使用它们是一种更好的做法。
所使用的代码和ONNX模型(均附于下方):
ONNX.eurusd.H1.120.Prediction_seasonal.mq5 EURUSD_LSTM_270_1h_filtered_seasonal0.72.onnx onnx_LSTM_simple_EURUSD_concat_seasonals.py
使用什么时间周期呢?
对于文章的这一部分,我已经对过滤器进行了微调,以使其对EURUSD产生更好的结果。
cutoff_frequency = 0.1 # Frecuencia de corte en proporción a la frecuencia de Nyquist order = 1
我还修改了过滤器:
from sklearn.preprocessing import MinMaxScaler from scipy.signal import butter, lfilter # Parámetros del filtro pasa bajo cutoff_frequency = 0.1 # Frecuencia de corte en proporción a la frecuencia de Nyquist order = 1 # Aplicar filtro pasa bajo def butter_lowpass_filter(data, cutoff, fs, order): nyquist = 0.5 * fs normal_cutoff = cutoff / nyquist b, a = butter(order, normal_cutoff, btype='low', analog=False) print("Coeficientes del filtro - b:", b) print("Coeficientes del filtro - a:", a) y = lfilter(b, a, data) return y scaled = MinMaxScaler(feature_range=(0,1)) valores_df1 = df.filter(['close']).values valores_df1 = pd.DataFrame(valores_df1) x_features1 = valores_df1[[0]] valores_df2 = x_features1.values data_escalada = scaled.fit_transform(valores_df2) print(data_escalada) filtered_data_low = butter_lowpass_filter(data_escalada, cutoff_frequency, fs=1, order=order) print("filtered_data_low",filtered_data_low) filtered_data_low_unscaled = scaled.inverse_transform(filtered_data_low)
订单的价格必须四舍五入到整数。
该策略针对1小时的时间间隔,在2024年2月进行了为期一个月的测试。对于30分钟的时间间隔,测试从2月1日至2月15日进行,以此类推。
对于15分钟的时间间隔,我分别进行了有过滤器和无过滤器的测试。结果表明,使用经过微调的过滤器可以获得更好的结果(至少是总体上的)。
15分钟带过滤器(夏普比率)
15分钟不带过滤器
30分钟带过滤器(现在开始我将一直使用过滤器)
LSTM.30m.EURUSD.120.0.94.onnx LTSM_simple_30m_filtrado.py ONNX.eurusd.H1.120.30m_eurusd.mq5
1小時
1 hour files.zip (815.64 KB)
2 小时
由于我不能使用2天的时间周期,我使用一天的周期(用于EA中接下来几天的值)。
我无法加载超过32个文件,剩下的文件将打包在文件夹中上传。
结论
似乎使用这种策略时,随着时间周期的加大,对于所有盈利水平(TPs)和止损水平(SLs)来说,结果看起来更加稳健。
NextDays变量
我采用了这篇文章中的策略:如何在mql5中使用ONNX模型5,但由于我在使用2小时策略时取得了不错的结果,并且在该策略中我使用了1天周期接下来,我们将研究“NextDays”变量的其他取值。
if(TimeCurrent()>=ExtNextDay) { GetMinMax(); //--- set next day time ExtNextDay=TimeCurrent(); ExtNextDay-=ExtNextDay%PeriodSeconds(PERIOD_D1); ExtNextDay+=PeriodSeconds(PERIOD_D1); }
void GetMinMax(void) { vectorf close; close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE); ExtMin=close.Min(); ExtMax=close.Max(); }
我们现在将研究使用30分钟周期和过滤数据的EURUSD,并尝试不同的NextDay周期(我们将使用1天、12小时和6小时),然后讨论结果。
1天30分钟周期
12小时30分钟周期
6小时30分钟周期
当微调NextDay变量后结果看上去更好。让我们看看在较短的NextDay周期变量下情况如何。
NextDay为4小时的30分钟周期
NextDay为2小时的30分钟周期
NextDay为1小时的30分钟周期
看起来,在至少30分钟的周期内,大约8到12个30分钟的K线(或称为柱形图)能给出更好的结果。
由于这个“游戏”的目的是赢得更多的钱,而实现这一目标的方法之一就是拥有更多的盈利交易和一个稳健的策略。让我们看看是否可以利用这个起点,在5分钟的周期内获胜。因此,我们将尝试这个策略,将NextDay变量的周期设置为5分钟、1小时和30分钟。
NextDay变量为1小时的5分钟周期
NextDay变量为30分钟的5分钟周期
其他时间周期似乎更加可靠,但是,如果你想要另一个EA,你有更多的选择可以使用。
最后,我们可以对策略进行调整,以期获得更好的结果或更稳定的结果。例如,在1小时的周期内,我们可以设定时间或K线数量的限制。对于使用次日12小时周期,我们可以规定一旦订单成交,持有时间不得超过12小时。
if(ExtPredictedClass>=0) { if(PositionSelect(_Symbol)) CheckForClose(); else { CheckForOpen(); started_time1 = rates[0].time; string started_time1_str = TimeToString(started_time1);
int total = PositionsTotal(); if(total>0) { datetime started_time2 = rates[0].time; string started_time2_str = TimeToString(started_time2); int segundos = started_time2 - started_time1; Print("Tiempo 1: ", started_time1_str); Print("Tiempo 2: ", started_time2_str); Print("Diferencia en segundos: ", segundos); if((segundos >= days*PeriodSeconds(PERIOD_H12)) && segundos != 0) { Print("closing position----------------"); ExtTrade.PositionClose(_Symbol,3); } }
ONNX.eurusd.120.1h_H12_eurusd.v3.mq5 (9.23 KB)
结论
我们已经进行了调整、筛选和模型对比,尝试了不同的EA和参数设置,试图通过微调来获得更好的结果。我希望你阅读这篇文章时感到愉快,就像我撰写它时一样享受这个过程。
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/14424

