跳转至

cTrader 中的并行订单执行

cTrader 脱颖而出的功能之一是能够使用同步和异步编程来执行您的订单。 您可以使用同步编程来实现需要等待订单执行结果才能继续的策略;而异步编程则最适合用于创建可以同时提交多个订单的高速策略。

在本视频及其对应的文章中,我们将解释同步和异步执行之间的区别,并向您展示如何使用这两种方法来下新订单。

同步与异步订单执行

基本流程

同步订单执行可以通过一个简单的图表来解释。 当下新订单同步时,cBot 必须等待一个订单执行完毕才能继续执行其他指令。 换句话说,cTrader 将订单发送到服务器,等待响应,然后才开始执行下一行代码。

flowchart LR
  A([订单 1,100 ms]) --> B([订单 2,70 ms]) --> C([订单 3,80 ms]) --> D([后续指令]) 

当下上述订单同步时,您的 cBot 将总共花费 250 毫秒才能继续执行订单 3 之后的任何指令。

另一方面,异步执行允许 cTrader 并行发送多个订单,而无需等待服务器的响应。 通过使用异步编程,策略可以最大限度地减少执行多个订单所需的总时间。

flowchart LR
  subgraph Orders
    direction TB
    A([订单 1,100 ms]) --- B([订单 2,70 ms]) --- C([订单 3,80 ms])
  end
  D([后续指令])
  Orders --> D

在上面的图表中,所有三个订单都在同一时间提交,cBot 不会等待它们的执行结果就继续执行后续指令。

代码示例

我们还可以使用代码来演示同步执行的工作原理。

1
2
3
4
5
6
7
8
9
protected override void OnStart()
{
    for (int i = 0; i < 10; i++)
    {
        ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000);
        Print("Order Sent");
        Print("Positions Count:" + Positions.Count); 
    }
}

如果我们使用此代码运行 cBot,日志将显示仓位计数仅在每个仓位执行后增加。 代码在发送下一个订单的执行请求之前,会等待每个订单执行完毕。

要切换到异步订单执行,我们可以使用 ExecuteMarketOrderAsync 方法。

1
2
3
4
5
6
7
8
9
protected override void OnStart()
{
    for (int i = 0; i < 10; i++)
    {
        ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000);
        Print("Order Sent");
        Print("Positions Count:" + Positions.Count); 
    }
}

我们还将添加一个事件处理程序来监听 Positions.Opened 事件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
protected override void OnStart()
{
    Positions.Opened += Positions_Opened;
    for (int i = 0; i < 10; i++)
    {
        ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000, PositionOpened);
        Print("Order Sent");
        Print("Positions Count:" + Positions.Count); 
    }
}

在我们的事件处理程序中,我们将在仓位打开时以及在当前时刻的未平仓仓位数量时打印一条消息。

如果我们使用此代码运行 cBot 并查看日志,我们将看到所有订单都已发送,而无需收到有关先前指令结果的确认。 仓位计数器在整个过程中一直保持在 0。 确认在所有订单发送到服务器后的几毫秒内到达并打印在日志中。

使用回调订单

现在我们可以回到源代码,并演示异步执行的另一个宝贵功能,即使用回调方法的选项,这些方法在订单执行完成后立即调用。 我们将移除 Positions.Opened 事件的处理程序,并实现一个回调方法,在订单执行后打印确认信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private void TradeExecuted(TradeResult tradeResult)
{
    if (tradeResult.IsSuccessful)
    {
        Print("Trade succeeded. Position Opened.");
    }
    else
    {
        Print("Trade failed. Position Not Opened.");
    }
}

然后我们将此方法作为参数传递给 ExecuteMarketOrderAsync 方法。

1
ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000, TradeExecuted);

与我们之前的方法不同,无论交易是否成功,回调方法都会被调用,这也意味着您会被告知不成功的交易,并可以采取相应的行动。

监控异步执行的状态

cTrader 还提供了一个易于使用的工具来跟踪异步交易的状态。 ExecuteMarketOrderAsync 方法返回一个 TradeOperation 对象,其中包含有关订单是否仍在执行的信息。

我们可以首先声明一个新字段。

1
TradeOperation _tradeOperation;

然后我们将移除 for 循环,并将订单执行的结果分配给新字段。

1
_tradeOperation = ExecuteMarketOrderAsync(TradeType.Buy, SymbolName, 1000, TradeExecuted);

我们还将向 cBot 添加一个计时器。 计时器将每 100 毫秒检查一次操作的状态。

1
2
Timer.TimerTick += Timer_TimerTick;
Timer.Start(new System.TimeSpan(0, 0, 0, 0, 100));

如果操作仍在执行,计时器将在日志中打印此信息。

1
2
3
4
5
6
7
8
private void Timer_TimerTick()
{
    Print("Tick");
    if (_tradeOperation != null)
    {
        Print("Trade operation is executing: " + _tradeOperation.IsExecuting);
    }
}

cBot 构建完成后,我们可以在模拟账户上测试其工作原理。 在日志中,计时器应定期打印异步执行状态的更新。

总结

异步执行是一个强大的工具,与同步编程相比,它可以大大加快自动下单的速度。 然而,如果您需要在继续下一步之前了解交易的结果,仍然可以使用同步执行。 另一方面,当您需要并行发送多个订单而没有任何延迟时,异步执行是最佳选择。