您的位置 首页 外汇EA指标

外汇EA编写教程:概率论在缺口交易中的应用

内容 总结 智能交易系统一般说明 交易策略是一种对抗随机漫游假设的尝试。 缺口交易策略 测试策略并计算每个交易的最佳风险 结束语 其他文件 总结 本文延续了作者上一篇文章的主题:概…

内容

  • 总结
  • 智能交易系统一般说明
  • 交易策略是一种对抗随机漫游假设的尝试。
  • 缺口交易策略
  • 测试策略并计算每个交易的最佳风险
  • 结束语
  • 其他文件

总结

本文延续了作者上一篇文章的主题:概率论和数理统计在交易中的应用。我们将探索创建和测试交易策略的方法。

首先,我们将探讨这种交易的可能性,即检测与随机漫游假设的偏差。事实证明,如果价格显示零漂移随机漫游(没有方向趋势),盈利交易是不可能的。这为找到一种假设的抵消方法提供了基础。如果我们找到一种方法来抵消这一假设,我们可以尝试用它来制定交易策略。

我们还将继续研究我们在前面的文章中开始讨论的风险主题。此外,我们将它们用作第一和第二参考文献。

由于我们的方法是基于概率论,所以建议理解它的基础,但这不是强制性的。了解概率方法的本质是很重要的,因为它们被更系统、更经常地使用,结果就越明显(由于大数定律)。当然,它们的应用应该得到充分的展示。

智能交易系统一般说明

EA的发展大致可分为三个阶段:

  1. 创造创意。
  2. 使用各种简化的验证思想。
  3. 使想法与市场现实相适应。

本文将重点放在第二阶段,以便我们能更彻底地适应这个主题。此外,本阶段的讨论在论坛中比其他阶段更为频繁。

我们描述了已经实现的简化。我们限制EA只交易一项资产。我们假设资产价格以账户货币表示。我们将排除非交易业务(如账户利息和资金使用权),不考虑不同类型的订单(仅按市场价格交易)。执行订单时,我们将忽略滑动点,并将点差视为固定值。我们还假设EA控制我们账户中的所有资金,并且没有涉及其他EA。

通过所有这些简化,EA操作的结果无疑被定义为V(t)函数与时间相关的股票持有。正值V(t)对应于买入,而负值对应于卖出。此外,P(t)函数和C 0(初始资本)。下图显示了一个可能的V=V(t)位置图。

持仓样本

买入价和卖出价之间的算术平均数。

v(t)和p(t)是分段常数(逐步)函数,因为它们的值是某些最小增量步骤的倍数。如果需要更严格的数学定义,可以将其视为从右到左的连续定义,并限制在左间隙。我们假设V(T)缺口点永远不会与P(T)匹配。换言之,在任何时候,价格或头寸最多改变两个值中的一个,或者两者都保持不变。值得注意的是,价格或数量可能发生变化的时间点也是最小步骤的倍数。

根据这些数据,我们可以发现c(t)函数是资金价值随时间变化的函数。定义为在T-时间结算时,由EA控制的账户部分的余额。因为我们的账户上只有一个EA,所以这个值与metatrader 5中定义的净账户值一致。

定义t时c(t)的变化。如果此时没有音量和价格的变化,那么它自然是零。如果价格变动,基金的增长等于交易量乘以价格增量的乘积。如果交易量发生变化,当绝对位置减少时,可能有两种选择,资本不变,当它增加时,资本减少量等于价差的绝对值和交易量的变化的乘积。换句话说,如果一个位置被部分关闭,净值将不会改变,而增加位置将导致净值略有下降。因此,C(t)在t时刻的资本价值等于C 0=C(0)之和,所有的变化都是从零到T。

在开发我们的风险理论时(在前两篇文章中),我们使用了“交易”的概念。这一概念与元交易员5中的所谓“交易”并不完全一致,但更符合那里的所谓“交易”。准确地说,它对应于我们所说的简单位置。根据我们的定义,一个简单的位置是由打开和打开时间决定的。它的体积和方向在这些时刻之间保持不变。下面是一个简单仓库的V=V(t)图表示例。

简单仓位 (交易)

任何位置(因为它总是一个分段常数)都可以想象为简单位置的总和。这种表示可以用多种方式来实现。下图显示了以两种不同方式表示的单个位置的总和,即简单位置。初始头寸以蓝色显示,而所有交易分别以绿色和红色显示。当我们采用元交易者5中的交易概念时,我们有另一个选择。这些方法都是非常合理的。

将仓位切分为成交的两种方法

在某些EAS中,这不是很有意义。例如,可能有一些EAS的位置先增加后减少。同时,也有一些EAS是以这种方式自然表达的。例如,一个位置可以由一系列不与时间相交的简单位置组成。下图包含此类位置的示例。

不适与相适的仓位将作为交易总合呈现

每笔交易(简单头寸)后资本的相对变化c1/c0表示为盈利能力a和风险r两个值:c1/c0=1+r a。盈利能力等于交易期间价格上涨与入门价和止损差的比率,而风险与交易量成正比,且意味着当止损激活时将损失的资本份额。

