交易操作
在为交易所创建自己的适配器时,需要实现执行交易操作的方法,例如注册、替换和撤销订单。当 StockSharp 核心收到相应消息时,会调用这些方法。
订单注册
要注册新订单,需要实现 RegisterOrderAsync 方法。收到 OrderRegisterMessage 消息时会调用此方法。
注册订单时的主要步骤如下:
- 检查订单类型和附加条件。
- 将订单参数转换为交易所能理解的格式。
- 通过交易所 API 发送订单注册请求。
- 处理交易所响应,并发送相应的 ExecutionMessage 消息。
public override async ValueTask RegisterOrderAsync(OrderRegisterMessage regMsg, CancellationToken cancellationToken)
{
var condition = (CoinbaseOrderCondition)regMsg.Condition;
switch (regMsg.OrderType)
{
case null:
case OrderTypes.Limit:
case OrderTypes.Market:
break;
case OrderTypes.Conditional:
{
// 处理条件订单,例如资金提现
if (!condition.IsWithdraw)
break;
var withdrawId = await _restClient.Withdraw(regMsg.SecurityId.SecurityCode, regMsg.Volume, condition.WithdrawInfo, cancellationToken);
await SendOutMessageAsync(new ExecutionMessage
{
DataTypeEx = DataType.Transactions,
OrderStringId = withdrawId,
ServerTime = CurrentTime.ConvertToUtc(),
OriginalTransactionId = regMsg.TransactionId,
OrderState = OrderStates.Done,
HasOrderInfo = true,
}, cancellationToken);
await PortfolioLookupAsync(null, cancellationToken);
return;
}
default:
throw new NotSupportedException(LocalizedStrings.OrderUnsupportedType.Put(regMsg.OrderType, regMsg.TransactionId));
}
// 确定订单类型(市价或限价)
var isMarket = regMsg.OrderType == OrderTypes.Market;
var price = isMarket ? (decimal?)null : regMsg.Price;
// 向交易所发送订单
var result = await _restClient.RegisterOrder(
regMsg.TransactionId.To<string>(), regMsg.SecurityId.ToSymbol(),
regMsg.OrderType.ToNative(), regMsg.Side.ToNative(), price,
condition?.StopPrice, regMsg.Volume, regMsg.TimeInForce,
regMsg.TillDate.EnsureToday(), regMsg.Leverage, cancellationToken);
var orderState = result.Status.ToOrderState();
// 处理订单注册结果
if (orderState == OrderStates.Failed)
{
await SendOutMessageAsync(new ExecutionMessage
{
DataTypeEx = DataType.Transactions,
ServerTime = result.CreationTime,
OriginalTransactionId = regMsg.TransactionId,
OrderState = OrderStates.Failed,
Error = new InvalidOperationException(),
HasOrderInfo = true,
}, cancellationToken);
}
}
订单替换
要替换现有订单,需要实现 ReplaceOrderAsync 方法。收到 OrderReplaceMessage 消息时会调用此方法。
替换订单的主要步骤如下:
- 检查交易所是否支持订单替换。
- 通过交易所 API 发送订单替换请求。
- 处理交易所响应,并发送相应的 ExecutionMessage 消息。
public override async ValueTask ReplaceOrderAsync(OrderReplaceMessage replaceMsg, CancellationToken cancellationToken)
{
// 发送替换订单请求
await _restClient.EditOrder(
replaceMsg.OldOrderId.To<string>(),
replaceMsg.Price,
replaceMsg.Volume,
cancellationToken);
// 注意:订单替换结果的处理通常发生在
// 在收到交易所更新时调用的单独方法中
}
订单替换的细节
实现订单替换时,需要考虑交易所协议的具体行为。StockSharp 为此提供了 MessageAdapter.IsReplaceCommandEditCurrent 属性。
如果交易所协议在修改订单时保留旧标识符,则需要重写此属性并返回 true。这样 StockSharp 就知道,替换订单时不应等待交易所返回新的标识符。
public override bool IsReplaceCommandEditCurrent => true;
如果修改订单时旧订单会被撤销,并以新的交易所标识符注册新订单,则不需要重写此属性。默认情况下它返回 false,这符合大多数交易所的行为。
订单撤销
要撤销现有订单,需要实现 CancelOrderAsync 方法。收到 OrderCancelMessage 消息时会调用此方法。
撤销订单的主要步骤如下:
- 检查订单标识符的存在。
- 通过交易所 API 发送撤单请求。
- 处理交易所响应,并发送相应的 ExecutionMessage 消息。
public override async ValueTask CancelOrderAsync(OrderCancelMessage cancelMsg, CancellationToken cancellationToken)
{
// 检查订单标识符是否存在
if (cancelMsg.OrderStringId.IsEmpty())
throw new InvalidOperationException(LocalizedStrings.OrderNoExchangeId.Put(cancelMsg.OriginalTransactionId));
// 发送撤销订单请求
await _restClient.CancelOrder(cancelMsg.OrderStringId, cancellationToken);
// 注意:订单撤销结果的处理通常发生在
// 在收到交易所更新时调用的单独方法中
}
批量撤单
一些交易所支持批量撤单功能,允许通过一次请求撤销多个或全部活动订单。在某些市场条件下,这对于快速平仓或清理订单簿非常有用。
要在适配器中实现批量撤单,通常使用 CancelOrderGroupAsync 方法。当接收到 OrderGroupCancelMessage 消息时会调用此方法。
需要注意的是,并非所有交易所都支持此功能。例如,Coinbase 并未提供批量撤单的 API。在这种情况下,可能需要实现对单个订单的顺序撤单。
下面是批量撤单方法的实现示例,取自支持此功能的 BitStamp 连接器:
public override async ValueTask CancelOrderGroupAsync(OrderGroupCancelMessage cancelMsg, CancellationToken cancellationToken)
{
await _httpClient.CancelAllOrders(cancellationToken);
}
注意不要在适配器构造函数中移除此命令类型的支持:
//this.RemoveSupportedMessage(MessageTypes.OrderGroupCancel);
跟踪订单状态
在 Coinbase 以及其他一些现代交易所中,订单状态更新会通过 WebSocket 连接广播。这意味着执行交易操作(注册、替换或撤销订单)后,不需要立即通过 REST API 请求新的订单状态。适配器会通过已建立的 WebSocket 连接自动接收更新。
处理这些更新的方法类似于 SessionOnOrderReceived,这在请求投资组合和订单的当前状态一节中已经讨论过。每当交易所发送订单状态更新时,无论该更新是由用户操作触发,还是由交易所自身状态变化触发,都会调用该方法。
这种方式可以更高效地跟踪订单状态,降低交易所 API 负载,并确保实时接收更新。不过,在实现自己的适配器时,需要仔细研究所用交易所的 API 文档,才能正确配置和处理这些 WebSocket 更新。
错误处理
执行交易操作时,必须正确处理可能出现的错误和异常。如果发生错误,需要发送 ExecutionMessage 消息,并设置 Error 属性。
实现细节
实现处理交易操作的方法时,需要考虑具体交易所的特点:
- 支持的订单类型(市价单、限价单、止损单等)。
- 订单标识符的格式。
- 交易所处理订单的 API 细节。
- 可能存在的请求频率限制。