feat: 自研突破交易策略

This commit is contained in:
zhangyangbin
2026-05-16 13:51:59 +08:00
parent 87572f3b10
commit 56eb1908dc
13 changed files with 1409 additions and 0 deletions
+306
View File
@@ -0,0 +1,306 @@
//+------------------------------------------------------------------+
//| BreakoutMarker.mq5 |
//| 突破极值点标记EA |
//| 用于标记突破策略中的极值点位置 |
//+------------------------------------------------------------------+
#property copyright "Breakout Strategy"
#property version "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2
//--- 绘图设置
#property indicator_label1 "高点极值"
#property indicator_type1 DRAW_ARROW
#property indicator_color1 clrRed
#property indicator_width1 3
#property indicator_label2 "低点极值"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrLime
#property indicator_width2 3
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
input int InpMAPeriod = 14; // MA周期
input int InpWaveThreshold = 1000; // 波段阈值(点数)
input bool InpShowDebugInfo = true; // 显示调试信息
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
int ma_handle; // MA指标句柄
double HighExtremeBuffer[]; // 高点极值缓冲区
double LowExtremeBuffer[]; // 低点极值缓冲区
//+------------------------------------------------------------------+
//| 自定义指标初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
// 创建MA指标
ma_handle = iMA(_Symbol, PERIOD_CURRENT, InpMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE) {
Print("创建MA指标失败");
return(INIT_FAILED);
}
// 设置指标缓冲区
SetIndexBuffer(0, HighExtremeBuffer, INDICATOR_DATA);
SetIndexBuffer(1, LowExtremeBuffer, INDICATOR_DATA);
// 设置箭头代码
PlotIndexSetInteger(0, PLOT_ARROW, 234); // 下箭头(标记高点)
PlotIndexSetInteger(1, PLOT_ARROW, 233); // 上箭头(标记低点)
PlotIndexSetInteger(0, PLOT_ARROW_SHIFT, -10);
PlotIndexSetInteger(1, PLOT_ARROW_SHIFT, 10);
// 设置数组为时间序列
ArraySetAsSeries(HighExtremeBuffer, true);
ArraySetAsSeries(LowExtremeBuffer, true);
// 设置指标名称
IndicatorSetString(INDICATOR_SHORTNAME, "突破极值点标记");
Print("突破极值点标记EA初始化成功");
Print("参数 - MA周期:", InpMAPeriod, ", 波段阈值:", InpWaveThreshold, "点");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 自定义指标反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
IndicatorRelease(ma_handle);
Print("突破极值点标记EA已卸载");
}
//+------------------------------------------------------------------+
//| 自定义指标迭代函数 |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(InpShowDebugInfo && prev_calculated == 0)
Print("OnCalculate 首次调用 - rates_total:", rates_total);
if(rates_total < InpMAPeriod + 2) {
if(InpShowDebugInfo)
Print("K线数量不足: ", rates_total);
return(0);
}
// 设置数组为时间序列
ArraySetAsSeries(time, true);
ArraySetAsSeries(open, true);
ArraySetAsSeries(high, true);
ArraySetAsSeries(low, true);
ArraySetAsSeries(close, true);
// 获取MA数据
double ma_array[];
ArraySetAsSeries(ma_array, true);
int copied = CopyBuffer(ma_handle, 0, 0, rates_total, ma_array);
if(copied <= 0) {
if(InpShowDebugInfo)
Print("复制MA数据失败,返回值:", copied);
return(0);
}
if(InpShowDebugInfo && prev_calculated == 0)
Print("MA数据复制成功,数量:", copied);
// 初始化缓冲区
ArrayInitialize(HighExtremeBuffer, 0);
ArrayInitialize(LowExtremeBuffer, 0);
// 识别所有突破K线
int breakout_bars[];
int breakout_types[];
ArrayResize(breakout_bars, 0);
ArrayResize(breakout_types, 0);
for(int i = rates_total - InpMAPeriod - 1; i >= 1; i--) {
int breakout_type = CheckBreakout(i, open, close, ma_array);
if(breakout_type != 0) {
int size = ArraySize(breakout_bars);
ArrayResize(breakout_bars, size + 1);
ArrayResize(breakout_types, size + 1);
breakout_bars[size] = i;
breakout_types[size] = breakout_type;
}
}
if(InpShowDebugInfo)
Print("共检测到 ", ArraySize(breakout_bars), " 个突破K线");
// 处理突破K线并计算极值点
ProcessBreakouts(breakout_bars, breakout_types, high, low, time);
return(rates_total);
}
//+------------------------------------------------------------------+
//| 检查是否是突破K线 |
//+------------------------------------------------------------------+
int CheckBreakout(int index, const double &open[], const double &close[], const double &ma[])
{
// 多单突破: 开盘 < MA, 收盘 > MA
if(open[index] < ma[index] && close[index] > ma[index])
return 1;
// 空单突破: 开盘 > MA, 收盘 < MA
if(open[index] > ma[index] && close[index] < ma[index])
return -1;
return 0;
}
//+------------------------------------------------------------------+
//| 处理所有突破K线并计算极值点 |
//+------------------------------------------------------------------+
void ProcessBreakouts(int &breakout_bars[], int &breakout_types[],
const double &high[], const double &low[],
const datetime &time[])
{
int total = ArraySize(breakout_bars);
if(total < 2) return;
// 过滤连续同向突破
int filtered_bars[];
int filtered_types[];
ArrayResize(filtered_bars, 0);
ArrayResize(filtered_types, 0);
for(int i = 0; i < total; i++) {
int current_bar = breakout_bars[i];
int current_type = breakout_types[i];
// 检查是否有后续同向突破
bool skip = false;
for(int j = i + 1; j < total; j++) {
if(breakout_types[j] != current_type)
break; // 遇到反向突破,停止检查
// 比较哪个更极端
if(current_type == 1) { // 多单突破,保留最低价更低的
if(low[breakout_bars[j]] < low[current_bar]) {
skip = true;
break;
}
} else { // 空单突破,保留最高价更高的
if(high[breakout_bars[j]] > high[current_bar]) {
skip = true;
break;
}
}
}
if(!skip) {
int size = ArraySize(filtered_bars);
ArrayResize(filtered_bars, size + 1);
ArrayResize(filtered_types, size + 1);
filtered_bars[size] = current_bar;
filtered_types[size] = current_type;
}
}
if(InpShowDebugInfo)
Print("过滤后剩余 ", ArraySize(filtered_bars), " 个有效突破K线");
// 计算极值点并应用波段阈值过滤
double last_extreme_price = 0;
int last_extreme_bar = -1;
int last_extreme_type = 0;
for(int i = 0; i < ArraySize(filtered_bars) - 1; i++) {
int current_bar = filtered_bars[i];
int current_type = filtered_types[i];
int next_bar = filtered_bars[i + 1];
// 计算当前段的极值
double extreme_price = 0;
int extreme_bar = current_bar;
if(current_type == 1) { // 多单突破后找最高价
extreme_price = high[current_bar];
for(int j = current_bar; j >= next_bar; j--) {
if(high[j] > extreme_price) {
extreme_price = high[j];
extreme_bar = j;
}
}
} else { // 空单突破后找最低价
extreme_price = low[current_bar];
for(int j = current_bar; j >= next_bar; j--) {
if(low[j] < extreme_price) {
extreme_price = low[j];
extreme_bar = j;
}
}
}
// 应用波段阈值过滤
if(last_extreme_price != 0) {
double price_diff = MathAbs(extreme_price - last_extreme_price);
double price_diff_points = price_diff / _Point;
if(InpShowDebugInfo) {
Print("索引 ", i, " - 极值:", extreme_price,
", 价差:", (int)price_diff_points, "点",
", 时间:", TimeToString(time[extreme_bar]));
}
if(price_diff_points >= InpWaveThreshold) {
// 标记上一个极值点
MarkExtreme(last_extreme_bar, last_extreme_price, last_extreme_type, high, low);
// 更新为当前极值点
last_extreme_price = extreme_price;
last_extreme_bar = extreme_bar;
last_extreme_type = current_type;
}
// 如果不满足阈值,不更新last_extreme,继续用原极值点比较
} else {
// 第一个极值点,直接记录
last_extreme_price = extreme_price;
last_extreme_bar = extreme_bar;
last_extreme_type = current_type;
}
}
// 标记最后一个极值点
if(last_extreme_bar >= 0) {
MarkExtreme(last_extreme_bar, last_extreme_price, last_extreme_type, high, low);
}
}
//+------------------------------------------------------------------+
//| 标记极值点 |
//+------------------------------------------------------------------+
void MarkExtreme(int bar, double price, int type, const double &high[], const double &low[])
{
if(type == 1) { // 多单突破形成的高点
HighExtremeBuffer[bar] = price;
if(InpShowDebugInfo)
Print("标记高点极值 - 索引:", bar, ", 价格:", price);
} else { // 空单突破形成的低点
LowExtremeBuffer[bar] = price;
if(InpShowDebugInfo)
Print("标记低点极值 - 索引:", bar, ", 价格:", price);
}
}
+346
View File
@@ -0,0 +1,346 @@
//+------------------------------------------------------------------+
//| 20260516_EA.mq5 |
//| 突破极值点标记EA (Expert Advisor版本) |
//+------------------------------------------------------------------+
#property copyright "Breakout Strategy"
#property version "1.00"
#property strict
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
input int InpMAPeriod = 14; // MA周期
input int InpWaveThreshold = 1000; // 波段阈值(点数)
input bool InpShowDebugInfo = true; // 显示调试信息
input color InpHighExtremeColor = clrRed; // 高点极值颜色
input color InpLowExtremeColor = clrLime; // 低点极值颜色
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
int ma_handle; // MA指标句柄
datetime last_process_time = 0; // 上次处理时间
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// 创建MA指标
ma_handle = iMA(_Symbol, PERIOD_CURRENT, InpMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE) {
Print("创建MA指标失败");
return(INIT_FAILED);
}
Print("========================================");
Print("突破极值点标记EA初始化成功");
Print("品种:", _Symbol);
Print("周期:", EnumToString(Period()));
Print("MA周期:", InpMAPeriod);
Print("波段阈值:", InpWaveThreshold, "点 (", InpWaveThreshold/100.0, "美元)");
Print("========================================");
// 删除旧的标记对象
ObjectsDeleteAll(0, "Extreme_");
// 处理历史数据
ProcessHistory();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
IndicatorRelease(ma_handle);
Print("突破极值点标记EA已卸载");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 每根新K线处理一次
datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);
if(current_time == last_process_time)
return;
last_process_time = current_time;
// 重新处理历史数据
ProcessHistory();
}
//+------------------------------------------------------------------+
//| 处理历史数据 |
//+------------------------------------------------------------------+
void ProcessHistory()
{
int bars = Bars(_Symbol, PERIOD_CURRENT);
if(bars < InpMAPeriod + 2) {
Print("K线数量不足: ", bars);
return;
}
// 限制处理的K线数量以提高性能
int process_bars = MathMin(bars, 5000);
if(InpShowDebugInfo)
Print("开始处理历史数据 - 总K线:", bars, ", 处理数量:", process_bars);
// 获取价格数据
MqlRates rates[];
ArraySetAsSeries(rates, true);
int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, process_bars, rates);
if(copied <= 0) {
Print("复制价格数据失败");
return;
}
// 获取MA数据
double ma_array[];
ArraySetAsSeries(ma_array, true);
copied = CopyBuffer(ma_handle, 0, 0, process_bars, ma_array);
if(copied <= 0) {
Print("复制MA数据失败");
return;
}
if(InpShowDebugInfo)
Print("数据准备完成 - 开始识别突破K线");
// 识别所有突破K线
int breakout_bars[];
int breakout_types[];
ArrayResize(breakout_bars, 0);
ArrayResize(breakout_types, 0);
for(int i = process_bars - InpMAPeriod - 1; i >= 1; i--) {
int breakout_type = CheckBreakout(i, rates, ma_array);
if(breakout_type != 0) {
int size = ArraySize(breakout_bars);
ArrayResize(breakout_bars, size + 1);
ArrayResize(breakout_types, size + 1);
breakout_bars[size] = i;
breakout_types[size] = breakout_type;
}
}
if(InpShowDebugInfo)
Print("共检测到 ", ArraySize(breakout_bars), " 个突破K线");
if(ArraySize(breakout_bars) == 0) {
Print("未检测到任何突破K线,请检查MA参数或数据");
return;
}
// 处理突破K线并标记极值点
ProcessBreakouts(breakout_bars, breakout_types, rates);
}
//+------------------------------------------------------------------+
//| 检查是否是突破K线 |
//+------------------------------------------------------------------+
int CheckBreakout(int index, const MqlRates &rates[], const double &ma[])
{
// 多单突破: 开盘 < MA, 收盘 > MA
if(rates[index].open < ma[index] && rates[index].close > ma[index])
return 1;
// 空单突破: 开盘 > MA, 收盘 < MA
if(rates[index].open > ma[index] && rates[index].close < ma[index])
return -1;
return 0;
}
//+------------------------------------------------------------------+
//| 处理所有突破K线并计算极值点 |
//+------------------------------------------------------------------+
void ProcessBreakouts(int &breakout_bars[], int &breakout_types[],
const MqlRates &rates[])
{
int total = ArraySize(breakout_bars);
if(total < 2) return;
// 过滤连续同向突破
int filtered_bars[];
int filtered_types[];
ArrayResize(filtered_bars, 0);
ArrayResize(filtered_types, 0);
for(int i = 0; i < total; i++) {
int current_bar = breakout_bars[i];
int current_type = breakout_types[i];
// 检查是否有后续同向突破
bool skip = false;
for(int j = i + 1; j < total; j++) {
if(breakout_types[j] != current_type)
break;
if(current_type == 1) {
if(rates[breakout_bars[j]].low < rates[current_bar].low) {
skip = true;
break;
}
} else {
if(rates[breakout_bars[j]].high > rates[current_bar].high) {
skip = true;
break;
}
}
}
if(!skip) {
int size = ArraySize(filtered_bars);
ArrayResize(filtered_bars, size + 1);
ArrayResize(filtered_types, size + 1);
filtered_bars[size] = current_bar;
filtered_types[size] = current_type;
}
}
Print("过滤后剩余 ", ArraySize(filtered_bars), " 个有效突破K线(经过同向突破合并)");
// 删除旧的极值点标记
ObjectsDeleteAll(0, "Extreme_");
// 计算所有极值点
struct ExtremeInfo {
datetime time;
double price;
int type;
bool is_valid_wave; // 是否构成有效波段
};
ExtremeInfo extremes[];
ArrayResize(extremes, 0);
// 先计算所有极值点
for(int i = 0; i < ArraySize(filtered_bars) - 1; i++) {
int current_bar = filtered_bars[i];
int current_type = filtered_types[i];
int next_bar = filtered_bars[i + 1];
// 计算当前段的极值
double extreme_price = 0;
datetime extreme_time = 0;
if(current_type == 1) {
extreme_price = rates[current_bar].high;
extreme_time = rates[current_bar].time;
for(int j = current_bar; j >= next_bar; j--) {
if(rates[j].high > extreme_price) {
extreme_price = rates[j].high;
extreme_time = rates[j].time;
}
}
} else {
extreme_price = rates[current_bar].low;
extreme_time = rates[current_bar].time;
for(int j = current_bar; j >= next_bar; j--) {
if(rates[j].low < extreme_price) {
extreme_price = rates[j].low;
extreme_time = rates[j].time;
}
}
}
// 添加到极值点数组
int size = ArraySize(extremes);
ArrayResize(extremes, size + 1);
extremes[size].time = extreme_time;
extremes[size].price = extreme_price;
extremes[size].type = current_type;
extremes[size].is_valid_wave = false;
}
Print("共计算出 ", ArraySize(extremes), " 个极值点");
// 判断哪些是有效波段
// 策略:检查每个极值点与其前一个极值点是否能构成有效波段
// 如果构成有效波段,则两个极值点都标记为有效
if(ArraySize(extremes) > 0) {
// 先全部标记为无效
for(int i = 0; i < ArraySize(extremes); i++) {
extremes[i].is_valid_wave = false;
}
int valid_wave_count = 0;
for(int i = 1; i < ArraySize(extremes); i++) {
// 检查与前一个极值点的价差
double price_diff = MathAbs(extremes[i].price - extremes[i-1].price);
double price_diff_points = price_diff / _Point;
if(InpShowDebugInfo) {
Print("检查极值点 #", i, " 与 #", i-1, " - 价差:", (int)price_diff_points, "点(",
DoubleToString(price_diff_points/100, 2), "美元)");
}
// 有效波段条件:与前一个极值点价差≥阈值
if(price_diff_points >= InpWaveThreshold) {
// 标记这两个极值点都为有效波段
extremes[i-1].is_valid_wave = true;
extremes[i].is_valid_wave = true;
valid_wave_count++;
if(InpShowDebugInfo)
Print(" ✓ 极值点 #", i-1, " 和 #", i, " 构成有效波段");
} else {
if(InpShowDebugInfo)
Print(" ✗ 价差不足(需要", InpWaveThreshold, "点)");
}
}
Print(">>> 识别到 ", valid_wave_count, " 个有效波段");
}
// 标记所有极值点
int valid_count = 0;
for(int i = 0; i < ArraySize(extremes); i++) {
DrawExtreme(extremes[i].time, extremes[i].price, extremes[i].type, i, extremes[i].is_valid_wave);
if(extremes[i].is_valid_wave)
valid_count++;
}
Print("========================================");
Print("处理完成 - 总极值点:", ArraySize(extremes), ", 有效波段极值点:", valid_count);
Print("========================================");
ChartRedraw();
}
//+------------------------------------------------------------------+
//| 在图表上绘制极值点 |
//+------------------------------------------------------------------+
void DrawExtreme(datetime time, double price, int type, int index, bool is_valid_wave)
{
string obj_name = "Extreme_" + IntegerToString(index);
if(type == 1) {
// 多单突破形成的高点 - 画下箭头
ObjectCreate(0, obj_name, OBJ_ARROW, 0, time, price);
ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 234);
ObjectSetInteger(0, obj_name, OBJPROP_COLOR, is_valid_wave ? InpHighExtremeColor : clrDarkRed);
ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, is_valid_wave ? 3 : 2);
ObjectSetInteger(0, obj_name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
} else {
// 空单突破形成的低点 - 画上箭头
ObjectCreate(0, obj_name, OBJ_ARROW, 0, time, price);
ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 233);
ObjectSetInteger(0, obj_name, OBJPROP_COLOR, is_valid_wave ? InpLowExtremeColor : clrDarkGreen);
ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, is_valid_wave ? 3 : 2);
ObjectSetInteger(0, obj_name, OBJPROP_ANCHOR, ANCHOR_TOP);
}
if(InpShowDebugInfo)
Print("标记极值点 #", index, " - 类型:", (type == 1 ? "高点" : "低点"),
", 时间:", TimeToString(time), ", 价格:", DoubleToString(price, _Digits),
", 有效波段:", (is_valid_wave ? "是" : "否"));
}
+689
View File
@@ -0,0 +1,689 @@
//+------------------------------------------------------------------+
//| 20260516_Trade.mq5 |
//| 突破交易策略 - 完整交易版本 |
//+------------------------------------------------------------------+
#property copyright "Breakout Strategy"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| 输入参数 |
//+------------------------------------------------------------------+
input group "=== 波段识别参数 ==="
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M1; // K线周期
input int InpMAPeriod = 14; // MA周期
input bool InpUsePercentMode = true; // 使用百分比模式
input int InpWaveThreshold = 1000; // 波段阈值(点数/固定模式)
input double InpWavePercent = 0.25; // 波段阈值(百分比/百分比模式)
input group "=== 风险管理参数 ==="
input int InpStopLoss = 200; // 止损点数
input int InpTakeProfit = 300; // 止盈点数
input int InpMaxHoldingMinutes = 5; // 最大持仓时间(分钟)
input group "=== 仓位管理参数 ==="
input bool InpUseCompounding = true; // 使用复利模式
input double InpFixedLots = 0.01; // 固定手数
input double InpLotsPer500 = 0.05; // 每500$开仓手数(复利模式)
input int InpMaxPositions = 99; // 最大开仓手数
input group "=== 调试选项 ==="
input bool InpShowDebugInfo = false; // 显示调试信息
input bool InpShowMarkers = true; // 显示极值点标记
input int InpMagicNumber = 20260516; // EA魔术号
//+------------------------------------------------------------------+
//| 全局变量 |
//+------------------------------------------------------------------+
int ma_handle; // MA指标句柄
CTrade trade; // 交易对象
// 最新有效波段信息
struct ValidWaveInfo {
bool exists; // 是否存在有效波段
double high_price; // 高点价格
double low_price; // 低点价格
datetime update_time; // 更新时间
bool high_used; // 高点是否已使用
bool low_used; // 低点是否已使用
};
ValidWaveInfo latest_wave; // 最新有效波段
// 极值点结构体定义
struct ExtremePoint {
datetime time;
double price;
int type;
bool is_valid;
};
// 函数声明
void UpdateLatestValidWave();
void DrawExtremeMarkers(ExtremePoint &extremes[]);
void DrawLatestValidWave(double high_price, datetime high_time, double low_price, datetime low_time);
int CheckBreakout(int index, const MqlRates &rates[], const double &ma[]);
void FilterBreakouts(const int &breakout_bars[], const int &breakout_types[],
const MqlRates &rates[], int &filtered_bars[], int &filtered_types[]);
void CheckOpenSignals();
bool OpenPosition(ENUM_ORDER_TYPE order_type, double wave_high, double wave_low, double threshold_points);
double CalculateLotSize();
void ManagePositions();
bool CheckTakeProfitReached(ulong ticket);
void CheckTrailingStop(ulong ticket);
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// 创建MA指标(使用指定的K线周期)
ma_handle = iMA(_Symbol, InpTimeframe, InpMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE) {
Print("创建MA指标失败");
return(INIT_FAILED);
}
// 设置交易参数
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFilling(ORDER_FILLING_IOC);
// 初始化最新有效波段
latest_wave.exists = false;
latest_wave.high_price = 0;
latest_wave.low_price = 0;
latest_wave.update_time = 0;
latest_wave.high_used = false;
latest_wave.low_used = false;
Print("========================================");
Print("突破交易策略EA初始化成功");
Print("品种:", _Symbol);
Print("K线周期:", EnumToString(InpTimeframe));
Print("MA周期:", InpMAPeriod);
if(InpUsePercentMode)
Print("波段阈值模式: 百分比 - ", InpWavePercent, "%");
else
Print("波段阈值模式: 固定点数 - ", InpWaveThreshold, "点");
Print("止损:", InpStopLoss, "点 | 止盈:", InpTakeProfit, "点");
Print("最大持仓时间:", InpMaxHoldingMinutes, "分钟");
Print("最大开仓手数:", InpMaxPositions);
Print("手数模式:", (InpUseCompounding ? "复利" : "固定"),
InpUseCompounding ? StringFormat(" (每500$开%.2f手)", InpLotsPer500) : StringFormat(" (%.2f手)", InpFixedLots));
Print("========================================");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
IndicatorRelease(ma_handle);
// 删除所有标记
ObjectsDeleteAll(0, "ValidWave_");
Print("突破交易策略EA已卸载");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. 更新最新有效波段
UpdateLatestValidWave();
// 2. 检查开仓信号
CheckOpenSignals();
// 3. 管理已有持仓
ManagePositions();
}
//+------------------------------------------------------------------+
//| 更新最新有效波段 |
//+------------------------------------------------------------------+
void UpdateLatestValidWave()
{
int bars = Bars(_Symbol, InpTimeframe);
if(bars < InpMAPeriod + 2)
return;
// 限制处理的K线数量
int process_bars = MathMin(bars, 500);
// 获取价格数据(使用指定的K线周期)
MqlRates rates[];
ArraySetAsSeries(rates, true);
if(CopyRates(_Symbol, InpTimeframe, 0, process_bars, rates) <= 0)
return;
// 获取MA数据
double ma_array[];
ArraySetAsSeries(ma_array, true);
if(CopyBuffer(ma_handle, 0, 0, process_bars, ma_array) <= 0)
return;
// 识别突破K线
int breakout_bars[];
int breakout_types[];
ArrayResize(breakout_bars, 0);
ArrayResize(breakout_types, 0);
for(int i = process_bars - InpMAPeriod - 1; i >= 1; i--) {
int breakout_type = CheckBreakout(i, rates, ma_array);
if(breakout_type != 0) {
int size = ArraySize(breakout_bars);
ArrayResize(breakout_bars, size + 1);
ArrayResize(breakout_types, size + 1);
breakout_bars[size] = i;
breakout_types[size] = breakout_type;
}
}
// 过滤连续同向突破
int filtered_bars[];
int filtered_types[];
FilterBreakouts(breakout_bars, breakout_types, rates, filtered_bars, filtered_types);
// 计算极值点
ExtremePoint extremes[];
ArrayResize(extremes, 0);
for(int i = 0; i < ArraySize(filtered_bars) - 1; i++) {
int current_bar = filtered_bars[i];
int current_type = filtered_types[i];
int next_bar = filtered_bars[i + 1];
double extreme_price = 0;
datetime extreme_time = 0;
if(current_type == 1) {
extreme_price = rates[current_bar].high;
extreme_time = rates[current_bar].time;
for(int j = current_bar; j >= next_bar; j--) {
if(rates[j].high > extreme_price) {
extreme_price = rates[j].high;
extreme_time = rates[j].time;
}
}
} else {
extreme_price = rates[current_bar].low;
extreme_time = rates[current_bar].time;
for(int j = current_bar; j >= next_bar; j--) {
if(rates[j].low < extreme_price) {
extreme_price = rates[j].low;
extreme_time = rates[j].time;
}
}
}
int size = ArraySize(extremes);
ArrayResize(extremes, size + 1);
extremes[size].time = extreme_time;
extremes[size].price = extreme_price;
extremes[size].type = current_type;
extremes[size].is_valid = false;
}
// 判断有效波段并标记
for(int i = 1; i < ArraySize(extremes); i++) {
double price_diff = MathAbs(extremes[i].price - extremes[i-1].price);
double price_diff_points = price_diff / _Point;
// 计算阈值
double threshold = 0;
if(InpUsePercentMode) {
// 百分比模式:以前一个极值点价格为基准计算百分比
double base_price = extremes[i-1].price;
threshold = (base_price * InpWavePercent / 100.0) / _Point;
} else {
// 固定点数模式
threshold = InpWaveThreshold;
}
if(price_diff_points >= threshold) {
extremes[i-1].is_valid = true;
extremes[i].is_valid = true;
}
}
// 绘制所有极值点标记
if(InpShowMarkers) {
DrawExtremeMarkers(extremes);
}
// 查找最新的有效波段
for(int i = ArraySize(extremes) - 1; i >= 1; i--) {
double price_diff = MathAbs(extremes[i].price - extremes[i-1].price);
double price_diff_points = price_diff / _Point;
// 计算阈值
double threshold = 0;
if(InpUsePercentMode) {
double base_price = extremes[i-1].price;
threshold = (base_price * InpWavePercent / 100.0) / _Point;
} else {
threshold = InpWaveThreshold;
}
if(price_diff_points >= threshold) {
// 找到最新的有效波段
double high = MathMax(extremes[i].price, extremes[i-1].price);
double low = MathMin(extremes[i].price, extremes[i-1].price);
datetime high_time = (extremes[i].price > extremes[i-1].price) ? extremes[i].time : extremes[i-1].time;
datetime low_time = (extremes[i].price < extremes[i-1].price) ? extremes[i].time : extremes[i-1].time;
// 检查是否是新的波段
if(latest_wave.exists == false ||
extremes[i].time > latest_wave.update_time ||
high != latest_wave.high_price ||
low != latest_wave.low_price) {
latest_wave.exists = true;
latest_wave.high_price = high;
latest_wave.low_price = low;
latest_wave.update_time = extremes[i].time;
latest_wave.high_used = false; // 新波段,重置使用状态
latest_wave.low_used = false;
// 绘制最新有效波段
if(InpShowMarkers) {
DrawLatestValidWave(high, high_time, low, low_time);
}
if(InpShowDebugInfo) {
string threshold_info = InpUsePercentMode ?
StringFormat("%.2f%% (%.0f点)", InpWavePercent, threshold) :
StringFormat("%d点", InpWaveThreshold);
Print("更新最新有效波段 - 高:", DoubleToString(high, _Digits),
" 低:", DoubleToString(low, _Digits),
" 价差:", (int)price_diff_points, "点",
" 阈值:", threshold_info);
}
}
break;
}
}
}
//+------------------------------------------------------------------+
//| 绘制所有极值点标记 |
//+------------------------------------------------------------------+
void DrawExtremeMarkers(ExtremePoint &extremes[])
{
// 删除旧的标记
ObjectsDeleteAll(0, "ValidWave_Extreme_");
for(int i = 0; i < ArraySize(extremes); i++) {
string obj_name = "ValidWave_Extreme_" + IntegerToString(i);
if(extremes[i].type == 1) {
// 高点 - 画下箭头
ObjectCreate(0, obj_name, OBJ_ARROW, 0, extremes[i].time, extremes[i].price);
ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 234);
ObjectSetInteger(0, obj_name, OBJPROP_COLOR, extremes[i].is_valid ? clrRed : clrDarkRed);
ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, extremes[i].is_valid ? 3 : 1);
ObjectSetInteger(0, obj_name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
} else {
// 低点 - 画上箭头
ObjectCreate(0, obj_name, OBJ_ARROW, 0, extremes[i].time, extremes[i].price);
ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 233);
ObjectSetInteger(0, obj_name, OBJPROP_COLOR, extremes[i].is_valid ? clrLime : clrDarkGreen);
ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, extremes[i].is_valid ? 3 : 1);
ObjectSetInteger(0, obj_name, OBJPROP_ANCHOR, ANCHOR_TOP);
}
}
}
//+------------------------------------------------------------------+
//| 绘制最新有效波段 |
//+------------------------------------------------------------------+
void DrawLatestValidWave(double high_price, datetime high_time, double low_price, datetime low_time)
{
// 删除旧的最新波段标记
ObjectDelete(0, "ValidWave_Latest_High");
ObjectDelete(0, "ValidWave_Latest_Low");
ObjectDelete(0, "ValidWave_Latest_Line");
// 标记最新有效波段的高点(更大更亮的箭头)
ObjectCreate(0, "ValidWave_Latest_High", OBJ_ARROW, 0, high_time, high_price);
ObjectSetInteger(0, "ValidWave_Latest_High", OBJPROP_ARROWCODE, 234);
ObjectSetInteger(0, "ValidWave_Latest_High", OBJPROP_COLOR, clrYellow);
ObjectSetInteger(0, "ValidWave_Latest_High", OBJPROP_WIDTH, 4);
ObjectSetInteger(0, "ValidWave_Latest_High", OBJPROP_ANCHOR, ANCHOR_BOTTOM);
// 标记最新有效波段的低点
ObjectCreate(0, "ValidWave_Latest_Low", OBJ_ARROW, 0, low_time, low_price);
ObjectSetInteger(0, "ValidWave_Latest_Low", OBJPROP_ARROWCODE, 233);
ObjectSetInteger(0, "ValidWave_Latest_Low", OBJPROP_COLOR, clrYellow);
ObjectSetInteger(0, "ValidWave_Latest_Low", OBJPROP_WIDTH, 4);
ObjectSetInteger(0, "ValidWave_Latest_Low", OBJPROP_ANCHOR, ANCHOR_TOP);
// 绘制连接线
ObjectCreate(0, "ValidWave_Latest_Line", OBJ_TREND, 0, high_time, high_price, low_time, low_price);
ObjectSetInteger(0, "ValidWave_Latest_Line", OBJPROP_COLOR, clrYellow);
ObjectSetInteger(0, "ValidWave_Latest_Line", OBJPROP_WIDTH, 2);
ObjectSetInteger(0, "ValidWave_Latest_Line", OBJPROP_STYLE, STYLE_SOLID);
ObjectSetInteger(0, "ValidWave_Latest_Line", OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, "ValidWave_Latest_Line", OBJPROP_BACK, true);
}
//+------------------------------------------------------------------+
//| 检查是否是突破K线 |
//+------------------------------------------------------------------+
int CheckBreakout(int index, const MqlRates &rates[], const double &ma[])
{
if(rates[index].open < ma[index] && rates[index].close > ma[index])
return 1;
if(rates[index].open > ma[index] && rates[index].close < ma[index])
return -1;
return 0;
}
//+------------------------------------------------------------------+
//| 过滤连续同向突破 |
//+------------------------------------------------------------------+
void FilterBreakouts(const int &breakout_bars[], const int &breakout_types[],
const MqlRates &rates[], int &filtered_bars[], int &filtered_types[])
{
int total = ArraySize(breakout_bars);
ArrayResize(filtered_bars, 0);
ArrayResize(filtered_types, 0);
for(int i = 0; i < total; i++) {
int current_bar = breakout_bars[i];
int current_type = breakout_types[i];
bool skip = false;
for(int j = i + 1; j < total; j++) {
if(breakout_types[j] != current_type)
break;
if(current_type == 1) {
if(rates[breakout_bars[j]].low < rates[current_bar].low) {
skip = true;
break;
}
} else {
if(rates[breakout_bars[j]].high > rates[current_bar].high) {
skip = true;
break;
}
}
}
if(!skip) {
int size = ArraySize(filtered_bars);
ArrayResize(filtered_bars, size + 1);
ArrayResize(filtered_types, size + 1);
filtered_bars[size] = current_bar;
filtered_types[size] = current_type;
}
}
}
//+------------------------------------------------------------------+
//| 检查开仓信号 |
//+------------------------------------------------------------------+
void CheckOpenSignals()
{
if(!latest_wave.exists)
return;
// 检查当前持仓数量是否已达上限
int total_positions = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(!PositionSelectByTicket(PositionGetTicket(i)))
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
continue;
total_positions++;
}
if(total_positions >= InpMaxPositions) {
if(InpShowDebugInfo)
Print("已达最大持仓数量限制: ", total_positions, "/", InpMaxPositions);
return;
}
double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
// 计算当前波段的实际阈值
double wave_threshold_points = 0;
if(InpUsePercentMode) {
// 百分比模式:取高低点的平均值作为基准
double base_price = (latest_wave.high_price + latest_wave.low_price) / 2.0;
wave_threshold_points = (base_price * InpWavePercent / 100.0) / _Point;
} else {
// 固定点数模式
wave_threshold_points = InpWaveThreshold;
}
// 检查多单信号:突破高点(且该高点未使用过)
if(!latest_wave.high_used && current_price > latest_wave.high_price) {
if(InpShowDebugInfo)
Print("检测到多单信号 - 价格:", current_price, " 突破高点:", latest_wave.high_price);
if(OpenPosition(ORDER_TYPE_BUY, latest_wave.high_price, latest_wave.low_price, wave_threshold_points)) {
latest_wave.high_used = true; // 标记高点已使用,该波段高点失效
if(InpShowDebugInfo)
Print("高点已使用,等待新的有效波段");
}
}
// 检查空单信号:突破低点(且该低点未使用过)
if(!latest_wave.low_used && current_price < latest_wave.low_price) {
if(InpShowDebugInfo)
Print("检测到空单信号 - 价格:", current_price, " 突破低点:", latest_wave.low_price);
if(OpenPosition(ORDER_TYPE_SELL, latest_wave.high_price, latest_wave.low_price, wave_threshold_points)) {
latest_wave.low_used = true; // 标记低点已使用,该波段低点失效
if(InpShowDebugInfo)
Print("低点已使用,等待新的有效波段");
}
}
}
//+------------------------------------------------------------------+
//| 开仓 |
//+------------------------------------------------------------------+
bool OpenPosition(ENUM_ORDER_TYPE order_type, double wave_high, double wave_low, double threshold_points)
{
double lots = CalculateLotSize();
if(lots <= 0)
return false;
double current_price = (order_type == ORDER_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_ASK) :
SymbolInfoDouble(_Symbol, SYMBOL_BID);
double sl = 0, tp = 0;
bool result = false;
// 生成包含有效波段信息的注释
string comment = StringFormat("%s H%.0f L%.0f T%.0f",
(order_type == ORDER_TYPE_BUY ? "B" : "S"),
wave_high,
wave_low,
threshold_points);
if(order_type == ORDER_TYPE_BUY) {
sl = current_price - InpStopLoss * _Point;
tp = current_price + InpTakeProfit * _Point;
result = trade.Buy(lots, _Symbol, 0, sl, tp, comment);
if(result) {
Print("开多单成功 - 手数:", lots,
" 波段:H:", wave_high, " L:", wave_low,
" 止损:", sl, " 止盈:", tp);
}
} else {
sl = current_price + InpStopLoss * _Point;
tp = current_price - InpTakeProfit * _Point;
result = trade.Sell(lots, _Symbol, 0, sl, tp, comment);
if(result) {
Print("开空单成功 - 手数:", lots,
" 波段:H:", wave_high, " L:", wave_low,
" 止损:", sl, " 止盈:", tp);
}
}
return result;
}
//+------------------------------------------------------------------+
//| 计算开仓手数 |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
double lots = 0;
if(InpUseCompounding) {
// 复利模式
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
lots = NormalizeDouble((balance / 500.0) * InpLotsPer500, 2);
} else {
// 固定手数模式
lots = InpFixedLots;
}
// 检查手数限制
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lot_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(lots < min_lot) lots = min_lot;
if(lots > max_lot) lots = max_lot;
lots = MathFloor(lots / lot_step) * lot_step;
return lots;
}
//+------------------------------------------------------------------+
//| 管理持仓 |
//+------------------------------------------------------------------+
void ManagePositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--) {
if(!PositionSelectByTicket(PositionGetTicket(i)))
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber)
continue;
// 检查最大持仓时间
datetime open_time = (datetime)PositionGetInteger(POSITION_TIME);
int holding_minutes = (int)((TimeCurrent() - open_time) / 60);
if(holding_minutes >= InpMaxHoldingMinutes) {
ulong ticket = PositionGetTicket(i);
trade.PositionClose(ticket);
Print("持仓时间超限,平仓 - Ticket:", ticket, " 持仓时长:", holding_minutes, "分钟");
continue;
}
// 手动检查止盈(防止跳空未触发)
ulong ticket = PositionGetTicket(i);
if(CheckTakeProfitReached(ticket)) {
trade.PositionClose(ticket);
Print("手动止盈平仓 - Ticket:", ticket);
continue;
}
// 检查移动止损
CheckTrailingStop(ticket);
}
}
//+------------------------------------------------------------------+
//| 检查是否达到止盈(防止跳空未触发) |
//+------------------------------------------------------------------+
bool CheckTakeProfitReached(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return false;
// 如果止盈设置为0,不检查
if(InpTakeProfit <= 0)
return false;
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_price = (pos_type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// 计算浮盈点数
double profit_points = 0;
if(pos_type == POSITION_TYPE_BUY) {
profit_points = (current_price - open_price) / _Point;
} else {
profit_points = (open_price - current_price) / _Point;
}
// 检查是否达到或超过止盈点数
if(profit_points >= InpTakeProfit) {
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| 检查移动止损 |
//+------------------------------------------------------------------+
void CheckTrailingStop(ulong ticket)
{
if(!PositionSelectByTicket(ticket))
return;
double open_price = PositionGetDouble(POSITION_PRICE_OPEN);
double current_sl = PositionGetDouble(POSITION_SL);
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
double current_price = (pos_type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// 计算浮盈点数
double profit_points = 0;
if(pos_type == POSITION_TYPE_BUY) {
profit_points = (current_price - open_price) / _Point;
} else {
profit_points = (open_price - current_price) / _Point;
}
// 浮盈达到止损点数,移动止损至成本价
if(profit_points >= InpStopLoss) {
double new_sl = open_price;
// 检查是否需要更新
bool need_update = false;
if(pos_type == POSITION_TYPE_BUY && (current_sl < new_sl || current_sl == 0)) {
need_update = true;
} else if(pos_type == POSITION_TYPE_SELL && (current_sl > new_sl || current_sl == 0)) {
need_update = true;
}
if(need_update) {
double tp = PositionGetDouble(POSITION_TP);
if(trade.PositionModify(ticket, new_sl, tp)) {
Print("移动止损至成本价 - Ticket:", ticket, " 新止损:", new_sl);
}
}
}
}
+64
View File
@@ -0,0 +1,64 @@
# 突破交易策略
## 基础概念
- 平台:MQL5
- 品种:XAU黄金
- 1美金:4100与4101之间相差1美金
- 1美金 = 100点
## 辅助指标
- 移动平均线(Moving Average
- 周期默认设置为:14
## 核心概念
- 突破K线:开盘价在均线下方,收盘价在均线上方,称之为多单突破K线,反之亦然
- 极值点:相邻两根突破K线之间所有K线(包括突破K线)中的最高价或最低价称之为极值点
- 有效波段:相邻两个极值点的价差达到波段阈值点数时,期间的所有K线(包含突破K线本身)组成的行情,称之为有效波段
## 开仓逻辑
- 价格突破有效波段的高点开多单,价格突破有效波点的低点开空单
- 该开仓逻辑仅适用最新的一个有效波段
- 浮盈达到止损点数后,即可将止损移动至成本价
- 已经开仓后,继续监控新的有效波段突破,每单开仓后就与有效波段无关了
## 参数设置
### 波段识别参数
- K线周期:支持参数设置,不跟随图表,默认1min
- 使用百分比模式:默认true(使用百分比模式)
- 波段阈值点数:默认1000点(固定模式有效)
- 波段阈值百分比:默认0.25%(百分比模式有效)
### 风险管理参数
- 止损点数:默认200点
- 止盈点数:默认300点
- 最大持仓时间:默认5分钟
### 仓位管理参数
- 使用复利模式:默认true(开启复利模式)
- 固定手数:默认0.01(固定模式有效)
- 每500$开仓手数:默认0.05(复利模式有效)
- 最大开仓手数:默认99
## 补充说明
### 突破K线处理
- 如果出现多个多单突破K线,仅保留最低价更低的那个K线,反之亦然
- 多头有效波段之间,仅允许出现一个多单突破K线和一个空单突破K线,反之亦然
### 有效波段规则
- 一个有效波段只能开一单(无论是高点还是低点)
- 开仓后该波段立即失效
- 必须等新的有效波段形成才能再次开仓
### 波段阈值模式说明
**固定点数模式:**
- 相邻极值点价差达到固定点数即为有效波段
- 适合明确知道想要的波段大小的场景
- 例如:设置1000点,则任何1000点以上的波段都有效
**百分比模式:**
- 以前一个极值点价格为基准,按百分比计算阈值
- 动态适应不同价格水平
- 例如:设置1%,价格4700时阈值为47美元(4700点),价格2350时阈值为23.5美元(2350点)
- 适合价格波动较大或需要根据价格水平动态调整的场景
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
https://nfs.faireconomy.media/ff_calendar_thisweek.json
https://api.pioneeralgofx.com
https://www.worldtimeserver.com
https://www.economies.com