因此,我们不再研究V(T)、P(T)和C(T)的时间函数,而是研究代表事务顺序的数字序列。这大大简化了高级研究。特别是在处理不确定性概率模型时,我们可以避免将自限随机过程理论应用于有限随机变量集。

对于资产价格行为和交易结果,概率论是一种广泛接受的不确定性数学建模方法。根据这种方法,我们应该把V(t)、P(t)和C(t)函数的实现看作是一些随机过程(轨迹)。一般来说,这个任务实际上是无法解决的。主要原因是缺乏一个能准确描述价格行为的符合概率模型。因此,考虑可能解决方案的特殊情况是有意义的。如上所述,在本文中,我们将考虑EA形成的头寸可以适当地表示为一系列简单的头寸(交易)。

值得一提的是,另一个与EA相关的问题-参数。详细的研究认为它们对于EA开发过程的一些形式化(标准化)是有用的。我们将参数分为三类:

  1. 历史参数。在EA操作过程中,参数可能因事务而异。这些是指标、白天时刻、新闻数据和每月等效值。一般来说,它们是时间函数,就像价格或头寸一样。在简化应用程序的情况下,我们可以将它们视为事务到达时的已知数字序列。每个特定事务的参数(方向、卷、停止和停止)是根据历史参数的值定义的。
  2. 实际参数。我们简单地称它们为瞬态参数。它们是在EA开始交易时设置的,并且只能在测试和优化EA时应用。
  3. 元参数设置EA优化算法,如自定义优化条件参数。假设我们希望通过两个条件来优化EA,尽管只有一个条件一个条件。我们将两个原始条件形成一个新条件,并将它们的一些权重放在一起。这些权重作为元参数。

例如,在下面描述的基于凹口的EA中,最小凹口是EA参数,每个特定凹口的大小是历史参数。在这种情况下,元参数可以包括优化条件数(我们假设条件按一定顺序编号,例如,1)根据利润优化,和(2)根据退出优化等)。

在本文中,我们将使用与历史参数相关的重要简化。当我们讨论交易的收益分配时,它通常取决于这些参数。我们假设这种依赖是无关紧要的。主要原因是这种依赖通常会使模型复杂化,最终可能导致过度拟合。

交易策略是一种对抗随机漫游假设的尝试。

我们提到缺乏准确的模型来描述价格行为。然而,近似模型也可能有用。例如,有一个著名的价格行为模型,它将价格视为具有零漂移(无方向性趋势)的随机漫游。这个模型被称为随机行走假设。根据这一假设,如果我们考虑点利差,则任何EA的平均利润为零或是一个小的损失。

很难证明在随机漫游中不可能赚钱,因为它需要涉及随机过程理论(ITO演算、停止时间等)的复杂数学装置。一般来说,当随机漫游交易没有趋势时,可以归结为资本是martingale(概率论,不要与赌博系统的martingale混淆)。鞅是一个随机过程,其均值(数学期望值)不随时间变化。在我们的例子中,这意味着资本价值的数学期望在任何时候都等于其初始值。

因此,当我们开始研究交易理念时,我们应该在随机漫游统计中寻找明显的价格偏差。为此,我们将使用概率论和数理统计中的思路,但首先,我们做一些观察:

  • 任何这样的解本质上都是概率论的,我们的结论总是有一些错误的非零概率。
  • 如果我们的方法没有检测到偏差,那并不意味着它们根本不存在。也许其他的方法可以检测到它们。
  • 统计显著性偏差不能保证统计显著正收益偏差的存在是必要的,但不是充分的条件。

让我们构造一种搜索随机游动偏差的方法。为此,我们考虑了一些随机变量,并根据实际价格形成的样本建立经验概率分布。此外,假设价格行为是随机漫游的,我们将构造相同价值的理论概率分布。比较这些分布,我们将决定抵消(或不可能抵消)随机漫游场景。

我们来构造一个合适数值的例子。假设在t0开始时,价格等于p0。我们取其他不等于p0的p1价格值。等到价格达到p1(t1)=p1的t1。我们搜索价格P2在T0和T1,这是最远的价格P1。我们引入数值K=(P2-P0)/(P0—P1)。P1<P0<P2或P2<P0& lt;P1条件总是有效的,所以K<0在任何时间。下面提供了一个图表来解释这个想法。蓝线代表p0的价格,它穿过价格图的时间是t0。红线代表p1的价格,它是t0,当图表被触摸的时刻是t1。绿线代表p2的价格,并且尽可能远离p1。

p0, p1 和 p2 价位

