跳转至

cBot 生命周期

在本文及其附带的视频中,我们逐步介绍了代码结构,并解释了 cTrader cBot 事件和生命周期背后的逻辑。

提醒一下,您可以在 cTrader 的Algo应用程序中访问您的 cBot。 要创建新的 cBot,只需点击新建 cBot按钮或选择下面显示的下拉菜单中的此选项。

解释基本模板

创建新的 cBot 后,您应该在代码编辑器窗口中看到以下基本结构。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class NewcBot : Robot
    {
        [Parameter(DefaultValue = "Hello world!")]
        public string Message { get; set; }

        protected override void OnStart()
        {
            // To learn more about cTrader Algo visit our Help Center:
            // https://help.ctrader.com/ctrader-algo

            Print(Message);
        }

        protected override void OnTick()
        {
            // Handle price updates here
        }

        protected override void OnStop()
        {
            // Handle cBot stop here
        }
    }
}

请注意,基本模板中只显示了三个事件;这些事件由 OnStart()OnTick()OnStop() 方法处理。 我们将添加两个额外的方法(OnBar()OnException()),并在稍后解释它们的工作原理;首先,我们将讨论 OnStart() 方法。

使用 OnStart() 方法

OnStart() 方法在 cBot 首次启动时触发。 它用于初始化您计划在 cBot 中使用的任何变量,包括指标、计数器、事件处理程序或计时器。

注意

初始代码模板已经包含了一个代码语句,用于在 cBot 启动时将消息打印到日志中。 如您所见,我们的 cBot 具有 Message 参数,OnStart() 方法将此参数的值传递给 Print() 方法。

我们可以通过在交易应用程序中启动 cBot 实例并打开日志选项卡查看结果来演示 OnStart() 方法的工作原理。

我们还可以随时停止 cBot,在参数选项卡中更改 Message 参数的值,然后重新启动我们的实例。 启动时,我们的新消息仍将打印到日志中。

使用 OnTick() 方法

每当交易品种的买入报价或卖出报价发生变化时,OnTick() 方法就会被触发。 在此方法中,您通常可以编写头寸的入场和出场条件,以及在新数据到达时需要运行的任何其他辅助功能。

我们将在 OnTick() 方法中添加以下代码,以便它将消息打印到日志中。

1
Print("OnTick. Bid: ", Symbol.Bid, ", Ask: ", Symbol.Ask);

如下所示,我们的 cBot 现在会在每次价格变化时将买入报价和卖出报价打印到日志中。

使用 OnBar() 方法

OnBar() 方法未包含在默认的 cBot 代码模板中,因此我们将添加以下代码片段来添加它。

1
2
3
4
protected override void OnBar() 
{
    Print("OnBar");
}

每当 cBot 所附图表上绘制新的柱线或蜡烛时,OnBar() 方法就会被触发。 与 OnTick() 方法类似,您可以使用 OnBar() 方法来编写头寸的入场和出场条件,以及每个新柱线形成时需要执行的任何其他逻辑。

OnBar() 方法的触发方式

如果您使用的是 H1 图表,则每小时会形成一个新的柱线,这意味着 OnBar() 方法将每小时调用一次。 如果您使用的是 m1 图表,则相同的方法将每分钟调用一次。

我们的 OnBar() 方法将在每个新柱线/蜡烛形成时打印“OnBar”。 为了演示其工作原理,我们将把 cBot 附加到 m1 图表上并查看日志。

使用 OnStop() 方法

当 cBot 由用户或通过代码停止时,OnStop() 方法会被调用。 当这种情况发生时,OnStop() 方法中的任何代码都会被执行。 这可以用于执行最终操作,例如平仓。

我们将在 OnStop() 方法中添加以下代码——与往常一样,我们的 cBot 在停止时会打印 Message 参数的值。

1
Print(Message);

在下面的截图中,我们启动并停止了一个 cBot 实例,以演示其工作原理。

使用 OnException() 方法

OnException() 方法通过捕获未处理的异常来提供容错能力,允许您决定如何处理它们。 它未包含在默认代码模板中,因此我们将使用以下代码片段自行添加它。

1
2
3
4
protected override void OnException(Exception exception) 
{
    Print("Ooops, unhandled exception! No worries, cBot still alive");            
}

为了触发 OnException() 方法,我们将编写一些代码来捕获当我们尝试访问不再存在的交易信息时发生的异常。 我们将在 OnStart() 方法中添加以下代码。

1
ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000, "my label");

我们在 OnStart() 方法中编写的代码将执行一个具有以下可配置参数的市价单。

  • 交易类型将是买单。
  • 交易将针对 cBot 所附图表的当前交易品种执行。
  • 订单交易量将为 1,000 单位。
  • 附加到订单的唯一标签将是“my label”。

我们还将在 OnTick() 方法中添加以下代码。

1
2
3
var position = Positions.Find("my label");
if (Positions.Find("my label") != null) Print("PositionId: ", position.Id);
Print("Message below");

我们在 OnTick() 方法中编写的代码将使用标签名称“my label”找到在 OnStart() 方法中打开的头寸。

如果找到具有相同标签的头寸,它将打印未平仓交易的头寸 ID 到日志中。 它还将打印另一条名为“Message below”的消息,作为下一行代码的执行。

当我们的 cBot 启动并调用 OnTick() 方法且找到订单时,cBot 将打印订单 ID 到日志中,并在订单 ID 之后打印“Message below”消息。

如您所见,没有出现任何异常,我们的 cBot 完美地完成了它的工作。

然而,现在我们将手动关闭由 cBot 打开的头寸,并对代码进行一些小的更改,以确保一定会发生异常。 在 OnStart() 方法中,我们将订单标签更改为“cTrader”。 在 OnTick() 方法中,我们将注释掉检查是否存在带有“my label”标签的头寸的条件。

这是我们 cBot 的最终代码——请注意,此代码将始终触发异常。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace cAlgo.Robots
{
    [Robot(AccessRights = AccessRights.None)]
    public class LifecycleTest : Robot
    {
        [Parameter(DefaultValue = "cTrader rocks!")]
        public string Message { get; set; }

        protected override void OnStart()
        {
            ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000, "cTrader");
        }

        protected override void OnTick()
        {
            var position = Positions.Find("my label");
            //if (Positions.Find("my label") != null) 
            Print("PositionId: ", position.Id);
            Print("Message below");
        }

        protected override void OnStop()
        {
            Print(Message);
        }

        protected override void OnException(Exception exception) 
        {
            Print("Ooops, unhandled exception! No worries, cBot still alive");            
        }
    }
}

我们将再次运行 cBot 并看看会发生什么。

发生的情况是,当 cBot 启动时创建了一个新的市价单。 附加到此订单的标签是 "cTrader"。 在 OnTick() 方法中,代码尝试获取带有 my label 标签的头寸,并且由于我们注释掉了带有条件的代码行。 然后它尝试将头寸 ID 打印到日志中。

这导致了一个异常错误,在以前的 cTrader 版本中,这通常会停止 cBot,但现在有了新的 OnException() 方法,我们可以捕获并处理此错误。 cBot 将继续运行,我们可以记录错误以便修复。

摘要

cBot 的生命周期由几个关键事件组成,您可以通过 OnStart()OnTick()OnStop()OnBar()OnException() 方法来处理这些事件。 通过自定义 cBot 对主要事件的响应方式,您可以确保您的算法完全按照预期运行。