跳转至

如何处理 Bar 事件

Bar 事件对于任何 cBot 的生命周期都至关重要。 正确处理这些事件对于管理 cBot 如何应对新的市场条件至关重要。 在本文及其对应的视频中,我们将演示 Bar 事件的关键类型,并讨论 cTrader API 暴露的这些事件的处理程序。

  • Bar - 指的是由 cTrader 在交易图表上顺序绘制的不同类型的对象。 它可以是 K 线图、Renko 砖块、Range 条形图,甚至是平均 K 线图。

处理 BarOpened 事件

当新的 Bar 开始在链接到 cBot 的交易图表上绘制时,Bar 事件就会发生。 BarOpened 事件发生在刚刚开始在图表上绘制的新 Bar 上。

处理此事件是通过 OnBar() 方法完成的,该方法是继承自基类 Robot 的方法。 我们现在将创建一个处理 BarOpened 事件的 cBot,作为我们分析市场情绪的策略的一部分。 我们将从模板中删除 OnStart()OnStop() 方法,并将 OnTick() 处理程序替换为 OnBar() 处理程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
protected override void OnBar()
{
    var previousBar = Bars[Bars.Count - 2];
    var priceDifference = ((Bars.LastBar.Open - previousBar.Open) / previousBar.Open) * 100;
    if (priceDifference > 1)
    {
        ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000);
    }
    else if (priceDifference < -1)
    {
        ExecuteMarketOrder(TradeType.Sell, SymbolName, 10000);
    }
    else 
    {
        foreach (var position in Positions) 
        {
            position.Close();
        }
    }
}

cBot 应该在 D1(日线)时间框架的图表上启动。 在每个 Bar 上,它会比较开盘价与前一个 Bar 的开盘价差异,并在合适的方向上下达新订单。 如果价格差异不显著,机器人将关闭所有当前打开的仓位。

如果我们对这个简单的机器人进行回测,结果似乎令人鼓舞。

处理 BarClosed 事件

当一个新的 Bar 打开时,它实际上除了开盘价之外没有任何价格数据。 在许多情况下,您可能希望您的 cBot 访问前一个 Bar 的数据,以确保策略的顺利执行。 这就是为什么 BarClosed 事件发生在刚刚关闭的 Bar 上(即新 Bar 之前的那个 Bar),并允许您轻松使用其价格和交易量数据。 当 BarClosed 事件被触发时,新打开的 Bar 完全从 Bars 集合中省略。

要处理 BarClosed 事件,API 暴露了 OnBarClosed 方法。

注意

在前面的示例中,我们使用 Bars.LastBar.Open 属性来获取新打开的 Bar 的开盘价。 如果我们在 OnBarClosed() 方法中使用相同的属性,我们将获得刚刚关闭的 Bar 的开盘价。

我们将创建一个简单的 cBot,使用 OnBarClosed() 处理程序如下。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
protected override void OnBarClosed()
{
    var lowCloseDifference = ((Bars.LastBar.Close - Bars.LastBar.Low) / Bars.LastBar.Close) * 100;
    if (lowCloseDifference > 0.5)
    {
        foreach (var position in Positions)
        {
            position.Close();
        }
        ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, null, null, 50);

    }
}

cBot 通过评估蜡烛底部影线的长度来检测看涨(锤子)形态。 如果检测到锤子形态,所有先前打开的仓位将被关闭,并下达一个新的买入订单,止损为 50 点。

如果我们对机器人进行回测,将获得不错的结果。

添加自定义事件处理程序

在处理 BarOpenedBarClosed 事件时,您还可以使用稍微不同的语法,将这些事件分配给自定义处理程序。 虽然 OnBar()OnBarClosed() 只触发一次,但您可以分配任意数量的自定义处理程序,这允许添加复杂的逻辑。

注意

自定义事件处理程序必须在 OnStart() 方法中添加。 自定义处理程序还必须接受 BarOpenedEventArgs(对于 BarOpened 事件)或 BarClosedEventArgs(对于 BarClosed 事件)类型的参数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
protected override void OnStart() 
{
    Bars.BarClosed += BarClosedHandler;
    Bars.BarClosed += AnotherClosedHandler;
    Bars.BarOpened += BarOpenedHandler;
}

void BarClosedHandler(BarClosedEventArgs args) {}
void AnotherClosedHandler(BarClosedEventArgs args) {}
void BarOpenedHandler(BarOpenedEventArgs args) {}

我们将创建另一个 cBot,它使用两个自定义处理程序来处理 BarOpened 事件,以应对可能的看涨和看跌反转。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
protected override void OnStart() 
{
    Bars.BarOpened += BullishReversal;
    Bars.BarOpened += BearishReversal;

}

private void BullishReversal(BarOpenedEventArgs args) 
{
    if (Bars.LastBar.Open > Bars.Last(1).Close && Bars.LastBar.Open > Bars.Last(2).Close) 
    {
        ExecuteMarketOrder(TradeType.Buy, SymbolName, 10000, null, 10, 50);
    }
}

private void BearishReversal(BarOpenedEventArgs args) 
{
    if (Bars.LastBar.Open < Bars.Last(1).Close && Bars.LastBar.Open < Bars.Last(2).Close) 
    {
        ExecuteMarketOrder(TradeType.Sell, SymbolName, 10000, null, 10, 50);
    }
}

cBot 检查新打开的 Bar 的开盘价是否高于前两个 Bar 的收盘价。 如果是这种情况,则下达买入订单。 如果开盘价低于前两个 Bar 的收盘价,则下达卖出订单。 使用两个独立的事件处理程序允许我们将交易逻辑划分为较小的组件,如果需要,我们可以在以后轻松修改这些组件。

在 D1 时间框架上进行回测的结果也是积极的。

摘要

通过学习如何正确处理 Bar 事件,您可以让您的 cBot 完全按照您的意愿执行操作。