数值背后的想法很简单。假设我们在t0交易。以p0和p1、p1>p0价格出售,止损。p2是停止盈利的最低可实现价格,而k是交易中可实现的最高利润。实际上,我们在执行事务时不知道确切的k值。在这种不确定概率模型的框架内,我们只能讨论知识的概率分布。假设我们知道fk(x)的概率分布函数,定义为k<x的概率。假设我们使用pk的价格作为停止点:pk-p0=k(p0-p1)。在这种情况下,fk(k)等于在停止前触摸停止的概率。相应地,1-fk(k)等于先激活停止利润的概率。现在让差值为零。那么,在止损激活的情况下,盈利能力等于-1,而在止损激活的情况下,盈利能力等于K。此类交易的数学期望值:M=(-1)*FK(K)+K*(1-FK(K)=K-(K+1)*FK(K),如果FK(K)=K/(K+1),盈利能力等于零。

如果我们知道方程fk(x),我们甚至可以对ea进行初步优化。例如,我们可以找到最佳的停/停比率来最大化交易盈利能力的数学期望。然后我们可以找到交易中风险的最优值。因此,EA甚至可以在准备就绪之前进行优化。这节省了时间,并允许您在早期放弃明显不适当的想法。

如果假设价格表现为无趋势的随机游动,则k值的分布由fk(x)=fk0(x)分布函数设置,其中,如果x<0,fk0(x)=0;如果x&gt;0,fk0(x)=x/(x+1)。更确切地说,我们可以假设这里使用的随机遍历是零漂移维纳过程(没有趋势)。如我们所见,如果满足随机漫游假设,且点差等于零,则在任何停止/停止比率下,盈利能力的数学期望值等于零。在非零差的情况下,它是负的。

而不是k,我们可以考虑值q= k/(k+1)=(p2-p0)/(p2-p1),k= q/(1-q)。这个价值可以表示为停止盈利与停止盈利的比率。它更方便,因为它从(0;1)区间取值,并且在随机游走的情况下具有比K更简单的分布(甚至在该区间内)。

接下来,我们将关注Q值。我们来研究如何构造和应用它的经验分布函数fq(x)。假设我们有一个交易的想法,让我们检查一下价格历史。我们有一组n个入口点。入门价p0,i和止损p1,i,其中i=1,…,n是为每一个定义的。现在我们应该确定这个想法是否有一定的盈利潜力。对于每一笔交易,我们应该搜索价格p2,i,尽可能远离止损直到激活时间。基于此价格,我们得到n个样本qi=(p2,i-p0,i)/(p2,i-p1,i),i=1,…,n。由样本构造的经验分布函数由fq(x)=m(x)/n方程定义,其中m(x)等于小于x的qi样本元素的数量。如果价格行为表现为像是无趋势的随机漫游:如果x<0,则fq0(x)=0;如果x<0,则fq0(x)=x;如果x<0,则fq0(x)=x;如果0&lt;x<1,则fq0(x)=x;如果fq0(x)=1,则fq0(x)=x。

如果FQ(x)与随机巡检的理论分布函数FQ0(x)之间存在显著差异,我们需要检查这种差异在盈利能力中的重要性。如果即使考虑到点的差异,盈利能力也足够积极,那么是时候选择适当的停止/停止比率了。这可以通过最大化利润预期来实现。在此之后,我们可以对每一笔交易的风险价值选择一个最佳值,然后对这一想法进行初步的检验。如果结果为正数,则继续创建实际事务EA是有意义的。接下来,我们将尝试在实践中演示该算法。

这个问题已经升级了——如何使用随机演练进行类似的比较,以获得更复杂的场外算法。通常,答案与之前的研究相同。主要障碍是随机漫游的利润分配只能在极少数情况下以分析的形式得到。然而,蒙特卡罗模拟总是可以用来获得经验近似值。

缺口交易策略

在分析这个想法之前,我应该注意到我们的主要任务是提供分析方法,而不是盈利的交易策略。过分关注利润会使我们陷入琐碎的细节中,并分散我们对整体情况的注意力。

资产价格是分散的,因此变化总是突然的和跳跃式的。这些突然的跳跃可能大小不同。当它们很大时,就称为间隙。没有明确的界限来区分这个差距和通常的价格变化。我们可以自由设定我们认为合适的界限。

这个差距非常适合展示上一节中概述的理论。每个都由两个时间点和资产价格设置。使用前面介绍的价格表示法,我们假设p0是后面的价格,而p1是更早的价格。一旦出现缺口,我们将进入交易。价格p1不仅可以视为止损,还可以视为止损。这意味着我们可以选择两种系统类型中的一种,希望快速填补缺口,或者将价格趋势拉向缺口方向。填补缺口意味着价格返回到p1,或者突破它。

由于外汇资产的标准形式存在着相对较少的差距,论坛管理层建议笔者在讨论本文主题时概括这一概念。定义差距时,可以放弃需求声明,它只考虑两个连续价格之间的差异。显然,这种情况下可能出现的差距将变得非常大,因此从交易的角度来看,在合理的选择中限制自己是值得的。例如,作者建议检查一个美国会议的收盘价与下一个开盘价之间的差额。

让我解释一下时间概念是如何被形式化的。它由三个时间间隔设置:持续时间、长度和前向。周期是周期性的,长度不超过时间限制。逐案报价属于所有时段,或不属于任何时段(如果时段长度严格小于时段长度,则后者是可能的)。向前移动是从零时间点到其后第一个周期开始之间的时间间隔。它应该比最后期限还短。这个周期的概念比通常的略宽,允许我们观察,例如,分钟列之间的间隙。我们将在下面的草图中描述它。绿色箭头表示时间长度,红色箭头表示时间长度,蓝色箭头表示时间长度。

时段

我们将使用两个稍有不同的EAS来收集与差距相关的统计数据。第一个(Gaps_Reg_Stat.mq5)考虑两个顺序引用之间的差距,第二个(Gaps_Ses_Stat.mq5)考虑期间之间的差距。当然,这些EAS不会在测试模式下进行交易和运行。建议第一次运行基于一个真正的逐报价,第二次运行基于一分钟一分钟的OHLC。EA代码如下。

// gaps_reg_stat.mq5
#define ND 100

input double gmin=0.1;                 // 最小缺口大小: USDJPY - 0.1, EURUSD - 0.001
input string fname="gaps//stat.txt";   // 统计文件名

struct SGap
  { double p0;
    double p1;
    double p2;
    double p3;
    double s;
    void set(double p_1,double p,double sprd);
    bool brkn();
    void change(double p);
    double gap();
    double Q();
  };

class CGaps
  { SGap gs[];
    int ngs;
    int go[];
    int ngo;
    public:
    void init();
    void add(double p_1,double p,double sprd);
    void change(double p);
    void gs2f(string fn);
  };

CGaps gaps;
MqlTick tick0;
bool is0=false;

void OnTick()
  { MqlTick tick;
    if (!SymbolInfoTick(_Symbol, tick)) return;
    if(is0)
      { double p=(tick.bid+tick.ask)*0.5, p0=(tick0.bid+tick0.ask)*0.5;
        gaps.change(p);
        if(MathAbs(p-p0)>=gmin) gaps.add(p0,p,tick.ask-tick.bid);
      }
    else is0=true;
    tick0=tick;
  }

int OnInit()
  { gaps.init();
     return(INIT_SUCCEEDED);
  }
  
void OnDeinit(const int reason)
  { gaps.gs2f(fname);
  }

void SGap :: set(double p_1,double p,double sprd)
  { p1=p_1; p0=p2=p3=p; s=sprd;
  }

bool SGap :: brkn()
  { return ((p0>p1)&&(p3<=p1))||((p0<p1)&&(p3>=p1));
  }

void SGap :: change(double p)
  { if(brkn()) return;
    if((p0>p1&&p>p2) || (p0<p1&&p<p2)) p2=p;
    p3=p;
  }

double SGap :: gap()
  { return MathAbs(p0-p1);
  }

double SGap :: Q()
  { double q=p2-p1;
    if(q==0.0) return 0.0;
    return (p2-p0)/q;
  }

void CGaps :: init()
  { ngs=ngo=0;
  }

void CGaps :: add(double p_1,double p,double sprd)
  { ++ngs;
    if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND);
    gs[ngs-1].set(p_1,p,sprd);
    int i=0;
    for(; i<ngo; ++i) if(go[i]<0) break;
    if(i==ngo)
      {
        ++ngo;
        if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND);
      }
    go[i]=ngs-1;
  }

