同时交易多种工具时平衡风险(基础篇)
📘

同时交易多种工具时平衡风险(基础篇)

第 1/2 篇

◍ 多品种同跑时怎么把风险摁住

在 MT5 里同时跑好几个品种做日内交易,账户整体回撤往往不是单个标的波动率能解释的,而是相关性叠加后的尾部暴露。2024-09-20 有社区示例贴提到,同时交易多工具的思路可以从头写代码实现,也能给老手提供以前没试过的旧办法新写法。 要把风险平衡落地,第一步是先定义清楚“风险”在你的系统里指什么:是单笔止损占比、是组合 VaR、还是日内最大回撤?标准终端本身给了基础接口,但真正做组合层控制得自己写平衡逻辑。 技术实现上,建议先盘点终端原生能力(比如通过 PositionSelect 跨品种查持仓),再决定哪些必须自研。后面几节会拆标准终端能力集,以及怎么把算法塞进你的软件基础设施里。外汇和贵金属杠杆高,多品种同开仓会放大滑点与隔夜风险,参数没调好之前先用模拟盘验证。

「用分时价格与日均波动率给多品种风险称重」

同时跑几个交易品种时,风险平衡得看两个硬指标:一个是分时价格,也就是标准手下最小价格变动对应的货币值;另一个是日平均波动率。前者在不同品种间差距能拉得很开,比如 EURGBP 报价在 1.27042 附近,而 AUDNZD 只有 0.61374,同样 1 点跳动,账户里的盈亏分量完全不同。 日均波动率描述一天里价格通常能走多远,它比分时价格更不恒定,会随市场阶段漂移。EURGBP 日常平均大约波动 336 点,CHFJPY 同一天能跑 1271 点,差不多是前者的四倍。这组数只代表“通常”和“最可能”的波动,没覆盖价格单边猛冲不回调的异常时段——USDJPY 在 D1 上就常有这种波动率陡增。 异常波动是另一类风险,靠品种间平衡防不住。真碰到“黑天鹅”,得靠几条原则压损失:别在单一品种上押大比例风险;别长期扛未平仓位;别把所有资金塞进同一经纪商同一个账户;别在多个账户和经纪商复刻同一笔交易。外汇和贵金属本身高风险,异常波动可能瞬间吞掉本金,这几条是底线而非收益承诺。 把分时价格和日均波动率一起算,能让多货币对同跑时风险互相抵消、预期利润normalize,不至于被某个品种带偏。往后交易历史统计会更均匀,策略优化器处理时样本与均值的标准偏差更小、误差更低。接下来得挑个容器把这类数据存起来。

数组还是 vector,先看内存账

给 EA 选数据容器时,别只盯着写法顺不顺手。性能和占用的内存才是硬指标,尤其 MT5 跑多品种平衡逻辑时,容器选错会让初始化阶段就吃掉不必要的资源。 直接拉一段代码验证:同样塞两个 double 值,普通数组和 vector 在编译期的 sizeof 结果分别是 16 字节和 128 字节。差出来的 112 字节,是 vector 为了内置方法和冗余分配换来的——它自带一堆标准操作,但代价就是更胖。 如果任务只是原样存下筛选好的品种数据,简单数组就够用,别为用不上的功能买单。等到算法里要频繁处理同质的数值序列,再切到 vector,能省掉自己写排序、求和那堆底层函数的开发时间。 下面这段声明就是实盘里算平衡仓位的骨架:symbols 存品种名,tick_val、atr、volume、point 各开一个数组装价格变动、波动、计算仓位和点值,risk_contract 用 vector 兜住标准合约风险额。开 MT5 新建脚本粘进去跑一遍 Print,内存差肉眼可见。

MQL5 / C++
  class="type">class="kw">double arr[] = {class="num">1.5, class="num">2.3};
  vector<class="type">class="kw">double> vect = {class="num">1.5, class="num">2.3};
