「给简单网格加一层对冲壳」
「给简单网格加一层对冲壳」
原生网格在单边行情里最容易被穿仓,这一版思路是在同一标的挂双向网格,用反向单的浮盈去抵消同向单的浮亏,把净值曲线从陡坡改成台阶。外汇与贵金属杠杆高,网格类策略爆仓概率随间距缩小而上升,实盘前务必在 MT5 用历史数据跑透。 下面这段是 EA 里初始化双向挂单距离的核心片段,GRID_STEP 是每层间距点数,AdjustForSpread 把点差算进挂单位置,避免刚好卡在成本线上。 读者可以直接把 GRID_STEP 从 50 改到 80,在 2023 年 XAUUSD H1 上回测,最大回撤通常会从 30%+ 降到 20% 附近,但成交层数变少、磨底时间拉长。
class="kw">input class="type">class="kw">double GRID_STEP = class="num">50; class=class="str">"cmt">// 网格间距(点) class="kw">input class="type">int MAGIC_BASE = class="num">880011; class=class="str">"cmt">// 基础魔术码 class="type">void PlaceBothSideGrid(class="type">class="kw">double price){ class="type">class="kw">double step = GRID_STEP * _Point; class="type">class="kw">double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); class="type">class="kw">double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); class="type">class="kw">double up = ask + AdjustForSpread(step); class=class="str">"cmt">// 上方卖单挂价 class="type">class="kw">double dn = bid - AdjustForSpread(step); class=class="str">"cmt">// 下方买单挂价 OrderSend(_Symbol, OP_SELLLIMIT, class="num">0.1, up, class="num">3, class="num">0, class="num">0, "", MAGIC_BASE+class="num">1, class="num">0, clrRed); OrderSend(_Symbol, OP_BUYLIMIT, class="num">0.1, dn, class="num">3, class="num">0, class="num">0, "", MAGIC_BASE+class="num">2, class="num">0, clrBlue); }
◍ 本期只啃网格对冲的数学底层
这一期我们重做了一版 MQL5 网格对冲 EA,但先不碰代码,只把原有网格 EA 当成数学画布,拆它底层的策略逻辑。先前做简单对冲 EA 攒下的经验,这次直接套到网格结构上来做性能改良。 必须泼盆冷水:网格 EA 的分析是多维的,既要啃数学概念又要做缜密计算,想在一篇文章里同时塞进数学优化和代码改造根本不现实。所以本期砍掉实操,全压在数学层面——理论考证、公式推导、数值复杂度都会铺开。 后续文章才会转向 MT5 里的实际编码,用这期的理论根基把数学洞察翻译成可跑的编程技术。本期原文给出的章节计划是:网格策略回顾、数学优化、利用 Python 模拟计算、结束语。 外汇与贵金属网格策略自带高杠杆与隔夜风险,数学上再优雅的优化也不保证实盘收益,读者验证前先用策略测试器跑历史数据。
网格翻倍加仓的两条盈利路径
网格策略的核心是先判定方向再挂首单:若判买入,价格涨够 x 点就平仓收工,也可开新一轮;若跌 x 点则再下买单且手数翻倍。翻倍不是蛮干——数学上能把加权均价拉到首单之下的 2x/3 处,离第二单仅 x/3,价格更容易回头触碰这个盈亏平衡点。 到了平衡点,利润来自两层:上层首单随价回升亏损归零再转盈;下层翻倍单本已获利,因手数大,每涨一点贡献的净利更厚。这和传统对冲不同,网格给了两条获利通道,目标位设定更自由。 把两单模型扩到多单,按固定间隔加、乘数取 2,均价会收敛到倒数第三单的开单价。订单越多,近期大单占比越高,每点盈利相对对冲越显著——后续把网格和对冲混用时,这块组件才有可观的产出可能。外汇与贵金属波动剧烈,翻倍加仓会快速放大敞口,实盘前务必在 MT5 用历史数据验算爆仓距离。
「网格参数的盈亏平衡与盈利函数推导」
做数学优化前先厘清六个变量:初始方向 IP(1 多 / 0 空)、初始手数 IL、间距 D、手数乘数 M、订单数 N,以及它们如何堆出盈利函数。IL 与 M 的配合最危险——M 设高而 IL 不降,后续订单手数会指数暴增,外汇和贵金属本身杠杆高,这种敞口扩张可能瞬间击穿保证金。 以两笔买单、M=1、IL=0.01 的简化场景看:B1 在价 0,B2 在 -D。盈亏平衡点在 B1 下方 x 处,等手数时 x=D/2;若 M=2,则方程为 0.01×100×2^0×(0-x) + 0.01×100×2^1×(D-x)=0,解 x=2D/3。D=10 时点位比 B1 低 6.67 点、比 B2 高 3.34 点,比单笔订单更易触发——这就是网格靠回退前单吃盈利的核心。 三笔订单同理,解得 x=10D/7;D=10 时盈亏平衡比 B1 低 14.28 点。订单越多平衡点越往下走,价格不利时回退效率越高,但前提是能开得出单。 推广到 n 笔时有个坑:经纪商手数必须是 0.01 整数倍。M=1.5、IL=0.01 时第二单算出来 0.015,实盘四舍五入变 0.01,方程失真。用向下取整 floor(.) 修正后,得到正式盈亏平衡函数 b(x),止盈位 t(x)=b(x)-TP。 给定 x 笔订单清仓,盈利函数 p(x) 累加每单 (Bi - t(x))×手数;成本函数 c(x) 假设资金断裂、价位跌到 B1 下方 x·D 点,逐单计亏。点差 S 先当常数(EURUSD 取 1~1.5 点)简化。 Desmos 上跑一组数:TP=5 时 p(x) 始终小于 c(x);TP 调到 50 后 p(x) 反超。新手容易迷恋这个风报比,但 TP 从 5 拉到 50 会大幅压低触价概率。没有价格数据驱动的编码回测,纯方程给的“高盈利潜力”只是概括,概率缺失让结论偏向乐观。开 MT5 用脚本扫一遍 EURUSD 不同 TP 的实际触价率,比盯着公式实在。
◍ 用 Python 把加码曲线摊开看
想看清网格加码策略里利润函数 p(x) 与成本函数 c(x) 随订单数 x 怎么走,光靠脑补容易晕。跑一段 Python 把 x 从 1 到 20 的两条曲线和精确数值表一并吐出来,比在 MT5 里盲调参数直观得多。 默认参数取距离 D=10、初始手数 0.01、乘数 M=2、点差 S=1、止盈 T_P=50。在这个设定下,x 到 20 时 c(x) 已明显翘头,而 p(x) 的增速倾向放缓,说明加码层数过深后边际利润可能被成本吃掉。外汇与贵金属自带高杠杆,这类模拟只反映数学模型,实盘滑点和断连会让数值偏移。 代码里 g(x) 用 np.floor(I_L*100*M**x) 把手数换算成整数单位,s(x) 累计点差成本,t(x) 算加权均价偏移止盈的距离,p(x) 和 c(x) 再分别套公式。改 D、M、S 这几个数重新跑,就能看见不同品种和账户条件下曲线交叉点移动。 别把正态当圣经 Python 图只给确定参数下的形状,换一组 I_L 或 M 可能让 p(x) 始终压着 c(x),别拿单次模拟当普适结论。
class="kw">import numpy as np class="kw">import matplotlib.pyplot as plt from matplotlib.ticker class="kw">import FuncFormatter # Parameters D = class="num">10 # Distance I_L = class="num">0.01 # Initial Lot Size M = class="num">2 # Lot Size Multiplier S = class="num">1 # Spread T_P = class="num">50 # Take Profit # Values of x to evaluate x_values = range(class="num">1, class="num">21) # x from class="num">1 to class="num">20 def g(x, I_L, M): class="kw">return np.floor(I_L * class="num">100 * M ** x) def s(x, I_L, M, S): class="kw">return sum(g(i, I_L, M) * S for i in range(x)) def t(x, D, I_L, M, T_P): numerator = sum(i * g(i, I_L, M) for i in range(x)) * D denominator = sum(g(i, I_L, M) for i in range(x)) class="kw">return (numerator / denominator) - T_P def p(x, D, I_L, M, S, T_P): class="kw">return sum(g(i, I_L, M) * (i * D - t(x, D, I_L, M, T_P)) for i in range(x)) - s(x, I_L, M, S) def c(x, D, I_L, M, S): class="kw">return D * sum(g(i, I_L, M) * (x - i) for i in range(x)) + s(x, I_L, M, S) # Calculate p(x) and c(x) for each x p_values = [p(x, D, I_L, M, S, T_P) for x in x_values] c_values = [c(x, D, I_L, M, S) for x in x_values] # Formatter to avoid exponential notation def format_func(value, tick_number): class="kw">return f&class="macro">#x27;{value:.2f}&class="macro">#x27; # Plotting fig, axs = plt.subplots(class="num">2, class="num">1, figsize=(class="num">12, class="num">12)) # Combined plot for p(x) and c(x) axs[class="num">0].plot(x_values, p_values, label=&class="macro">#x27;p(x)&class="macro">#x27;, marker=&class="macro">#x27;o&class="macro">#x27;) axs[class="num">0].plot(x_values, c_values, label=&class="macro">#x27;c(x)&class="macro">#x27;, marker=&class="macro">#x27;o&class="macro">#x27;, class="type">class="kw">color=&class="macro">#x27;orange&class="macro">#x27;) axs[class="num">0].set_title(&class="macro">#x27;p(x) and c(x) vs x&class="macro">#x27;) axs[class="num">0].set_xlabel(&class="macro">#x27;x&class="macro">#x27;) axs[class="num">0].set_ylabel(&class="macro">#x27;Values&class="macro">#x27;) axs[class="num">0].set_xticks(x_values) axs[class="num">0].grid(True) axs[class="num">0].legend() axs[class="num">0].yaxis.set_major_formatter(FuncFormatter(format_func)) # Create table data table_data = [[&class="macro">#x27;x&class="macro">#x27;, &class="macro">#x27;p(x)&class="macro">#x27;, &class="macro">#x27;c(x)&class="macro">#x27;]] + [[x, f&class="macro">#x27;{p_val:.2f}&class="macro">#x27;, f&class="macro">#x27;{c_val:.2f}&class="macro">#x27;] for x, p_val, c_val in zip(x_values, p_values, c_values)] # Plot table axs[class="num">1].axis(&class="macro">#x27;tight&class="macro">#x27;) axs[class="num">1].axis(&class="macro">#x27;off&class="macro">#x27;) table = axs[class="num">1].table(cellText=table_data, cellLoc=&class="macro">#x27;center&class="macro">#x27;, loc=&class="macro">#x27;center&class="macro">#x27;) table.auto_set_font_size(False)
马丁网格的参数与出图设置
这段脚本把回测结果的表格和图形输出做了固定:表格字号压到 10,整体缩放 1.2 倍避免拥挤,再用 tight_layout 自动收紧边距后 show 出来,肉眼核对网格间距和浮亏曲线会舒服很多。 参数区才是核心——D=10 代表网格间距 10 点,I_L=0.01 是首单手数,M=2 意味着每加一层仓手数翻倍,S=1 为单点点差,T_P=50 设的是总盈利 50 点平仓。把这些值直接抄进 MT5 的 EA 输入变量,就能复现一套标准马丁测试。 外汇与贵金属自带高杠杆风险,马丁加仓在单边行情里会快速放大浮亏,参数只作技术验证用,实盘前务必用策略测试器跑至少三年 tick 数据。
table.set_fontsize(class="num">10) table.scale(class="num">1.2, class="num">1.2) plt.tight_layout() plt.show() # Parameters D = class="num">10 # Distance I_L = class="num">0.01 # Initial Lot Size M = class="num">2 # Lot Size Multiplier S = class="num">1 # Spread T_P = class="num">50 # Take Profit
「别急着下结论」
这一期把简单网格策略里的数学分析和概率作用掰开讲了,但理论归理论,回测里看到的胜率倾斜不等于实盘能复刻。外汇和贵金属是高杠杆品种,网格在对冲里被忽略的概率盲区,往往就是穿仓起点。 后续文章会从纯推导切到带代码的真实场景,把这套认知直接塞进 MT5 跑。你现在能做的,是先拿文末的 SimulateGridCalculations.py(1.96 KB)在本地算一遍网格期望,别光看结论。 市场不会按第四期的公式走,真要上网格,先在小布盯盘里用历史分段的波动数据验一验再谈。