void CGaps :: change(double p)
  { for(int i=0; i<ngo; ++i)
      { if(go[i]<0) continue;
        gs[go[i]].change(p);
        if(gs[go[i]].brkn()) go[i]=-1;
      }
  }

void CGaps :: gs2f(string fn)
  { int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT), c;
    for(int i=0;i<ngs;++i)
      { if (gs[i].brkn()) c=1; else c=0;
        FileWriteString(f,(string)gs[i].gap()+" "+(string)gs[i].Q()+" "+(string)c+" "+(string)gs[i].s);
        if(i==ngs-1) break;
        FileWriteString(f,"/n");
      }
    FileClose(f);
  }
// gaps_ses_stat.mq5
#define ND 100

input double gmin=0.001;               // 最小缺口大小: USDJPY - 0.1, EURUSD - 0.001
input uint   mperiod=1;                // 时段期限(分钟)
input uint   mlength=1;                // 时段长度(分钟)
input uint   mbias=0;                  // 第一个时段乖离(分钟)
input string fname="gaps//stat.txt";   // 统计文件名

struct SGap
  { double p0;
    double p1;
    double p2;
    double p3;
    double s;
    void set(double p_1,double p,double sprd);
    bool brkn();
    void change(double p);
    double gap();
    double Q();
  };

class CGaps
  { SGap gs[];
    int ngs;
    int go[];
    int ngo;
    public:
    void init();
    void add(double p_1,double p,double sprd);
    bool change(double p);
    void gs2f(string fn);
  };

CGaps gaps;
MqlTick tick0;
int ns0=-1;
ulong sbias=mbias*60, speriod=mperiod*60, slength=mlength*60;

void OnTick()
  { MqlTick tick;
    if (!SymbolInfoTick(_Symbol, tick)) return;
    double p=(tick.bid+tick.ask)*0.5;
    gaps.change(p);
    int ns=nsession(tick.time);
    if(ns>=0)
      { double p0=(tick0.bid+tick0.ask)*0.5;
        if(ns0>=0&&ns>ns0&&MathAbs(p-p0)>=gmin) gaps.add(p0,p,tick.ask-tick.bid);
        ns0=ns;
        tick0=tick;
      }
  }

int OnInit()
  { if(speriod==0||slength==0||speriod<slength||speriod<=sbias)
      { Print("wrong session format");
        return(INIT_FAILED);
      }
    gaps.init();
    return(INIT_SUCCEEDED);
  }
  
void OnDeinit(const int reason)
  { gaps.gs2f(fname);
  }

