单一资产交易顺序中的风险评估·综合运用
📉

单一资产交易顺序中的风险评估·综合运用

(3/3)· 从凯利衍生的最优 f 到一般情形附录,把账户回撤与未知 f 两座大山一次拆穿

含代码示例 第 3/3 篇
很多交易者直接把历史回测里算出的 f 当未来仓位系数,账户动辄腰斩才反应过来。另有不少人忽略止损份额 r 与收益 a 的代数约束,把买卖双向模型混成一锅粥。本文收尾篇只做一件事:把不确定性塞回公式里。

下降因子与随机抽样的底层实现

这段逻辑干两件事:先算一串价格的‘下降因子’ d,再对数组做原地随机抽样。下降因子衡量的是在窗口 N 内,每一个新低点相对此前最高点的回撤极小值,外汇与贵金属波动无序,该值仅反映历史形态,不预示方向,杠杆品种高风险。 核心循环从 i=1 扫到 N-1:若某根收盘价 c[i]≤0 直接返回 0(防脏数据);c[i] 比当前 mn 还低时,更新 mn 并用 MathMin 累积 d=mn/mx 的最小比值;否则一旦 c[i] 创新高,就把 mx 和 mn 同时重置为 c[i],相当于一段下跌结构结束、重新起算。 [CODE] double d=c[0], mx=c[0], mn=c[0]; for(int i=1; i<N; ++i) { if(c[i]<=0) return 0; if(c[i]<mn) {mn=c[i]; d=MathMin(d,mn/mx);} else {if(c[i]>mx) mx=mn=c[i];} } return d; } // 随机排列的第一个不大于 min(n,ArraySize(b)) // b[] 数组的元素 // 可以使用函数库 MathSample() 代替 trps() void trps(double &b[],int n) { if(n<=1) return; int sz=ArraySize(b); if(sz<=1) return; if(sz<n) n=sz; int ner; double dnc=MathRandomUniform(0,n,ner); if(!MathIsValidNumber(dnc)) {Print("错误 ",ner); ExpertRemove();} int nc=(int)dnc; if(nc>=0 && nc<n-1) { double tmp=b[n-1]; b[n-1]=b[nc]; b[nc]=tmp;} trps(b,n-1); } [/CODE] trps 是递归版 Fisher–Yates 抽样:每次从 [0,n-1] 用 MathRandomUniform 取一个索引 nc,和末位交换,再把规模缩到 n-1 递归。当数组长度小于 n 时自动把 n 截断为 sz,避免越界。 MathRandomUniform 返回非有限值时会 Print 错误码并 ExpertRemove 终止 EA,实盘前应在 MT5 策略测试器里跑一遍确认 ner 恒为 0,否则抽样逻辑会直接崩掉。

MQL5 / C++
class="type">class="kw">double d=c[class="num">0], mx=c[class="num">0], mn=c[class="num">0];
for(class="type">int i=class="num">1; i<N; ++i)
  {
  if(c[i]<=class="num">0) class="kw">return class="num">0;
  if(c[i]<mn) {mn=c[i]; d=MathMin(d,mn/mx);}
  else {if(c[i]>mx) mx=mn=c[i];}
  }
class="kw">return d;
}
class=class="str">"cmt">// 随机排列的第一个不大于 min(n,ArraySize(b))
class=class="str">"cmt">// b[] 数组的元素
class=class="str">"cmt">// 可以使用函数库 MathSample() 代替 trps()
class="type">void trps(class="type">class="kw">double &b[],class="type">int n)
  {
  if(n<=class="num">1) class="kw">return;
  class="type">int sz=ArraySize(b);
  if(sz<=class="num">1) class="kw">return;
  if(sz<n) n=sz;
  class="type">int ner;
  class="type">class="kw">double dnc=MathRandomUniform(class="num">0,n,ner);
  if(!MathIsValidNumber(dnc)) {Print("错误 ",ner); ExpertRemove();}
  class="type">int nc=(class="type">int)dnc;
  if(nc>=class="num">0 && nc<n-class="num">1)
    { class="type">class="kw">double tmp=b[n-class="num">1]; b[n-class="num">1]=b[nc]; b[nc]=tmp;}
  trps(b,n-class="num">1);
  }

◍ 样本跑出来为何判系统不可用

