diff --git a/20260516-突破策略/20260516.mq5 b/20260516-突破策略/20260516.mq5 new file mode 100644 index 0000000..2351f89 --- /dev/null +++ b/20260516-突破策略/20260516.mq5 @@ -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); + } +} diff --git a/20260516-突破策略/20260516_EA.mq5 b/20260516-突破策略/20260516_EA.mq5 new file mode 100644 index 0000000..3add4c6 --- /dev/null +++ b/20260516-突破策略/20260516_EA.mq5 @@ -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 ? "是" : "否")); +} diff --git a/20260516-突破策略/20260516_Trade.mq5 b/20260516-突破策略/20260516_Trade.mq5 new file mode 100644 index 0000000..25293b8 --- /dev/null +++ b/20260516-突破策略/20260516_Trade.mq5 @@ -0,0 +1,689 @@ +//+------------------------------------------------------------------+ +//| 20260516_Trade.mq5 | +//| 突破交易策略 - 完整交易版本 | +//+------------------------------------------------------------------+ +#property copyright "Breakout Strategy" +#property version "1.00" +#property strict + +#include + +//+------------------------------------------------------------------+ +//| 输入参数 | +//+------------------------------------------------------------------+ +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); + } + } + } +} diff --git a/20260516-突破策略/README.md b/20260516-突破策略/README.md new file mode 100644 index 0000000..70110da --- /dev/null +++ b/20260516-突破策略/README.md @@ -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点) +- 适合价格波动较大或需要根据价格水平动态调整的场景 diff --git a/KVB-50059417/golden blaze.set b/KVB-50059417/golden blaze.set new file mode 100644 index 0000000..d1e4432 Binary files /dev/null and b/KVB-50059417/golden blaze.set differ diff --git a/KVB-50059419/Gloden Blaze.set b/KVB-50059419/Gloden Blaze.set new file mode 100644 index 0000000..d1e4432 Binary files /dev/null and b/KVB-50059419/Gloden Blaze.set differ diff --git a/KVB-50059419/TwisterPro Scalper 1.8 500$.set b/KVB-50059419/TwisterPro Scalper 1.8 500$.set new file mode 100644 index 0000000..e8aa0bd Binary files /dev/null and b/KVB-50059419/TwisterPro Scalper 1.8 500$.set differ diff --git a/KVB-50059419/TwisterPro Scalper 1.8.ex5 b/KVB-50059419/TwisterPro Scalper 1.8.ex5 new file mode 100644 index 0000000..f9dafff Binary files /dev/null and b/KVB-50059419/TwisterPro Scalper 1.8.ex5 differ diff --git a/tag/872+言成跟单系统(续).ex5 b/tag/872+言成跟单系统(续).ex5 new file mode 100644 index 0000000..1d05a62 Binary files /dev/null and b/tag/872+言成跟单系统(续).ex5 differ diff --git a/tag/AXIO Gold EA.zip b/tag/AXIO Gold EA.zip new file mode 100644 index 0000000..20710cd Binary files /dev/null and b/tag/AXIO Gold EA.zip differ diff --git a/tag/AXIO Gold EA/Experts/AXIO Gold.ex5 b/tag/AXIO Gold EA/Experts/AXIO Gold.ex5 new file mode 100644 index 0000000..64136ff Binary files /dev/null and b/tag/AXIO Gold EA/Experts/AXIO Gold.ex5 differ diff --git a/tag/AXIO Gold EA/Presets/Axio gold.set b/tag/AXIO Gold EA/Presets/Axio gold.set new file mode 100644 index 0000000..f27e589 Binary files /dev/null and b/tag/AXIO Gold EA/Presets/Axio gold.set differ diff --git a/tag/AXIO Gold EA/Servers.txt b/tag/AXIO Gold EA/Servers.txt new file mode 100644 index 0000000..07bec93 --- /dev/null +++ b/tag/AXIO Gold EA/Servers.txt @@ -0,0 +1,4 @@ +https://nfs.faireconomy.media/ff_calendar_thisweek.json +https://api.pioneeralgofx.com +https://www.worldtimeserver.com +https://www.economies.com \ No newline at end of file