int nsession(datetime t)
  { ulong t0=(ulong)t;
    if(t0<sbias) return -1;
    t0-=sbias;
    if(t0%speriod>slength) return -1;
    return (int)(t0/speriod);
  }

void SGap :: set(double p_1,double p,double sprd)
  { p1=p_1; p0=p2=p3=p; s=sprd;
  }

bool SGap :: brkn()
  { return ((p0>p1)&&(p3<=p1))||((p0<p1)&&(p3>=p1));
  }

void SGap :: change(double p)
  { if(brkn()) return;
    if((p0>p1&&p>p2) || (p0<p1&&p<p2)) p2=p;
    p3=p;
  }

double SGap :: gap()
  { return MathAbs(p0-p1);
  }

double SGap :: Q()
  { double q=p2-p1;
    if(q==0.0) return 0.0;
    return (p2-p0)/q;
  }

void CGaps :: init()
  { ngs=ngo=0;
  }

void CGaps :: add(double p_1,double p,double sprd)
  { ++ngs;
    if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND);
    gs[ngs-1].set(p_1,p,sprd);
    int i=0;
    for(; i<ngo; ++i) if(go[i]<0) break;
    if(i==ngo)
      {
        ++ngo;
        if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND);
      }
    go[i]=ngs-1;
  }

bool CGaps :: change(double p)
  { bool chngd=false;
    for(int i=0; i<ngo; ++i)
      { if(go[i]<0) continue;
        gs[go[i]].change(p);
        if(gs[go[i]].brkn()) {go[i]=-1; chngd=true;}
      }
    return chngd;
  }

void CGaps :: gs2f(string fn)
  { int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT), c;
    for(int i=0;i<ngs;++i)
      { if (gs[i].brkn()) c=1; else c=0;
        FileWriteString(f,(string)gs[i].gap()+" "+(string)gs[i].Q()+" "+(string)c+" "+(string)gs[i].s);
        if(i==ngs-1) break;
        FileWriteString(f,"/n");
      }
    FileClose(f);
  }

ea非常简单,尽管在cgaps类中提到go[]数组是有意义的,其中存储了未填充的索引以允许加速ea工作。

在任何情况下,记录每个间隙的以下数据:绝对间隙值、Q值、闭合数据和间隙值。然后,检查Q经验分布值与均匀分布值的差异,做出进一步的分析决策。使用图形和计算(Kolmogorov统计计算)方法检查差异。为了简单起见,我们将自己的计算结果限制为Kolmogorov-Smirnov检验的p值。它取0到1之间的值,越小,样本分布与理论分布的一致性越小。

我们选择了Kolmogorov-Smirnov(单样本)测验进行数学研究。其主要原因是我们有兴趣区分均匀收敛测度中的分布函数,而不是任何积分测度。这个测试没有在mql5库中找到,所以我不得不使用r。值得注意的是,如果样本中有匹配的数字,那么条件的准确性将会降低(r给出相应的警告),但它仍然是可以接受的。

如果我们发现理论分布和经验分布之间存在显著差异,我们应该研究从中获利的可能性。如果没有明显的区别,那么我们要么放弃这个想法,要么尝试改进它。

