我正在使用GDAX API Websocket Stream尝试创建完整LEVEL3订单的副本。
我使用WebSocketSharp实现了一个非常简单的实现,我基本上做了类似的事情。
private readonly WebSocket _webSocket = new WebSocket("wss://ws-feed.gdax.com");
_webSocket.OnMessage += WebSocket_OnMessage;
_webSocket.Connect();
_webSocket.Send(JsonConvert.SerializeObject(new BeginSubscriptionMessage()));
private void WebSocket_OnMessage(object sender, MessageEventArgs e)
{
var message = JsonConvert.DeserializeObject<BaseMessage>(e.Data);
switch (message.Type)
{
case "match": //A trade occurred between two orders.
MatchMessage matchMessage = JsonConvert.DeserializeObject<MatchMessage>(e.Data);
_receivedMatchQueue.Enqueue(matchMessage);
break;
case "received": //A valid order has been received and is now active. This message is emitted for every single valid order as soon as the matching engine receives it whether it fills immediately or not.
ReceivedMessage receivedMessage = JsonConvert.DeserializeObject<ReceivedMessage>(e.Data);
_receivedMessageQueue.Enqueue(receivedMessage);
break;
case "open": //The order is now open on the order book. This message will only be sent for orders which are not fully filled immediately. remaining_size will indicate how much of the order is unfilled and going on the book.
OpenMessage openMessage = JsonConvert.DeserializeObject<OpenMessage>(e.Data);
_receivedOpenQueue.Enqueue(openMessage);
break;
case "done": //The order is no longer on the order book. Sent for all orders for which there was a received message. This message can result from an order being canceled or filled.
DoneMessage doneMessage = JsonConvert.DeserializeObject<DoneMessage>(e.Data);
_receivedDoneQueue.Enqueue(doneMessage);
break;
case "change": //Existing order has been changed
ChangeMessage changeMessage = JsonConvert.DeserializeObject<ChangeMessage>(e.Data);
_receivedChangeQueue.Enqueue(changeMessage);
break;
case "activate": //Stop order placed
//Console.WriteLine("Stop Order Placed");
//ActivateMessage activateMessage = JsonConvert.DeserializeObject<ActivateMessage>(e.Data);
break;
case "subscriptions":
break;
case "ticker":
TickerMessage tickerMessage = JsonConvert.DeserializeObject<TickerMessage>(e.Data);
_receivedTickerQueue.Enqueue(tickerMessage);
break;
case "l2update":
break;
}
}
我遇到的问题是,当我查看通过RECEIVED和OPEN消息收到的序列号时,我可以看到它们不是顺序的(based on the following information)表示正在跳过消息。< / p>
基本上你最终会得到像这样的东西
Open Message SequenceId: 5359746354
Open Message SequenceId: 5359746358
Open Message SequenceId: 5359746361
Open Message SequenceId: 5359746363
Open Message SequenceId: 5359746365
Open Message SequenceId: 5359746370
Open Message SequenceId: 5359746372
我已经尝试在Azure上测试它,只是为了确保它不是我的终端带宽限制,结果大致相似。
因此,如果删除消息,如何使用“完整”websocket流构建完整的“实时”订单?我可以安全地忽略它们吗?或者我只是以某种方式清除孤立的价值观?
任何做过类似事情的人的建议都会非常感激。
答案 0 :(得分:2)
最有可能的信息不会丢失,你只是对这些序列号所代表的“序列”有错误的印象。
如api文档中所述
大多数Feed消息都包含序列号。序号是 每个新邮件增加每个产品的整数值 正好比前一个序列号少1个。
因此,每个频道的每个频道都有单独的序列号(如ETH-USD
),而不是每种消息类型(如“开放”或“接收”)。假设您为产品ETH-USD
和ETH-EUR
订阅了“完整”渠道。然后你应该期待这样的序列:
receive `ETH-EUR` X
open `ETH-EUR` X + 1
receive `ETH-USD` Y
done `ETH-EUR` X + 2
open `ETH-USD` Y + 1
对于完整频道,消息类型包括:接收,打开,完成,匹配,更改,激活(请注意,自动收报机消息属于不同的通道,因此具有单独的序列)。因此,为了确保不会跳过任何消息,您需要跟踪所有这些消息类型,并确保您收到的最后一个序列号比每个产品的新序列号少1(如果您订阅了多个产品)。
证明代码:
class Program {
private static readonly WebSocket _webSocket = new WebSocket("wss://ws-feed.gdax.com");
private static long _lastSequence = 0;
private static readonly HashSet<string> _expectedTypes = new HashSet<string>(
new[] { "received", "open", "done", "match", "change", "activate" });
static void Main(string[] args) {
var subMsg = "{\"type\": \"subscribe\",\"product_ids\": [\"ETH-USD\"],\"channels\": [\"full\"]}";
_webSocket.OnMessage += WebSocket_OnMessage;
_webSocket.Connect();
_webSocket.Send(subMsg);
Console.ReadKey();
}
private static void WebSocket_OnMessage(object sender, MessageEventArgs e) {
var message = JsonConvert.DeserializeObject<BaseMessage>(e.Data);
if (_expectedTypes.Contains(message.Type)) {
lock (typeof(Program)) {
if (_lastSequence == 0)
_lastSequence = message.Sequence;
else {
if (message.Sequence > _lastSequence + 1) {
Debugger.Break(); // never hits, so nothing is dropped
}
_lastSequence = message.Sequence;
}
}
}
}
}
public class BaseMessage {
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("product_id")]
public string ProductId { get; set; }
[JsonProperty("sequence")]
public long Sequence { get; set; }
}