80-20 交易策略·进阶篇
(2/3)· 从手工指标到回测 EA,只讲如何把 Raschke 的边界反转规则写成能跑的代码
◍ 把80-20策略搬进MT5指标面板
做手工交易的人最怕信号看完就忘。这个指标直接把'80-20'系统的挂单、止损、止盈价位画在主图上,并支持弹出、邮件、推送三种通知任意组合,交易者开MT5加载后就能照着线挂单。外汇和贵金属波动剧烈,这类画线仅作概率参考,不等于方向保证。 指标另一块硬功能是历史布局回看:它高亮日线形态第一根柱线,并把上部20%和下部20%之间的内部区域填色。价格触信号线时,价位显示为挂单;挂单激活后信号线变止损/止盈线;平仓后线中断。这样哪根形态赚钱、哪根该改参数,一眼能估。 缓冲区声明是第一步。需要两个DRAW_FILLING画柱线区域,两个DRAW_COLOR_LINE画信号和挂单(颜色随多空变),再加两条固定色的DRAW_LINE画SL/TP。除简单线外每种显示占两个缓冲,所以总共10个缓冲、6个plot。 [CODE] #property indicator_chart_window #property indicator_buffers 10 #property indicator_plots 6 #property indicator_label1 "形态的第一根柱线" #property indicator_type1 DRAW_FILLING #property indicator_color1 clrDeepPink , clrDodgerBlue #property indicator_width1 1 #property indicator_label2 "形态的第一根柱线" #property indicator_type2 DRAW_FILLING #property indicator_color2 clrDeepPink , clrDodgerBlue #property indicator_width2 1 #property indicator_label3 "信号价位" #property indicator_type3 DRAW_COLOR_LINE #property indicator_style3 STYLE_SOLID #property indicator_color3 clrDeepPink , clrDodgerBlue #property indicator_width3 2 #property indicator_label4 "入场价位" #property indicator_type4 DRAW_COLOR_LINE #property indicator_style4 STYLE_DASHDOT #property indicator_color4 clrDeepPink , clrDodgerBlue #property indicator_width4 2 #property indicator_label5 "止损" #property indicator_type5 DRAW_LINE #property indicator_style5 STYLE_DASHDOTDOT #property indicator_color5 clrCrimson #property indicator_width5 1 #property indicator_label6 "止盈" #property indicator_type6 DRAW_LINE #property indicator_style6 STYLE_DASHDOTDOT #property indicator_color6 clrLime #property indicator_width6 1 [/CODE] 上面代码逐行拆:indicator_chart_window把指标叠在主图;buffers 10即开10个数据缓冲;plots 6代表6类绘制。label1/2配DRAW_FILLING用深粉+蓝填充第一根柱线内外区;label3/4是彩色线(实线信号、点划线挂单);label5/6是洋红止损、绿止盈点划点线,颜色写死不走多空逻辑。 输入项给了开关:Show_Outer/Inner控制填色,Alert_Popup/Email/Push挑通知方式,Bars_Limit限2000根历史。OnInit里把D1平均周期钳到最小1,把品种点数折算进gd_Extremum_Break,并按86400/PeriodSeconds()算每日柱数赋给gi_Min_Bars。 [CODE] #include <Expert\Signal\Signal_80- 20 .mqh> input bool Show_Outer = true ; input bool Show_Inner = true ; input bool Alert_Popup = true ; input bool Alert_Email = false ; input string Alert_Email_Subj = "" ; input bool Alert_Push = true ; input uint Bars_Limit = 2000 ; ENUM_LOG_LEVEL Log_Level = LOG_LEVEL_NONE; double buff_1st_Bar_Outer[], buff_1st_Bar_Outer_Zero[], buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], buff_Signal[], buff_Signal_Color[], buff_Entry[], buff_Entry_Color[], buff_SL[], buff_TP[], gd_Extremum_Break = 0 ; int gi_D1_Average_Period = 1 , gi_Min_Bars = WRONG_VALUE ; int OnInit () { gi_D1_Average_Period = int ( fmin ( 1 , TS_8020_D1_Average_Period)); gd_Extremum_Break = TS_8020_Extremum_Break * _Point ; gi_Min_Bars = int ( 86400 / PeriodSeconds ()); SetIndexBuffer ( 0 , buff_1st_Bar_Outer, INDICATOR_DATA ); PlotIndexSetDouble ( 0 , PLOT_EMPTY_VALUE , 0 ); SetIndexBuffer ( 1 , buff_1st_Bar_Outer_Zero, INDICATOR_DATA ); SetIndexBuffer ( 2 , buff_1st_Bar_Inner, INDICATOR_DATA ); PlotIndexSetDouble ( 1 , PLOT_EMPTY_VALUE , 0 ); SetIndexBuffer ( 3 , buff_1st_Bar_Inner_Zero, INDICATOR_DATA ); SetIndexBuffer ( 4 , buff_Signal, INDICATOR_DATA ); PlotIndexSetDouble ( 2 , PLOT_EMPTY_VALUE , 0 ); SetIndexBuffer ( 5 , buff_Signal_Color, INDICATOR_COLOR_INDEX ); SetIndexBuffer ( 6 , buff_Entry, INDICATOR_DATA ); PlotIndexSetDouble ( 3 , PLOT_EMPTY_VALUE , 0 ); SetIndexBuffer ( 7 , buff_Entry_Color, INDICATOR_COLOR_INDEX ); SetIndexBuffer ( 8 , buff_SL, INDICATOR_DATA ); PlotIndexSetDouble ( 4 , PLOT_EMPTY_VALUE , 0 ); SetIndexBuffer ( 9 , buff_TP, INDICATOR_DATA ); PlotIndexSetDouble ( 5 , PLOT_EMPTY_VALUE , 0 ); IndicatorSetInteger ( INDICATOR_DIGITS , _Digits ); IndicatorSetString ( INDICATOR_SHORTNAME , "80-20 TS" ); return ( INIT_SUCCEEDED ); } [/CODE] 这段逐行看:include把信号模块拉进来;input块是面板里能改的开关,默认弹窗和推送开、邮件关。全局double数组一一对应10个缓冲,Zero后缀数组专门给FILLING当底边(值设0即不画)。OnInit里fmin保下限,_Point折算突破阈值,PeriodSeconds算日柱数;SetIndexBuffer把数组绑到缓冲号,PlotIndexSetDouble清空值设0,短名标成'80-20 TS'。 别把prev_calculated当万能初判 切品种或周期时,若不在重初始化后强清缓冲,旧填充区会鬼影般留屏。但prev_calculated为零不光在首次,校验和变了也会归零,所以得另建结构存'是否首次启动''不掉零的已算计数''校验变标志''柱线起标''起算时间'。文末 global 层放一个名为 Brownie 的结构体加单例 go_Brownie,就能跨函数收数据,幻像问题大概率消掉。
class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">10 class="macro">#class="kw">property indicator_plots class="num">6 class="macro">#class="kw">property indicator_label1 "形态的第一根柱线" class="macro">#class="kw">property indicator_type1 DRAW_FILLING class="macro">#class="kw">property indicator_color1 clrDeepPink , clrDodgerBlue class="macro">#class="kw">property indicator_width1 class="num">1 class="macro">#class="kw">property indicator_label2 "形态的第一根柱线" class="macro">#class="kw">property indicator_type2 DRAW_FILLING class="macro">#class="kw">property indicator_color2 clrDeepPink , clrDodgerBlue class="macro">#class="kw">property indicator_width2 class="num">1 class="macro">#class="kw">property indicator_label3 "信号价位" class="macro">#class="kw">property indicator_type3 DRAW_COLOR_LINE class="macro">#class="kw">property indicator_style3 STYLE_SOLID class="macro">#class="kw">property indicator_color3 clrDeepPink , clrDodgerBlue class="macro">#class="kw">property indicator_width3 class="num">2 class="macro">#class="kw">property indicator_label4 "入场价位" class="macro">#class="kw">property indicator_type4 DRAW_COLOR_LINE class="macro">#class="kw">property indicator_style4 STYLE_DASHDOT class="macro">#class="kw">property indicator_color4 clrDeepPink , clrDodgerBlue class="macro">#class="kw">property indicator_width4 class="num">2 class="macro">#class="kw">property indicator_label5 "止损" class="macro">#class="kw">property indicator_type5 DRAW_LINE class="macro">#class="kw">property indicator_style5 STYLE_DASHDOTDOT class="macro">#class="kw">property indicator_color5 clrCrimson class="macro">#class="kw">property indicator_width5 class="num">1 class="macro">#class="kw">property indicator_label6 "止盈" class="macro">#class="kw">property indicator_type6 DRAW_LINE class="macro">#class="kw">property indicator_style6 STYLE_DASHDOTDOT class="macro">#class="kw">property indicator_color6 clrLime class="macro">#class="kw">property indicator_width6 class="num">1 class="macro">#include <Expert\Signal\Signal_80- class="num">20 .mqh> class="kw">input class="type">bool Show_Outer = true ; class="kw">input class="type">bool Show_Inner = true ; class="kw">input class="type">bool Alert_Popup = true ; class="kw">input class="type">bool Alert_Email = class="kw">false ; class="kw">input class="type">class="kw">string Alert_Email_Subj = "" ; class="kw">input class="type">bool Alert_Push = true ; class="kw">input class="type">uint Bars_Limit = class="num">2000 ; ENUM_LOG_LEVEL Log_Level = LOG_LEVEL_NONE; class="type">class="kw">double buff_1st_Bar_Outer[], buff_1st_Bar_Outer_Zero[], buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], buff_Signal[], buff_Signal_Color[], buff_Entry[], buff_Entry_Color[], buff_SL[], buff_TP[], gd_Extremum_Break = class="num">0 ; class="type">int gi_D1_Average_Period = class="num">1 , gi_Min_Bars = WRONG_VALUE ; class="type">int OnInit() { gi_D1_Average_Period = class="type">int ( fmin( class="num">1 , TS_8020_D1_Average_Period)); gd_Extremum_Break = TS_8020_Extremum_Break * _Point ; gi_Min_Bars = class="type">int ( class="num">86400 / PeriodSeconds()); SetIndexBuffer( class="num">0 , buff_1st_Bar_Outer, INDICATOR_DATA ); PlotIndexSetDouble( class="num">0 , PLOT_EMPTY_VALUE , class="num">0 ); SetIndexBuffer( class="num">1 , buff_1st_Bar_Outer_Zero, INDICATOR_DATA ); SetIndexBuffer( class="num">2 , buff_1st_Bar_Inner, INDICATOR_DATA ); PlotIndexSetDouble( class="num">1 , PLOT_EMPTY_VALUE , class="num">0 ); SetIndexBuffer( class="num">3 , buff_1st_Bar_Inner_Zero, INDICATOR_DATA ); SetIndexBuffer( class="num">4 , buff_Signal, INDICATOR_DATA ); PlotIndexSetDouble( class="num">2 , PLOT_EMPTY_VALUE , class="num">0 ); SetIndexBuffer( class="num">5 , buff_Signal_Color, INDICATOR_COLOR_INDEX ); SetIndexBuffer( class="num">6 , buff_Entry, INDICATOR_DATA ); PlotIndexSetDouble( class="num">3 , PLOT_EMPTY_VALUE , class="num">0 ); SetIndexBuffer( class="num">7 , buff_Entry_Color, INDICATOR_COLOR_INDEX ); SetIndexBuffer( class="num">8 , buff_SL, INDICATOR_DATA ); PlotIndexSetDouble( class="num">4 , PLOT_EMPTY_VALUE , class="num">0 ); SetIndexBuffer( class="num">9 , buff_TP, INDICATOR_DATA ); PlotIndexSetDouble( class="num">5 , PLOT_EMPTY_VALUE , class="num">0 ); IndicatorSetInteger( INDICATOR_DIGITS , _Digits ); IndicatorSetString( INDICATOR_SHORTNAME , "class="num">80-class="num">20 TS" ); class="kw">return ( INIT_SUCCEEDED ); }
「用 Brownie 结构接管指标状态与重算边界」
在 MT5 指标里,跨 tick 保持状态最忌讳用一堆散装全局变量。这里用一个 BROWNIE 结构把「上根柱时间、已算柱数、首启标志、历史更新、新柱标志」收进同一个对象,构造器和 f_Reset 都给了一套可复现的默认值:t_Last_Bar_Time=0、i_Prew_Calculated=WRONG_VALUE、b_First_Run 与 b_Is_New_Bar 置 true。 f_Update 的核心是新柱判定:用 TimeCurrent()-TimeCurrent()%PeriodSeconds() 算出当前柱开头时间,再和 t_Last_Bar_Time 比对。若相等则 b_Is_New_Bar=true,并刷新 t_Last_Bar_Time。它还顺手处理 OnCalculate 首次调用与历史数据变动(i_New_Prew_Calculated==0 且旧值非 WRONG_VALUE 时记 b_History_Updated=true),省得你在主循环里重复写这套判断。 OnDeinit 里只调一句 go_Brownie.f_Reset() 就能通知结构「逆初始化」,下次加载指标自动回到干净状态。若你的自定义类还需要开盘/最高/最低/收盘/成交量/点差,直接往 BROWNIE 里加字段,用 OnCalculate 现成数组传进去,替代 CopyOpen/CopyHigh/CopyRates 这类调用——能省 CPU,也避开那些函数错误分配的处理坑。 主函数里先投喂数据:go_Brownie.f_Update(prev_calculated)。接着算重算起点 i_Current_TF_Bar = rates_total - Bars_Limit;首启时清掉所有缓冲数组并重置 static 的 st_Last_D1_Bar、si_1st_Bar_of_Day。非首启则用 Bars(_Symbol,PERIOD_CURRENT,当日0点,t) 反推当日首根索引,把重算深度压到「从今天开始」,避免每 tick 全量回扫。 最后用 fmax/fmin 把 i_Current_TF_Bar 夹在 [0, rates_total-gi_Min_Bars] 内,while(++i_Current_TF_Bar<rates_total && !IsStopped()) 逐根跑。循环内先 fe_Get_Entry_Signal 取信号,e_Signal>1 就 continue 跳过无信号交易日。新交易日首根若出信号,要把上一交易日所有柱线范围补填,用 t_D1_Bar_To_Fill==WRONG_VALUE 作「无需填充」标志;信号线从首根拉到前日末根只为视觉连贯,多空块和填充色不同,所以拆成两个相似代码块处理。
class="kw">struct BROWNIE { class=class="str">"cmt">// Brownie: 在全局层面存储和处理数据的结构 class="type">class="kw">datetime t_Last_Bar_Time; class=class="str">"cmt">// 最后处理过的柱线时间 class="type">int i_Prew_Calculated; class=class="str">"cmt">// 已计算柱线的数量 class="type">bool b_First_Run; class=class="str">"cmt">// 首次启动标志 class="type">bool b_History_Updated; class=class="str">"cmt">// 历史更新标志 class="type">bool b_Is_New_Bar; class=class="str">"cmt">// 新柱线开盘标志 BROWNIE() { class=class="str">"cmt">// 构造器 class=class="str">"cmt">// 省缺值: t_Last_Bar_Time = class="num">0; i_Prew_Calculated = WRONG_VALUE; b_First_Run = b_Is_New_Bar = true; b_History_Updated = class="kw">false; } class="type">void f_Reset(class="type">bool b_Reset_First_Run = true) { class=class="str">"cmt">// 变量设置为零 class=class="str">"cmt">// 省缺值: t_Last_Bar_Time = class="num">0; i_Prew_Calculated = WRONG_VALUE; if (b_Reset_First_Run) b_First_Run = true; class=class="str">"cmt">// 如果允许则设为零 b_Is_New_Bar = true; b_History_Updated = class="kw">false; } class="type">void f_Update(class="type">int i_New_Prew_Calculated = WRONG_VALUE) { class=class="str">"cmt">// 更新变量 class=class="str">"cmt">// OnCalculate 内置函数首次调用标志 if (b_First_Run && i_Prew_Calculated > class="num">0) b_First_Run = class="kw">false; class=class="str">"cmt">// 新柱线? class="type">class="kw">datetime t_This_Bar_Time = TimeCurrent() - TimeCurrent() % PeriodSeconds(); b_Is_New_Bar = t_Last_Bar_Time == t_This_Bar_Time; class=class="str">"cmt">// 更新当前柱线时间? if (b_Is_New_Bar) t_Last_Bar_Time = t_This_Bar_Time; if (i_New_Prew_Calculated > -class="num">1) { class=class="str">"cmt">// 历史数据有任何变化? b_History_Updated = i_New_Prew_Calculated == class="num">0 && i_Prew_Calculated > WRONG_VALUE; class=class="str">"cmt">// 若首次调用 OnCalculate 的情况, 使用 prew_calculated if (i_Prew_Calculated == WRONG_VALUE) i_Prew_Calculated = i_New_Prew_Calculated; class=class="str">"cmt">// 或历史数据无更新 else if (i_New_Prew_Calculated > class="num">0) i_Prew_Calculated = i_New_Prew_Calculated; } } }; BROWNIE go_Brownie; class="type">void OnDeinit(class="kw">const class="type">int reason) { go_Brownie.f_Reset(); class=class="str">"cmt">// 通知 Brownie } go_Brownie.f_Update(prev_calculated); class=class="str">"cmt">// 为 Brownie 投送数据 class="type">int i_Period_Bar = class="num">0, class=class="str">"cmt">// 辅助计数器 i_Current_TF_Bar = rates_total - class="type">int(Bars_Limit); class=class="str">"cmt">// 当前时间帧循环开始的柱线索引 class="kw">static class="type">class="kw">datetime st_Last_D1_Bar = class="num">0; class=class="str">"cmt">// 一对 D1 时间帧柱线的最后一根已处理时间 (形态的第二根柱线) class="kw">static class="type">int si_1st_Bar_of_Day = class="num">0; class=class="str">"cmt">// 当日首根柱线的索引 if (go_Brownie.b_First_Run) { class=class="str">"cmt">// 如果这是首次启动 class=class="str">"cmt">// 在重新初始化期间清除缓存区: ArrayInitialize(buff_1st_Bar_Inner, class="num">0); ArrayInitialize(buff_1st_Bar_Inner_Zero, class="num">0); ArrayInitialize(buff_1st_Bar_Outer, class="num">0); ArrayInitialize(buff_1st_Bar_Outer_Zero, class="num">0); ArrayInitialize(buff_Entry, class="num">0); ArrayInitialize(buff_Entry_Color, class="num">0); ArrayInitialize(buff_Signal, class="num">0); ArrayInitialize(buff_Signal_Color, class="num">0); ArrayInitialize(buff_TP, class="num">0); ArrayInitialize(buff_SL, class="num">0); st_Last_D1_Bar = class="num">0; si_1st_Bar_of_Day = class="num">0; } else { class=class="str">"cmt">// 这不是首次启动 class="type">class="kw">datetime t_Time = TimeCurrent(); class=class="str">"cmt">// 重计算最小深度 - 从当日开始: i_Current_TF_Bar = rates_total - Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time % class="num">86400, t_Time) - class="num">1; } ENUM_ENTRY_SIGNAL e_Signal = ENTRY_UNKNOWN; class=class="str">"cmt">// 信号 class="type">class="kw">double d_SL = WRONG_VALUE, class=class="str">"cmt">// 止损位 d_TP = WRONG_VALUE, class=class="str">"cmt">// 止盈位 d_Entry_Level = WRONG_VALUE, class=class="str">"cmt">// 入场价位 d_Range_High = WRONG_VALUE, d_Range_Low = WRONG_VALUE; class=class="str">"cmt">// borders 形态第一根柱线范围的边界 class="type">class="kw">datetime t_Curr_D1_Bar = class="num">0, class=class="str">"cmt">// 当前 D1 柱线的时间 (形态的第二根柱线) t_D1_Bar_To_Fill = class="num">0; class=class="str">"cmt">// 要填充的 D1 柱线时间 (形态的第一根柱线) class=class="str">"cmt">// 确保重新计算的初始柱线索引在可接受的范围内: i_Current_TF_Bar = class="type">int(fmax(class="num">0, fmin(i_Current_TF_Bar, rates_total - gi_Min_Bars))); class="kw">while (++i_Current_TF_Bar < rates_total && !IsStopped()) { class=class="str">"cmt">// 遍历当前时间帧柱线 class=class="str">"cmt">// 主程序循环位于此处 } e_Signal = fe_Get_Entry_Signal(Time[i_Current_TF_Bar], d_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low); if (e_Signal > class="num">1) class="kw">continue; class=class="str">"cmt">// 柱线所属交易日内无信号
日内布局线的收口与触发逻辑
新 D1 柱线确认后,前一日的外层与内层区域会按阴线/阳线分别回填缓冲数组。外层取全天高低区间,内层则收缩 20%:以 d_Range_Low + 0.2*(d_Range_High - d_Range_Low) 为下沿、d_Range_High - 0.2*(d_Range_High - d_Range_Low) 为上沿,这个 0.2 系数直接决定通道厚度,调大则信号更宽松、触发更少。 信号线从昨日最后一根柱线起笔,沿 d_Range_Low - gd_Extremum_Break(阴线)或 d_Range_High + gd_Extremum_Break(阳线)延伸,一旦 Low/High 触碰即 break,因此 gd_Extremum_Break 是真实的突破缓冲距,外汇与贵金属波动大时建议实测 5~15 点区间。 入场、止损、止盈线在同一根当日柱线内交叉判定:若价格触到 d_Range_Low(阴线例)且前一根缓冲为 0,则向 past 延一根柱线补全显示;止损取当日最低/最高价 via ArrayMinimum/ArrayMaximum,止盈 d_TP 固定跟随。任一线被触碰即退出循环,形态布局在该柱线闭合。 循环外调用 f_Do_Alert 做通知,它比指标内建提示更宽:可载入音频文件,且买/卖信号能指定不同声音文件,属于可自定义的告警扩展点。
t_Curr_D1_Bar = Time[i_Current_TF_Bar] - Time[i_Current_TF_Bar] % class="num">86400; class=class="str">"cmt">// 柱线所属日的开始 if (st_Last_D1_Bar < t_Curr_D1_Bar) { class=class="str">"cmt">// 这是新一日的柱线 t_D1_Bar_To_Fill = Time[i_Current_TF_Bar - class="num">1] - Time[i_Current_TF_Bar - class="num">1] % class="num">86400; si_1st_Bar_of_Day = i_Current_TF_Bar; } else t_D1_Bar_To_Fill = WRONG_VALUE; class=class="str">"cmt">// 昨日柱线, 没有新的填充需求 st_Last_D1_Bar = t_Curr_D1_Bar; class=class="str">"cmt">// 记忆 if (t_D1_Bar_To_Fill != WRONG_VALUE) { class=class="str">"cmt">// 新的 D1 柱线 class=class="str">"cmt">// 填充昨日的 D1 柱线: i_Period_Bar = i_Current_TF_Bar; if (d_Entry_Level < d_Range_High) { class=class="str">"cmt">// D1 阴线 if (Show_Outer) class="kw">while (--i_Period_Bar > class="num">0) { class=class="str">"cmt">// 全部范围 if (Time[i_Period_Bar] < t_D1_Bar_To_Fill) class="kw">break; buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_Low; buff_1st_Bar_Outer[i_Period_Bar] = d_Range_High; } if (Show_Inner) { class=class="str">"cmt">// 内部区域 i_Period_Bar = i_Current_TF_Bar; class="kw">while (--i_Period_Bar > class="num">0) { if (Time[i_Period_Bar] < t_D1_Bar_To_Fill) class="kw">break; buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_Low + class="num">0.2 * (d_Range_High - d_Range_Low); buff_1st_Bar_Inner[i_Period_Bar] = d_Range_High - class="num">0.2 * (d_Range_High - d_Range_Low); } } class=class="str">"cmt">// 信号线的开始 — 从昨日的最后一根柱线 buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar - class="num">1] = d_Range_Low - gd_Extremum_Break; buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar - class="num">1] = class="num">0; } else { class=class="str">"cmt">// D1 阳线 if (Show_Outer) class="kw">while (--i_Period_Bar > class="num">0) { class=class="str">"cmt">// 全部范围 if (Time[i_Period_Bar] < t_D1_Bar_To_Fill) class="kw">break; buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_High; buff_1st_Bar_Outer[i_Period_Bar] = d_Range_Low; } if (Show_Inner) { class=class="str">"cmt">// 内部区域 i_Period_Bar = i_Current_TF_Bar; class="kw">while (--i_Period_Bar > class="num">0) { if (Time[i_Period_Bar] < t_D1_Bar_To_Fill) class="kw">break; buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_High - class="num">0.2 * (d_Range_High - d_Range_Low); buff_1st_Bar_Inner[i_Period_Bar] = d_Range_Low + class="num">0.2 * (d_Range_High - d_Range_Low); } } class=class="str">"cmt">// 信号线的开始 — 从昨日的最后一根柱线 buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar - class="num">1] = d_Range_High + gd_Extremum_Break; buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar - class="num">1] = class="num">1; } } else class="kw">continue; class=class="str">"cmt">// 信号线依旧与柱线交叉: i_Period_Bar = i_Current_TF_Bar; if (d_Entry_Level < d_Range_High) { class=class="str">"cmt">// D1 阴线 class="kw">while (++i_Period_Bar < rates_total) { if (Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) class="kw">break; buff_Signal[i_Period_Bar] = d_Range_Low - gd_Extremum_Break; buff_Signal_Color[i_Period_Bar] = class="num">0; if (d_Range_Low - gd_Extremum_Break >= Low[i_Period_Bar]) class="kw">break; } } else { class=class="str">"cmt">// D1 阳线 class="kw">while (++i_Period_Bar < rates_total) { if (Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) class="kw">break; buff_Signal[i_Period_Bar] = d_Range_High + gd_Extremum_Break; buff_Signal_Color[i_Period_Bar] = class="num">1; if (d_Range_High + gd_Extremum_Break <= High[i_Period_Bar]) class="kw">break; } } class=class="str">"cmt">// 入场线依旧与柱线交叉: if (d_Entry_Level < d_Range_High) { class=class="str">"cmt">// D1 阴线 class="kw">while (++i_Period_Bar < rates_total) { if (Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) class="kw">break; buff_Entry[i_Period_Bar] = d_Range_Low; buff_Entry_Color[i_Period_Bar] = class="num">0; if (d_Range_Low <= High[i_Period_Bar]) { if (buff_Entry[i_Period_Bar - class="num">1] == class="num">0.) { class=class="str">"cmt">// 在单根柱线上开始并结束, 延伸一根柱线到过去 buff_Entry[i_Period_Bar - class="num">1] = d_Range_Low; buff_Entry_Color[i_Period_Bar - class="num">1] = class="num">0; } class="kw">break; } } } else { class=class="str">"cmt">// D1 阳线 class="kw">while (++i_Period_Bar < rates_total) { if (Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) class="kw">break; buff_Entry[i_Period_Bar] = d_Range_High; buff_Entry_Color[i_Period_Bar] = class="num">1; if (d_Range_High >= Low[i_Period_Bar]) { if (buff_Entry[i_Period_Bar - class="num">1] == class="num">0.) { class=class="str">"cmt">// 在单根柱线上开始并结束, 延伸一根柱线到过去 buff_Entry[i_Period_Bar - class="num">1] = d_Range_High; buff_Entry_Color[i_Period_Bar - class="num">1] = class="num">1; } class="kw">break; } } } class=class="str">"cmt">// 止盈和止损线之一依旧与柱线交叉: if (d_Entry_Level < d_Range_High) { class=class="str">"cmt">// D1 阴线 class=class="str">"cmt">// 止损线等于日内最低价: d_SL = Low[ArrayMinimum(Low, si_1st_Bar_of_Day, i_Period_Bar - si_1st_Bar_of_Day)]; class="kw">while (++i_Period_Bar < rates_total) { if (Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) class="kw">break; buff_SL[i_Period_Bar] = d_SL; buff_TP[i_Period_Bar] = d_TP; if (d_TP <= High[i_Period_Bar] || d_SL >= Low[i_Period_Bar]) { if (buff_SL[i_Period_Bar - class="num">1] == class="num">0.) { class=class="str">"cmt">// 在单根柱线上开始并结束, 延伸一根柱线到过去 buff_SL[i_Period_Bar - class="num">1] = d_SL; buff_TP[i_Period_Bar - class="num">1] = d_TP; } class="kw">break; } } } else { class=class="str">"cmt">// D1 阳线 class=class="str">"cmt">// 止损线等于日内最高价: d_SL = High[ArrayMaximum(High, si_1st_Bar_of_Day, i_Period_Bar - si_1st_Bar_of_Day)]; class="kw">while (++i_Period_Bar < rates_total) { if (Time[i_Period_Bar] > t_Curr_D1_Bar + class="num">86399) class="kw">break; buff_SL[i_Period_Bar] = d_SL; buff_TP[i_Period_Bar] = d_TP; if (d_SL <= High[i_Period_Bar] || d_TP >= Low[i_Period_Bar]) { if (buff_SL[i_Period_Bar - class="num">1] == class="num">0.) { class=class="str">"cmt">// 在单根柱线上开始并结束, 延伸一根柱线到过去 buff_SL[i_Period_Bar - class="num">1] = d_SL; buff_TP[i_Period_Bar - class="num">1] = d_TP; } class="kw">break; } } }
◍ 正文
| void f_Do_Alert( // 搜索信号和通知的函数 string s_Message, // 警报消息 bool b_Alert = true , // 显示弹出窗口? bool b_Sound = false , // 播放音频文件? bool b_Email = false , // 发送邮件? bool b_Notification = false , // 发送推送通知? string s_Email_Subject = "" , // 邮件标题 string s_Sound = "alert.wav" // 音频文件 ) { static string ss_Prev_Message = "三是静音" ; // 之前的警报消息 static datetime st_Prev_Time; // 之前的警报柱线时间 datetime t_This_Bar_Time = TimeCurrent () — PeriodSeconds () % PeriodSeconds (); // 当前柱线时间 if (ss_Prev_Message != s_Message | st_Prev_Time != t_This_Bar_Time) { // 另一个 和/或 第一次 // 记忆: ss_Prev_Message = s_Message; st_Prev_Time = t_This_Bar_Time; // 发自消息串: s_Message = StringFormat ( "%s | %s | %s | %s" , TimeToString ( TimeLocal (), TIME_SECONDS ), // 本地时间 _Symbol , // 品种 StringSubstr ( EnumToString ( ENUM_TIMEFRAMES ( _Period )), 7 ), // 时间帧 s_Message // 消息 ); // 激活通知信号: if (b_Alert) Alert (s_Message); if (b_Email) SendMail (s_Email_Subject + " " + _Symbol , s_Message); if (b_Notification) SendNotification (s_Message); if (b_Sound) PlaySound (s_Sound); } } |
|---|
在 OnCalculate 事件处理程序完成之前检查是否需要调用函数, 并在程序主体中形成文本的代码: // 警报 i_Period_Bar = rates_total — 1 ; // 当前柱线 if (Alert_Popup + Alert_Email + Alert_Push == 0 ) return (rates_total); // 禁用所有 if (buff_Signal[i_Period_Bar] == 0 ) return (rates_total); // 尚未捕获