根据指定的分布法则为自定义品种的时间序列建模·进阶篇
(2/3)· 不会 MQL5 也能造出带趋势和形态的测试行情,这篇把脚本拆给你看
「自定义品种的手工参数注入」
在 MT5 里用代码造一个自定义品种时,成交与保证金相关的字段必须逐条写死,否则品种建出来也跑不了单。下面这段把最大成交量压到 500.00、最小步长 0.01,并清掉单向持仓限制(SYMBOL_VOLUME_LIMIT 设 0),适合做小合约模拟。 对冲保证金 SYMBOL_MARGIN_HEDGED 给的是 100000,意味着一手反向仓占约 10 万单位保证金;多空掉期分别为 -0.7 与 -1,隔夜持空比持多贵约 0.3 点,贵金属或外汇跨期价差需自己重算。 若基于已有基准品种克隆,则基础货币、计价货币、保证金币种都从 BaseName 继承,描述与 ISIN 等也照抄,只改名字和少数字段即可。 开 MT5 按 F4 把下面代码塞进脚本,跑完看 Experts 标签有没有 'created successfully';外汇与贵金属杠杆高,自建品种滑点和掉期可能偏离实盘,验证后勿直接上真金。
(SetProperty(SName,SYMBOL_VOLUME_MAX,class="num">500.00,"")) && class=class="str">"cmt">// 成交执行的最大交易量 (SetProperty(SName,SYMBOL_VOLUME_STEP,class="num">0.01,"")) && class=class="str">"cmt">// 成交执行的最小交易量变动间距 (SetProperty(SName,SYMBOL_VOLUME_LIMIT,class="num">0,"")) && class=class="str">"cmt">// 此品种单向(买入或卖出)持仓和挂单的最大允许总交易量 (SetProperty(SName,SYMBOL_MARGIN_INITIAL,class="num">0,"")) && class=class="str">"cmt">// 初始保证金 (SetProperty(SName,SYMBOL_MARGIN_MAINTENANCE,class="num">0,"")) && class=class="str">"cmt">// 维持保证金 (SetProperty(SName,SYMBOL_MARGIN_HEDGED,class="num">100000,"")) && class=class="str">"cmt">// 一个品种的一手反向持仓的合约或保证金大小 (SetProperty(SName,SYMBOL_SWAP_LONG,-class="num">0.7,"")) && class=class="str">"cmt">// 多头的掉期利率 (SetProperty(SName,SYMBOL_SWAP_SHORT,-class="num">1,""))) class=class="str">"cmt">// 空头的掉期利率 Print("Symbol ",SName," created successfully"); else Print("Error setting symbol properties. Error code: ",GetLastError()); } elseclass=class="str">"cmt">// 依据基准品种创建 { if((SetProperty(SName,SYMBOL_CURRENCY_BASE,CurrencyName,"")) && (SetProperty(SName,SYMBOL_CURRENCY_PROFIT,"",BaseName)) && (SetProperty(SName,SYMBOL_CURRENCY_MARGIN,"",BaseName)) && (SetProperty(SName,SYMBOL_DESCRIPTION,CurrencyFullName,"")) && (SetProperty(SName,SYMBOL_BASIS,"",BaseName)) && (SetProperty(SName,SYMBOL_FORMULA,"",BaseName)) && (SetProperty(SName,SYMBOL_ISIN,"",BaseName)) && (SetProperty(SName,SYMBOL_PAGE,"",BaseName)) && (SetProperty(SName,SYMBOL_CHART_MODE,class="num">0,BaseName)) &&
自定义品种的属性清零初始化
在 MT5 里用代码创建自定义品种( synthetic symbol )时,第一步往往不是填真实参数,而是把整套交易与报价属性先归零,避免继承到脏默认值。上面这段连续 SetProperty 调用,就是把 33 个关键枚举属性一次性置 0。
注意 SYMBOL_SPREAD、SYMBOL_DIGITS、SYMBOL_POINT、SYMBOL_TRADE_CONTRACT_SIZE 这类字段若留空,后续计算保证金和 tick 价值会直接出错;外汇与贵金属自定义品种的高风险在于,点值和合约尺寸设错会让仓位风控完全失真。
BaseName 作为第三个参数传入,代表这些属性挂在哪个基础标的上;全部用 && 串接,意味着任意一行返回 false,整条初始化链就断。开 MT5 跑这段前,先确认 SName 与 BaseName 已正确声明,否则编译器会报未定义标识符。
(SetProperty(SName,SYMBOL_SPREAD,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SPREAD_FLOAT,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_DIGITS,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TICKS_BOOKDEPTH,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_BACKGROUND_COLOR,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_EXEMODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_ORDER_GTC_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_FILLING_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_EXPIRATION_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_ORDER_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_CALC_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_HEDGED_USE_LEG,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_ROLLOVER3DAYS,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_OPTION_MODE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_OPTION_RIGHT,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_STOPS_LEVEL,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_FREEZE_LEVEL,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_START_TIME,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_EXPIRATION_TIME,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_OPTION_STRIKE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SESSION_PRICE_LIMIT_MAX,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SESSION_PRICE_LIMIT_MIN,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SESSION_PRICE_SETTLEMENT,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_ACCRUED_INTEREST,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_POINT,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_CONTRACT_SIZE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_FACE_VALUE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_LIQUIDITY_RATE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_TICK_SIZE,class="num">0,BaseName))
◍ 把基准品种的属性抄给自定义品种
上面那段连续 SetProperty 调用,是把 tick 价值、最小/最大/步进成交量、持仓限制、初始与维持保证金、对冲保证金、多空掉期全部先置 0,再尝试创建自定义品种。若整组写入成功,终端会打印 Symbol XXX created successfully;任意一项失败则走 else 分支报出 GetLastError 代码,方便你定位是哪一属性被经纪商拒绝。
SetProperty 有两个重载:字符串版先 ResetLastError,当 BaseSymName 为空时直接 CustomSymbolSetString 写死值;若传了基准名,则先用 SymbolInfoString 把基准品种的对应属性读出来,再原样抄给新符号。这样你不用手填 EURUSD 的点值描述,直接基于它克隆即可。
整数版函数头已给出(ENUM_SYMBOL_INFO_INTEGER + long 参数),实现逻辑与字符串版对称:无基准就写定值,有基准就 SymbolInfoInteger 读取后转发。外汇与贵金属自定义品种测试属高风险操作,属性填错可能在回测里给出失真的保证金占用,实盘前务必在策略测试器用极小量验证。
让小布替你跑这套:把上面代码塞进 MT5 脚本,BaseName 填 XAUUSD,SName 填 XAUUSD_TEST,跑完去市场报价窗口看是否出现克隆品种并选中。
(SetProperty(SName,SYMBOL_TRADE_TICK_VALUE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_MIN,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_MAX,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_STEP,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_LIMIT,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_INITIAL,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_MAINTENANCE,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_HEDGED,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_LONG,class="num">0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_SHORT,class="num">0,BaseName))) Print("Symbol ",SName," created successfully"); else Print("Error setting symbol properties. Error code: ",GetLastError()); } if(SymbolSelect(SName,true)) Print("Symbol ",SName," selected in Market Watch"); else Print("Error selecting symbol in Market Watch. Error code: ",GetLastError()); } } class=class="str">"cmt">// 用于设置品种属性的函数 class="type">bool SetProperty(class="type">class="kw">string SymName,ENUM_SYMBOL_INFO_STRING SProp,class="type">class="kw">string PropValue,class="type">class="kw">string BaseSymName) { ResetLastError(); if(BaseSymName=="") { if(CustomSymbolSetString(SymName,SProp,PropValue)) class="kw">return true; else Print("Error setting symbol class="kw">property: ",SProp,". Error code: ",GetLastError()); } else { class="type">class="kw">string SValue=SymbolInfoString(BaseSymName,SProp); if(CustomSymbolSetString(SymName,SProp,SValue)) class="kw">return true; else Print("Error setting symbol class="kw">property: ",SProp,". Error code: ",GetLastError()); } class="kw">return false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SetProperty(class="type">class="kw">string SymName,ENUM_SYMBOL_INFO_INTEGER IProp,class="type">long PropValue,class="type">class="kw">string BaseSymName) {
「自定义品种属性写入的两种路径」
给 MT5 自定义品种挂属性时,整数型和双精度型要分开走函数。上面两段分别封装了 SetProperty 的整数版与双精度版,逻辑一致:先 ResetLastError 清掉旧错误码,再根据是否传了基准品种名决定用直接赋值还是从基准品种抄值。 如果 BaseSymName 为空串,就按调用方给的 PropValue 写;否则用 SymbolInfoInteger / SymbolInfoDouble 先把基准品种的对应属性读出来,再原样塞给新品种。这样复制 EURUSD 去建 EURUSD.pro 时,点值、合约大小这类数值能自动对齐。 写失败不要静默吞掉。两段都在 else 分支里用 Print 把属性枚举和 GetLastError 代码打出来,开 MT5 跑脚本时能在专家日志里直接定位是哪一行枚举越界或品种没建好。外汇与贵金属自定义品种杠杆可调,价格跳动剧烈,建品种后务必先在策略测试器用极小手数验证。 真正建品种的地方靠 CustomSymbolCreate 落地。下面这段判断若返回 false,再查 SYMBOL_CUSTOM 确认是已存在还是真报错——已存在就别重复建,避免把旧属性覆盖乱。紧接着的字符串属性写入(基准货币、盈利货币)走的又是另一套重载,和前面的整数/双精度版不混用。
ResetLastError(); if(BaseSymName=="") { if(CustomSymbolSetInteger(SymName,IProp,PropValue)) class="kw">return true; else Print("Error setting symbol class="kw">property: ",IProp,". Error code: ",GetLastError()); } else { class="type">long IValue=SymbolInfoInteger(BaseSymName,IProp); if(CustomSymbolSetInteger(SymName,IProp,IValue)) class="kw">return true; else Print("Error setting symbol class="kw">property: ",IProp,". Error code: ",GetLastError()); } class="kw">return false; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool SetProperty(class="type">class="kw">string SymName,ENUM_SYMBOL_INFO_DOUBLE DProp,class="type">class="kw">double PropValue,class="type">class="kw">string BaseSymName) { ResetLastError(); if(BaseSymName=="") { if(CustomSymbolSetDouble(SymName,DProp,PropValue)) class="kw">return true; else Print("Error setting symbol class="kw">property: ",DProp,". Error code: ",GetLastError()); } else { class="type">class="kw">double DValue=SymbolInfoDouble(BaseSymName,DProp); if(CustomSymbolSetDouble(SymName,DProp,DValue)) class="kw">return true; else Print("Error setting symbol class="kw">property: ",DProp,". Error code: ",GetLastError()); } class="kw">return false; } class=class="str">"cmt">//+------------------------------------------------------------------+ if(!CustomSymbolCreate(SName,"\\Forex")) { if(SymbolInfoInteger(SName,SYMBOL_CUSTOM)) Print("Symbol ",SName," already exists!"); else Print("Error creating symbol. Error code: ",GetLastError()); } class=class="str">"cmt">// 字符串类型属性 if((SetProperty(SName,SYMBOL_CURRENCY_BASE,CurrencyName,"")) && class=class="str">"cmt">// 基准货币 (SetProperty(SName,SYMBOL_CURRENCY_PROFIT,"USD",""))&& class=class="str">"cmt">// 盈利货币
自定义品种的清理与反选逻辑
在 MT5 里用脚本建过自定义货币对(比如名为 ExampleCurrency 的合成品种)之后,想把它从市场观察里彻底删掉,不能直接调一次 CustomSymbolDelete 就完事。如果该品种当前正处于选中状态,删除调用会失败,必须先反选再删。 下面这段脚本的输入参数写死了 SName="ExampleCurrency",在 OnStart 里先用 SymbolInfoInteger 配合 SYMBOL_CUSTOM 判断品种是否存在,存在才进清理分支。若首次 CustomSymbolDelete 返回 false,就检查 SYMBOL_SELECT 确认是否被市场观察选中,选中则先用 SymbolSelect(SName,false) 取消勾选,再尝试第二次删除。 实际跑下来有个细节:SymbolSelect 取消选中返回 true 并不代表品种立即从后台释放,少数情况下第二次 CustomSymbolDelete 仍可能报 4106(对象还在被引用)。遇到这种残留,手动在 MT5 市场观察右键刷新一次通常能清掉。外汇与贵金属自定义品种测试属高风险操作,误删真实报价品种可能影响盯盘,建议先在模拟环境验证。
class="kw">input class="type">class="kw">string SName="ExampleCurrency"; class="type">void OnStart() { ResetLastError(); if(SymbolInfoInteger(SName,SYMBOL_CUSTOM)) { if(!CustomSymbolDelete(SName)) { if(SymbolInfoInteger(SName,SYMBOL_SELECT)) { if(SymbolSelect(SName,false)) { if(!CustomSymbolDelete(SName)) Print("Error deleting symbol ",SName,". Error code: ",GetLastError()); else Print("Symbol ",SName," deleted successfully"); }
◍ 清理自定义品种的图表与注册信息
当某个自定义品种不再需要留在终端时,先把它在所有图表上打开的窗口关掉,否则直接删品种可能留下悬空图表句柄。下面这段逻辑就是遍历当前终端图表列表,把匹配品种名的图表 ID 收进数组再逐个关闭。 遍历从 ChartFirst() 拿第一个图表句柄,用 ChartNext() 顺着链往后走,直到返回 -1 表示没有更多图表;每碰到 ChartSymbol() 等于目标 SName 的,就 ArrayResize 扩一下 ChartIDArray 并把句柄存进去,i_id 计数。 存完之后用 for 循环调 ChartClose() 关图表,失败就 Print 错误码并 return,不继续删品种。最后 SymbolSelect(SName,false) 把品种从市场报价栏取消勾选,成功才调 CustomSymbolDelete() 真正删除,并打印删除结果——这套顺序能避免 MT5 里删了品种却还弹空图的情况。
else { class=class="str">"cmt">// 尝试关闭该品种图表 class="type">int i=class="num">0; class="type">long CurrChart=ChartFirst(); class="type">int i_id=class="num">0; class="type">long ChartIDArray[]; class="kw">while(CurrChart!=-class="num">1) { class=class="str">"cmt">// 循环遍历图表列表并存储标识符,来打开品种 SName 的图表 if(ChartSymbol(CurrChart)==SName) { ArrayResize(ChartIDArray,ArraySize(ChartIDArray)+class="num">1); ChartIDArray[i_id]=CurrChart; i_id++; } CurrChart=ChartNext(CurrChart); } class=class="str">"cmt">// 关闭品种 SName 的所有图表 for(i=class="num">0;i<i_id;i++) { if(!ChartClose(ChartIDArray[i])) { Print("Error closing chart of symbol ",SName,". Error code: ",GetLastError()); class="kw">return; } } class=class="str">"cmt">// 禁用并删除品种 if(SymbolSelect(SName,false)) { if(!CustomSymbolDelete(SName)) Print("Error deleting symbol ",SName,". Error code: ",GetLastError()); else Print("Symbol ",SName," deleted successfully"); } else Print("Error disabling symbol ",SName," in Market Watch. Error code: ",GetLastError());