Print(class="kw">sizeof(arr));
Print(class="kw">sizeof(vect));
  class="type">class="kw">string symbols[];        class=class="str">"cmt">// symbols used for balancing
  class="type">class="kw">double tick_val[],       class=class="str">"cmt">// symbol tick price
        atr[],             class=class="str">"cmt">// symbol volatility
        volume[],          class=class="str">"cmt">// calculated position volume for symbols taking into account balancing 
        point[];           class=class="str">"cmt">// value of one price change point 
  vector<class="type">class="kw">double> risk_contract; class=class="str">"cmt">// risk amount for a standard contract

◍ 多品种输入的六种落地路径

在 MT5 里给脚本塞一堆交易品种名,全局看就两条路:要么走标准对话框跟人直接交互,要么借磁盘文件或远程接口跟别的程序打交道。本文聚焦 string 格式的多品种入参,列了六种可能实现方式,利弊先摊开说。 从 *.csv 读最省事,用标准文件函数就能跑,用户拿 Excel 甚至记事本写个表格就行。*.bin 得靠 FileLoad(),但这个函数处理不了含 string 的结构体,要绕路做字符 id 映射,性价比低。 .sqlite 是终端内置关系库,符号 ID 存成整数省磁盘,特定条件下效率不错。远程库(如 MySQL)要第三方库,比内置方案费人力;web API 用 WebRequest() 最跨平台,但没后端基建就别从头搭。 本实现选了第六种:input 修饰的 string 变量,不预先声明一堆变量,而用一行空格分隔的字符串装全部品种。下面代码块展示了前几种文件读取的骨架,以及 sqlite 打开方式。 核心填充靠 StringSplit() 按空格拆进 symbols[] 数组,用户默认值照着空格隔开写就不会错。拿到数组即可请求各品种数据做风险平衡。外汇与贵金属波动剧烈,多品种同跑放大暴露,实盘前务必在策略测试器验证参数分隔逻辑。

MQL5 / C++
  class="type">class="kw">string file_name = "Inputs.csv";                                            class=class="str">"cmt">// file name
  class="type">int handle=FileOpen(file_name,FILE_CSV|FILE_READ,";");                    class=class="str">"cmt">// attempt to find and open the file
  if(handle!=INVALID_HANDLE)                                                class=class="str">"cmt">// if the file is found, then
    {
      while(FileIsEnding(handle)==false)                                    class=class="str">"cmt">// start reading the file
        {
         class="type">class="kw">string str_follow = FileReadString(handle);                        class=class="str">"cmt">// reading

         class=class="str">"cmt">// here we implement filling the container depending on its type
        }
    }
  class="kw">struct InputsData                                                        class=class="str">"cmt">// take the structure, according to which the binary file was created 
    {
      class="type">int                  symbol_id;     class=class="str">"cmt">// id of a balanced symbol
      class="type">ENUM_POSITION_TYPE   type;         class=class="str">"cmt">// position type
    };
  InputsData inputsData[];                                                class=class="str">"cmt">// storage of inputs
  class="type">class="kw">string  filename="Inputs.bin";                                         class=class="str">"cmt">// file name
  ArrayFree(inputsData);                                                  class=class="str">"cmt">// array released
  class="type">long count=FileLoad(filename,inputsData,FILE_COMMON);                  class=class="str">"cmt">// load file
  class="type">class="kw">string filename="Inputs.sqlite"; class=class="str">"cmt">// file name with inputs prepared in advance
  class="type">int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE |
                            DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON); class=class="str">"cmt">// open the database

常见问题

先用各自近20日平均真实波幅(ATR)折算成统一风险单位,再按账户总风险上限反推每品种手数,避免高波动品种吃掉全部额度。
把每个品种当前分时波动除以其日均波动率,比值大于1.5的品种当天风险倾向偏高,应优先减仓或跳过。
小布可接入多品种行情,自动按波动率给你算出各品种风险权重和预警线,你只需看提示决定是否降仓。
品种数固定且少(如4个以内)用数组更省内存;若品种常变动或超10个,动态容器更灵活但需注意内存占用。
把品种名写进逗号分隔的字符串由程序拆开,或用外部配置文件读取,六种路径里这两种最易维护且改起来快。