如上所述,当形成缺口时,有两种方法可以以p0的价格向缺口或缺口背后的方向进入市场。让我们计算两种情况下的回报预期。在这样做时,我们认为点差是常数,并表示为s。绝对缺口值表示为g,而g0是最低值。

  • 沿间隙方向进入。在这种情况下,g+s是停止损失,而kg-s是停止损失。这里k是损益率。利润值:激活止损时为-1,激活止损时为(kg-s)/(g+s)。对应概率:fq(q)和1-fq(q)。用k表示,通过q:k=k(q)=q/(1-q)。然后是盈利能力的数学期望m,m=fq(q)*(-1)+(1-fq(q)*(k(q)g-s)/(g+s)。只有Q值,对于m,它对所有g(>g0)都是明显正的,这对我们来说是合适的。我们需要一个q值,当fq(q)明显低于随机穿行fq(q)的理论值时,当g=g0。
  • 反向缺口入口。在这种情况下,G-S停止盈利,而Kg+S停止亏损。这里k是损益率。盈利价值:对于止损激活-1,以及对于止损激活(GS)/(kg+s)。相应概率:1-fq(q)和fq(q)。表示为K通过Q与前面的段落相同:k= k(q)=q/(1-q)。对于盈利能力的数学期望M,我们得到了方程M=(1-Fq(q)*(-1)+*Fq(q)(G-S)/(k(q)g+s)仅Q值,对于m,它对于所有的g>>G0都是明显正的,这是适合我们的。我们需要q值,当f q(q)& nbsp时,它明显高于nq fq(q)& gt;fq0(q)的随机演练理论值。

收集了两个品种的统计数据:

  1. 欧元兑美元
  2. 美元兑日元

其中每一项都考虑到以下类型的差距:

  1. 在连续的报价之间。
  2. 分栏之间。
  3. 在交易期间之间。对于欧元兑美元,这是一个美国时期(芝加哥和纽约合并),对于美元兑日元,这是一个东京时期。

对于这六个选项中的每一个,将研究最新的统计数据:

  1. 200间隙
  2. 50间隙

所以我们有12个选择。每个结果如下:

  1. Kolmogorov统计的P值
  2. 缺口形成中的平均扩散平均点差
  3. q函数值(红线)分布的经验与理论
  4. M_-cont盈利能力数学期望图,交易方向上的差距,根据Q,与M_-cont=0理论线(红色)相比。这里,q是止损收益与止损的比率。
  5. 收益率数学期望图。根据差距,交易是反向的。根据Q,与M_rvrs=0理论线(红色)进行比较。此处,q为止损/(止盈+止损)的比率。

下面提供了所有结果选项。

  1. 欧元兑美元,连续报价之间的最后200个缺口。P值:3.471654E-07,平均价差:0.000695 EURUSD, 连续逐笔报价间最后 200 个缺口。
  2. 欧元兑美元,连续报价之间的最后50个缺口。P值:0.2428457,平均价差:0.0005724 EURUSD, 连续逐笔报价间最后 50 个缺口。
  3. 欧元兑美元,连续一分钟内两列之间的最后200个缺口。&P值:8.675995E-06,平均价差:0.0004352 EURUSD, 连续分钟柱线间最后 200 个缺口。
  4. 欧元兑美元,最后50个缺口之间的列连续分钟。P值:0.0125578,平均价差:0.000404 持仓样本0
  5. 欧元兑美元,过去200个时间缺口。P值:0.6659917,平均价差:0.0001323 持仓样本1
  6. 欧元兑美元,过去50个时间缺口。P值:0.08915716,平均价差:0.0001282 持仓样本2
  7. 美元/日元,连续报价之间的最后200个缺口。P值:2.267454E-06,平均价差:0.09563 持仓样本3
  8. 美元/日元,连续报价之间的最后50个缺口。P值:0.03259067,平均扩散:0.0597 持仓样本4
  9. 美元兑日元,连续一分钟内两列之间的最后200个缺口。P值:0.00037335,平均价差:0.05148 持仓样本5
  10. 美元日元,最后50个缺口之间的列连续分钟。P值:0.005747542,平均价差:0.0474 持仓样本6
  11. 美元兑日元,本期最后200个缺口。P值:0.07743524,平均价差:0.02023 持仓样本7
  12. 美元兑日元,本期最后50个缺口。P值:0.0091665,平均扩散:0.0185 持仓样本8

从这些结果,我们可以得出以下结论:

  • 随机走访的偏差非常显著。
  • 朝着缺口方向交易是没有希望的。以缩小差距为方向的交易似乎更可取,但利润很小(尤其是欧元兑美元)。
  • 当形成间隙时,点差可以比它的平均值增加几倍(这对于报价和分栏之间的差值来说是正确的)。
  • 在时间交易中,随机漫游的差距只发生在1-2个月的某个时间点,而在一年的时间内,偏差非常小。乍一看,这是由某个时点的主流趋势来判断的。显然,填充间隙在横截面积上更好,但在趋势区域中更差,虽然需要更详细的分析。nbsp;
  • 为了进一步分析,我们应该选择美元兑日元的最小列间距。

测试策略和计算每个交易的最佳风险

基于usdjpy分钟列之间的差距的系统版本看起来最有希望。在形成间隙的过程中检测到的点数量的显著增加意味着我们应该更加注意其定义。我们指定如下。我们考虑的缺点不是平均价格,而是买卖双方的价格。此外,我们将根据分录交易的类型选择其中一个。这意味着我们将根据投标价格定义上游缺口,而下游缺口是投标价格。间隙闭合也是如此。

我们开发了EA通过稍微修改我们用来收集时间间隙的统计数据。主要变化涉及间隙结构。由于间隙和事务之间有明确的对应关系,交换所需的所有信息(卷和关闭条件)将存储在该结构中。向事务添加了两个函数。一种是(pp2v())计算每笔交易的数量,另一种是nbsp;(trade())保留交易量和头寸之间的对应关系。下面提供了EA代码(gaps-ses-u test.mq5)。

// gaps_ses_test.mq5
#define ND 100

input uint   mperiod=1;                 // 时段期限(分钟)
input uint   mlength=1;                 // 时段长度(分钟)
input uint   mbias=0;                   // 第一个时段乖离(分钟)
input double g0=0.1;                    // 最小缺口大小: USDJPY - 0.1, EURUSD - 0.001
input double q0=0.4;                    // q0=sl/(sl+tp)
input double r=0.01;                    // 交易风险
input double s=0.02;                    // 近似点差
input string fname="gaps//stat.txt";    // 统计文件名

struct SGap
  { double p0;
    double p1;
    double p2;
    double v;
    int brkn();
    bool up();
    void change(double p);
    double gap();
    double Q();
    double a();
  };

class CGaps
  { SGap gs[];
    int ngs;
    int go[];
    int ngo;
    public:
    void init();
    void add(double p_1,double p);
    bool change(double pbid,double pask);
    double v();
    void gs2f(string fn);
  };

CGaps gaps;
MqlTick tick0;
int ns0=-1;
ulong sbias=mbias*60, speriod=mperiod*60, slength=mlength*60;
double dv=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);

void OnTick()
  { MqlTick tick;
    if (!SymbolInfoTick(_Symbol, tick)) return;
    bool chngd=gaps.change(tick.bid,tick.ask);
    int ns=nsession(tick.time);
    if(ns>=0)
      { if(ns0>=0&&ns>ns0)
          { if(tick0.ask-tick.ask>=g0) {gaps.add(tick0.ask,tick.ask); chngd=true;}
              else if(tick.bid-tick0.bid>=g0) {gaps.add(tick0.bid,tick.bid); chngd=true;}
          }
        ns0=ns;
        tick0=tick;
      }
    
    if(chngd) trade(gaps.v());
  }

int OnInit()
  {
     gaps.init();
     return(INIT_SUCCEEDED);
  }
  
void OnDeinit(const int reason)
  { gaps.gs2f(fname);
  }

int nsession(datetime t)
  { ulong t0=(ulong)t;
    if(t0<sbias) return -1;
    t0-=sbias;
    if(t0%speriod>slength) return -1;
    return (int)(t0/speriod);
  }

double pp2v(double psl, double pen)
  { if(psl==pen) return 0.0;
    double dc, dir=1.0;
    double c0=AccountInfoDouble(ACCOUNT_EQUITY);
    bool ner=true;
    if (psl<pen) ner=OrderCalcProfit(ORDER_TYPE_BUY,_Symbol,dv,pen+s,psl,dc);
      else {ner=OrderCalcProfit(ORDER_TYPE_SELL,_Symbol,dv,pen,psl+s,dc); dir=-1.0;}
    if(!ner) return 0.0;
    return -dir*r*dv*c0/dc;
  }

void trade(double vt)
  { double v0=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
    if(-v0<vt<v0) vt=v0*MathRound(vt/v0);
    double vr=0.0;
    if(PositionSelect(_Symbol))
      { vr=PositionGetDouble(POSITION_VOLUME);
        if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) vr=-vr;
      }
    int vi=(int)((vt-vr)/dv);
    if(vi==0) return;
    MqlTradeRequest request={0};
    MqlTradeResult  result={0};
    request.action=TRADE_ACTION_DEAL;
    request.symbol=_Symbol; 
    if(vi>0)
      { request.volume=vi*dv;
        request.type=ORDER_TYPE_BUY;
      }
      else
        { request.volume=-vi*dv;
          request.type=ORDER_TYPE_SELL;
        }
    if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError());
  }