把一段 30 笔交易的收益序列丢进 r_cmn.mq5 脚本,得到的样本分布参数为:平均 0.322655、分散 1.419552、非对称 0.990362、中位数 0.023030。dlt 设 0.05、G0 设 0.25,正态近似下 dlt 四分位是 -0.0351,G0 显著等级 0.369;自举 1 万次的结果几乎一致(四分位 -0.0136,等级 0.3727)。 按准则算出的 r_opt(δ)=0,意味着这套系统在当前数据下被直接禁用——要么样本不够,要么得拉更长的交易历史。即便前面附录的图看着漂亮,统计口径不买账。 根子在用的标准太“通用”。它覆盖了我们研究过的所有系统,而那些系统多是左截尾分布、理论期望可能无限,平均收益向对称正态收敛极慢。本例系统的偏度 0.99、中位数远小于均值,正是“砍亏损、让利润跑”的典型右偏。 想落地得更准,得切到参数化方法:利用具体系统和资产价格序列的信息做推测,但不扭曲真实分布,也就是处在“一般情况”里说的第二种变体。计算更重,但特定品种上更贴。 亏损不单来自趋势反转这类意外,更多时候是噪音和波动把系统拖垮。若系统分不清这两类原因、压不住它们的干扰,外汇和贵金属这种高杠杆品种上就别用。 下面这段是上述脚本的核心片段,可直接在 MT5 里建 r_cmn.mq5 跑通: #include <Math\Stat\Normal.mqh> // 引入正态分布统计库 #include <Math\Stat\Uniform.mqh> // 引入均匀分布库(自举备用) #include <Graphics\Graphic.mqh> // 图形库,绘图用 #define N 30 // 交易系列长度:只取30笔 #define NB 10000 // 为自举生成的样本数:1万次重采样 double G0=0.25; // 最低平均收益阈值 double dlt=0.05; // 显著等级 alpha void OnStart() // 脚本入口 { double a[N]= // 30笔交易收益数组 { -0.7615,0.2139,0.0003,0.04576,-0.9081,0.2969, // 前6笔

  • 6360,-0.3689,-0.6934,0.8549,1.8484,-0.9745,

-0.0325,-0.5037,-1.0163,3.4825,-0.1873,0.4850,

  • 9643,0.3734,0.8480,2.6887,-0.8462,0.5375,

-0.9141,0.9065,1.9506,-0.2472,-0.9218,-0.0775 }; double A=MathMean(a); // 算均值 double S2=MathVariance(a); // 算方差 double Sk=MathSkewness(a); // 算偏度 double Md=MathMedian(a); // 算中位数 Print("样本分布参数:"); // 输出标题 PrintFormat("平均: %f, 分散: %f, 非对称: %f, 中位数: %f",A,S2,Sk,Md); PrintFormat("交易数量: %d, dlt 值: %f, G0 值: %f",N,dlt,G0); // 通过正态分布近似 Print("通过正态分布近似:"); double q0, p0; // 四分位与显著等级变量 int ner; // 错误码 q0=MathQuantileNormal(dlt,A,MathSqrt(S2/N),ner); // 正态下dlt分位 if(!MathIsValidNumber(q0)) {Print("错误 ",ner); return;} // 校验失败退出 Print("dlt 四分位: ",q0); p0=MathCumulativeDistributionNormal(G0,A,MathSqrt(S2/N),ner); // G0累积概率