int SGap :: brkn()
  { if(((p0>p1)&&(p2<=p1))||((p0<p1)&&(p2>=p1))) return 1;
    if(Q()>=q0) return -1;
    return 0;
  }

bool SGap :: up()
  { return p0>p1;
  }

void SGap :: change(double p)
  { if(brkn()==0) p2=p;
  }

double SGap :: gap()
  { return MathAbs(p0-p1);
  }

double SGap :: Q()
  { if(p2==p1) return 0.0;
    return (p2-p0)/(p2-p1);
  }

double SGap :: a()
  { double g=gap(), k0=q0/(1-q0);
    return (g-s)/(k0*g+s);
  }

void CGaps :: init()
  { ngs=ngo=0;
  }

void CGaps :: add(double p_1,double p)
  { ++ngs;
    if(ArraySize(gs)<ngs) ArrayResize(gs,ngs,ND);
    gs[ngs-1].p0=gs[ngs-1].p2=p;
    gs[ngs-1].p1=p_1;
    double ps=p+(p-p_1)*q0/(1-q0);
    gs[ngs-1].v=pp2v(ps,p);
    int i=0;
    for(; i<ngo; ++i) if(go[i]<0) break;
    if(i==ngo)
      {
        ++ngo;
        if(ArraySize(go)<ngo) ArrayResize(go,ngo,ND);
      }
    go[i]=ngs-1;
  }

bool CGaps :: change(double pbid,double pask)
  { bool ch=false;
    for(int i=0; i<ngo; ++i)
      { if(go[i]<0) continue;
        if(gs[go[i]].up()) gs[go[i]].change(pbid); else gs[go[i]].change(pask);
        if(gs[go[i]].brkn()!=0) {go[i]=-1; ch=true;}
      }
    return ch;
  }

double CGaps :: v(void)
  { double v=0;
    for(int i=0; i<ngo; ++i) if(go[i]>=0) v+=gs[go[i]].v;
    return v;
  }

void CGaps :: gs2f(string fn)
  { int f=FileOpen(fn, FILE_WRITE|FILE_COMMON|FILE_ANSI|FILE_TXT);
    int na=0, np=0, bk;
    double kt=0.0, pk=0.0;
    for(int i=0;i<ngs;++i)
      { bk=gs[i].brkn();
        if(bk==0) continue;
        ++na; if(bk>0) ++np;
        kt+=gs[i].a();
      }
     if(na>0)
       { kt/=na;
         pk=((double)np)/na;
       }
     FileWriteString(f,"na = "+(string)na+"/n");
     FileWriteString(f,"kt = "+(string)kt+"/n");
     FileWriteString(f,"pk = "+(string)pk);
     FileClose(f);
  }

我们根据2017年的数据测试了EA,并根据结果确定了2018年的交易风险值。基于2017年测试结果的余额/净值图表如下。