MQL5 / C++
class="macro">#include <Math\Stat\Normal.mqh>
class="macro">#include <Math\Stat\Uniform.mqh>
class="macro">#include <Graphics\Graphic.mqh>
class="macro">#define N class="num">30        class=class="str">"cmt">// 交易系列长度
class="macro">#define NB class="num">10000    class=class="str">"cmt">// 为自举生成的样本数
class="type">class="kw">double G0=class="num">0.25;     class=class="str">"cmt">// 最低平均收益
class="type">class="kw">double dlt=class="num">0.05;    class=class="str">"cmt">// 显著等级
class="type">void OnStart()
  {
   class="type">class="kw">double a[N]=
     {
      -class="num">0.7615,class="num">0.2139,class="num">0.0003,class="num">0.04576,-class="num">0.9081,class="num">0.2969,   class=class="str">"cmt">// 交易收益
      class="num">2.6360,-class="num">0.3689,-class="num">0.6934,class="num">0.8549,class="num">1.8484,-class="num">0.9745,
      -class="num">0.0325,-class="num">0.5037,-class="num">1.0163,class="num">3.4825,-class="num">0.1873,class="num">0.4850,
      class="num">0.9643,class="num">0.3734,class="num">0.8480,class="num">2.6887,-class="num">0.8462,class="num">0.5375,
      -class="num">0.9141,class="num">0.9065,class="num">1.9506,-class="num">0.2472,-class="num">0.9218,-class="num">0.0775
     };
   class="type">class="kw">double A=MathMean(a);
   class="type">class="kw">double S2=MathVariance(a);
   class="type">class="kw">double Sk=MathSkewness(a);
   class="type">class="kw">double Md=MathMedian(a);
   Print("样本分布参数:");
   PrintFormat("平均: %f, 分散: %f, 非对称: %f, 中位数: %f",A,S2,Sk,Md);
   PrintFormat("交易数量: %d, dlt 值: %f, G0 值: %f",N,dlt,G0);
class=class="str">"cmt">// 通过正态分布近似
   Print("通过正态分布近似:");
   class="type">class="kw">double q0, p0;
   class="type">int ner;
   q0=MathQuantileNormal(dlt,A,MathSqrt(S2/N),ner);
   if(!MathIsValidNumber(q0)) {Print("错误 ",ner); class="kw">return;}
   Print("dlt 四分位: ",q0);
   p0=MathCumulativeDistributionNormal(G0,A,MathSqrt(S2/N),ner);

「自举与正态近似算出最小样本量」

这段脚本把前面算出的 G0 显著等级先做了合法性校验,不合法直接退出,避免后续统计全建立在脏数据上。 自举部分用 GetTickCount 做随机种子,从原样本 a 里重采样 NB 次,每次算均值塞进 s 数组;统计小于 G0 的次数占比,得到经验显著等级 p0,同时用 MathQuantile 取 dlt 对应的分位值 q[0]。 正态近似那段从 nmin=1 循环到 999,用 MathQuantileNormal 以 S2/nmin 为方差近似,一旦 q0 超过 G0 就 break,输出「最小交易数 nmin」。在 MT5 里跑这段代码,你能直接看到自己策略需要多少笔成交,才让观测优势脱离随机噪声——外汇和贵金属杠杆高,样本不够时任何「优势」都可能是运气,务必用这套自检。 sample 函数是自举的底层,用 MathRandomUniform 在 [0,N) 均匀抽索引,nc==N 时强制拉回 N-1 防越界;抽到的原值写进 b 完成一次重采样。把 N、NB、dlt、G0、S2 接进你自己的成交序列,就能复算专属的 nmin。

MQL5 / C++
  if(!MathIsValidNumber(p0)) {Print("MathIsValidNumber(p0) 错误 ",ner); class="kw">return;}
  Print("G0 的显著等级: ",p0);
class=class="str">"cmt">// 自举
  MathSrand(GetTickCount());
  class="type">class="kw">double b[N],s[NB];
  p0=class="num">0;
  for(class="type">int i=class="num">0;i<NB;++i)
    {
      sample(a,b);
      s[i]=MathMean(b);
      if(s[i]<G0) ++p0;
    }
  p0/=NB;
  class="type">class="kw">double p[class="num">1],q[class="num">1];
  p[class="num">0]=dlt;
  if(!MathQuantile(s,p,q)) {Print("MathQuantile() 错误"); class="kw">return;}
  Print("自举近似");
  Print("dlt 四分位: ",q[class="num">0]);
  Print("G0 的显著等级: ",p0);
class=class="str">"cmt">// 近似值 nmin(通过正态分布近似)
  class="type">int nmin;
  for(nmin=class="num">1; nmin<class="num">1000;++nmin)
    {
      q0=MathQuantileNormal(dlt,A,MathSqrt(S2/nmin),ner);
      if(!MathIsValidNumber(q0)) {Print("错误 ",ner); class="kw">return;}
      if(q0>G0) class="kw">break;
    }
  Print("最小交易数 nmin(通过正态分布近似)):");
  PrintFormat("不小于 %d, dlt 四分位的值: %f",nmin,q0);
  }

class="type">void sample(class="type">class="kw">double &a[],class="type">class="kw">double &b[])
  {
  class="type">int ner;
  class="type">class="kw">double dnc;
  for(class="type">int i=class="num">0; i<N;++i)
    {
      dnc=MathRandomUniform(class="num">0,N,ner);
      if(!MathIsValidNumber(dnc)) {Print("MathIsValidNumber(dnc) 错误 ",ner); ExpertRemove();}
      class="type">int nc=(class="type">int)dnc;
      if(nc==N) nc=N-class="num">1;
      b[i]=a[nc];
    }
  }
把回撤诊断交给小布盯盘
这些基于收益 a 与风险 r 的分布诊断,小布盯盘的 AIGC 已内置,打开对应品种页即可看到实时仓位暴露。把重复劳动交给小布,你专注决策。

常见问题

f 依赖已知成交历史,未来序列可能偏离,导致回撤放大。概率上更倾向用折减 f 或蒙特卡洛重采样评估。
小布盯盘会按本文的 r、a 定义逐笔标注品种页的仓位暴露,并给出回撤概率带,省去手算。
它放宽了固定最小交易量增量假设,讨论非单位资本表达与离散手数下的边界条件,倾向用于贵金属多合约情形。
点差与隔夜息会扭曲 p_exit 到 a 的映射,属高风险市场,结论仅作概率参考而非定向建议。
本篇引入顺序相关性,完整讨论见《单一资产交易顺序中的风险评估·基础篇》与《单一资产交易顺序中的风险评估·实战篇》。