持仓样本9

在进行风险计算之前,我必须做一些澄清。首先,我们需要证明确定正确风险水平的必要性。其次,有必要解释将这一理论应用于这一目的的好处。

投机交易总是与不确定性有关。任何交易系统有时都会导致亏损交易。因此,风险不应太大。否则,撤军将是过度的。另一方面,市场随时可能发生变化,利润体系瞬间成为亏损体系。因此,系统的“寿命”是有限的,精确的未知。因此,风险不应太小。否则,您将无法从您的交易系统中获得所有可能的利润。

现在,让我们看看主要的方法(与我们的方法不同)来定义与简要特性相关的风险:

  • 根据一些“经验丰富的交易者”的观点,风险是通过规定价值来定义的。最受欢迎的价值范围是基金的0.5-3%。尽管缺乏正当理由,但这种方法仍然足够可取。主要缺点是没有为特定系统选择特定风险值的规则。
  • 拉尔夫·文斯的“最佳F”方法。这种方法理论上是合理的,但通常提供的高风险值不足,可能导致非常大的提款量。
  • 将风险包括在EA参数中,并在测试和优化期间对其进行定义。由于这些参数在很大程度上取决于EA的设备以及如何对其进行优化,因此很难判断结果的合法性和充分性。一个可能的障碍是缺乏对进一步交易结果不确定性的文本研究。改装也是可能的,这可能导致不合理的风险值增加(前一种方法是一个明显的例子)。我们提出的方法是一种使风险合理化的方法。

与上述方法不同,我们的方法允许我们获得足够合理的风险值。参数可调,可根据指定的交易方式定制。让我们描述一下风险计算方法的性质。我们假设我们运行我们的交易系统,直到其在指定交易数量内的平均盈利能力低于指定的最低水平,或者直到提款超过同一交易订单中指定的最高水平。之后,基于停止的系统进行事务处理(例如,重新优化其参数)。选择风险值使系统仍然具有盈利的概率(盈利能力的退出或下降是一种自然的随机波动),不应超过规定值。

这种方法在前面的文章中有详细描述。在第二篇文章中,您可以找到计算优化风险值的脚本。此脚本适用于以固定利率进入并以指定止损和止损价格退出的所有交易。在上面的文章中,它被命名为bn.mq5。

作为测试许可的结果,我们的EA将数据写入文本文件,作为风险计算脚本所需参数的一部分。剩下的参数要么预先知道,要么通过穷尽搜索选择。如果发现脚本提取的风险为零,那么我们应该放弃这个交易理念,或者削弱我们的提款/盈利需求(通过更改参数),或者在更大的历史间隔内使用交易数据。下面是脚本的一部分,其中包含要设置的参数值。

input uint na=26;                     // 该系列中的交易数量
input double kt=1.162698185452029     // 止盈/止损比率
input double pk=0.5769230769230769    // 盈利能力

double G0=0.0;                        // 最低平均盈利能力
double D0=0.9;                        // 最小增量
double dlt=0.17;                      // 显著级别

由于2017年的测试结果在交易质量方面不具启发性,我们的要求相当中立。我们设定了EA不损失钱的条件(G0=0),并且在26次交易中撤出不超过10%(D0=0.9)(NA=26)。为了获得非零风险值,我们必须设置显著水平(DLT=0.17)。事实上,如果不超过十分之一,那就更好了。我们必须做出这样大的事实:交易的结果不好。具有这些参数的EA不应在该品种的实际事务中使用。使用指定的参数,脚本提供以下风险结果:r=0.014。您可以在下面找到2018年该风险值的EA测试结果。

简单仓位 (交易)0

虽然EA在测试期间获得了一些利润,但它们不太可能保持在实际交易中。经验品种在处理共同差距上的无效性是显而易见的。这种差距非常罕见(随着时间的推移变得越来越小),而且非常小。一个更深入的研究泛缺口的价格变化之间的交易周期似乎有更多的潜力。另外,重视资产的一般缺口也是有意义的。

结束语

概率方法非常适合于EA的开发和配置。同时,它们不会与其他可能的方法冲突。相反,它通常可以作为其他方法的补充,或者作为重新思考的触发器。

在本文中,我们不讨论传统EA参数优化的主题,只在传递时提到它。这一领域与概率方法(统计方案理论)有着重要的关系。也许稍后我会详细讨论这个问题。

其他文件

您可以在下面找到两个用于收集统计数据的EAS和一个用于测试事务的EA。

nbsp; 名字。 &说明
Gaps_Reg_Stat.mq5(间隙u Reg_Stat.mq5) 每个 收集连续报价之间的差距统计信息
2 GAPS?SES?STAT.MQ5 每个 收集期间之间的差距统计
间隙测试.mq5 每个 使用期间间隔测试事务

本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/5373。

附加文件下载zip gaps_reg_stat.mq5(2.47 kb)gaps_ses_stat.mq5(3.16 kb)gaps_ses_test.mq5(4.93 kb)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经(www.myfxtop.cn)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自网络,不代表迈投财经立场,转载请注明出处:http://www.myfxtop.cn/ea/8388.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: myfxtop@hotmail.com

9:30 - 18:00/Mon-Fri
返回顶部