作者:杨几安

因子投资和时序模型的坑还没填完,这里又要开一个高频交易的新坑ORZ

这个系列的目的,正如文章名字所指明的,是一个高频交易的入门系列,从0开始介绍高频交易的名词、思路,最后在系列结尾的时候会分享几个我曾经用来赚钱的策略。

再次引用一下在因子投资那一期中的内容:

经济学家斯蒂芬·罗斯(Stephen Ross)于1976年提出的资产定价模型。它通过多因子模型解释资产收益,强调市场中不存在套利机会时,资产的预期收益可由多个系统性风险因子线性决定​。

当市场中不存在套利机会的时候,因子投资收益稳定。当市场存在套利机会的时候,就是高频交易出手的时候了。

OrderBook

OrderBook,中文一般翻译为订单簿。然后根据这个行为是否成功发生,又可以分为quotes(报价)和Trades(交易)。

Quotes指的是市场上买卖双方提供的关于特定金融工具(如股票、期货、外汇等)的价格信息。这些信息通常包括最佳买价(Bid)、最佳卖价(Ask)、买卖双方的订单量等。

Trades记录了实际发生的交易,包括交易的价格、数量、时间戳等信息。

如果只是收集一只股票的orderbook,这个数据属于时序。如果同时收集了那一时刻所有股票的orderbook,则在使用上则归类为截面数据。

这两类数据是高频交易里最重要的数据,各位大神们通过这两种数据计算市场深度、市场情绪、评估交易成本等等。

大家所熟知的“卷”也是在OrderBook的处理过程,你比竞争对手早一点接收和处理完数据,就早一步下单。差1ms,就是赢钱和亏钱的区别。

所以有人就会去租与交易所在同一物理机房的机器做高频交易,以最大化降低时延的影响。

曾经我就有一个套利策略,本来稳定运行的很好,结果这个套利机会被别人也发现了,两个人就开始卷起来了。后来人越来越多,我的策略逐渐从盈利走向亏损,最后成交速度永远比不过别人,就只能怨自己菜,转而研究别的方向了。

一般来说,只有limit order(限价单)才会体现到Quotes上,因为如果做市价单一般会直接吃掉,进而体现在Trades,当然,如果极端情况下市场流动性不足,则market order也只能体现在quotes上。

更一般的,我们以maker 和 taker 来区分。

为市场注入流动性的,一般是maker。从市场中拿走流动性的,称之为taker。

作为一个交易所,当然希望更多的人来提供流动性,因此从交易费率上来说,maker的费率是远低于taker的。

甚至很多高频做市商的交易费率是负的。

接下来结合某所的 websocket stream 数据结构为例来介绍。

Trades

{
  "e": "trade",       // Event type
  "E": 1672515782136, // Event time
  "s": "BNBBTC",      // Symbol
  "t": 12345,         // Trade ID
  "p": "0.001",       // Price
  "q": "100",         // Quantity
  "T": 1672515782136, // Trade time
  "m": true,          // Is the buyer the market maker?
  "M": true           // Ignore
}

“e”: “trade”:表示事件类型为“交易”(trade)。 “E”: 1672515782136:表示事件的时间戳,通常是自Unix纪元(1970年1月1日)以来的毫秒数。 “s”: “BBTC”:表示交易的金融产品符号,这里是比特币(Bitcoin)。 “t”: 12345:表示交易的唯一标识符(ID)。 “p”: “0.001”:表示交易的价格,这里是0.001比特币。 “q”: “100”:表示交易的数量,这里是100单位的比特币。 “T”: 1672515782136:表示交易发生的具体时间戳。 “m”: true:表示是否是做市商(market maker)。如果是true,意味着这笔交易是做市商提供的流动性,如果是false,则表示是接受方(taker)。

这个就表示有一位maker的交易在1970年1月1日以0.001的价格成交了100btc。

然后按照你订阅的数据流的时效性,一般是5/10/20ms , 你都会收到如上的报文。编写你的交易策略对报文处理后做决策。

Quotes

很明显,trades一般就是拿大喇叭告诉你一声xxx成交了(莫名想起来缅北诈骗里的一个习俗,如果骗到人了就放50响礼炮,表示到手50万)

trades 就是干这个事情的。

一般做高频的会更加的关注quotes,因为它才包含了更多的市场信息。通常一个quotes会包含如下信息:

{
  "lastUpdateId": 160,  // Last update ID
  "bids": [             // Bids to be updated
    [
      "0.0024",         // Price level to be updated
      "10"              // Quantity
    ]
  ],
  "asks": [             // Asks to be updated
    [
      "0.0026",         // Price level to be updated
      "100"             // Quantity
    ]
  ]
}

如果你同时订阅了多个股票或者coin,他发给你的是一个聚合信息(aggStreams):

{
  "e": "depthUpdate", // Event type
  "E": 1672515782136, // Event time
  "s": "BNBBTC",      // Symbol
  "U": 157,           // First update ID in event
  "u": 160,           // Final update ID in event
  "b": [              // Bids to be updated
    [
      "0.0024",       // Price level to be updated
      "10"            // Quantity
    ]
  ],
  "a": [              // Asks to be updated
    [
      "0.0026",       // Price level to be updated
      "100"           // Quantity
    ]
  ]
}

与trades相比多了两个字段:b或者bid,a或者ask。

bid就是挂买单,有人愿意出价0.0024买入10单位的bnb。

ask就是挂卖单,有人愿意出价0.0026卖出100单位的bnb。

注意,我说的是“有人”,并不代表一个人。这10单位的bnb是交易所在处理完之后的结果,可能有1000个人下了2000单,它们的价格都是0.0024,最终体现在quotes上是这个结果。

而且,这个上边都是未成交的信息,成交的信息不体现在quotes,而是在Trades上。

所以也有可能是要以0.0024买入20单位,但是买到一半,市场上没有这个价格的了,因此体现了10单位在quotes上。

当然,这个信息在每个股民或者币民的交易app上也有展示,只不过这个是以api方式获取的高频数据。

images

在数据库中,未成交的报价长这个样子。

当你拿到这个quotes数据之后,关心的就是两个问题:

最优报价是多少?

满足我需求的价格下,深度depth是否满足?

还是用代码来说明,这里是我之前一个策略中写过的代码。

首先计算当前quotes中的交易深度是否能够满足我设置的阈值,以排除极端流动性不足的情况,如果流动性充足,我则以满足我要买的数量的阈值基础上,以比它低0.0005的价格做maker。

同样,我会以比它高0.0005的价格挂卖出单做maker。

提一句,这个价格就是你想买入或者卖出单best price,更严谨的话,是计算一个数学加权平均数。

def get_bothway_best_bid_and_ask(bids:list, asks:list,volumn_limit=3000)->Tuple[float,float]:
    bid_vol,bid_price = 0,0
    for [i,j] in bids:
        bid_vol += float(j)
        if bid_vol >= volumn_limit:
            best_bid = float(i) - 0.0005
        else:
            best_bid = 9999999

    ask_vol,ask_price = 0,0
    for [i,j] in asks:
        ask_vol += float(j)
        if ask_vol >= volumn_limit:
            best_ask= float+0.0005
        else:
            best_ask = -1
    return best_bid,best_ask

maker 和 taker

无论是从上边的数据示例,还是我们的生活经验,好像市场上的bid 永远小于ask。他们两个之间总是存在一定的差距,比如上面0.0002。

这种状态是市场的稳态。

但是在极端情况,大量买单冲击,是会出现bid 大于ask的情况,这是市场的非稳态。更一般的,如果市场上突然出现大量taker,交易所来不及处理,是会出现bid=ask的。

因为在交易的时候,我们总会倾向于当下的最优价格,所以一般来说是以best bid 或者 best ask来成交的。

我们通常意义上的股票价格,比如茅台价格是1436,是指的当时的成交价格。

所以试想一下,如果有人以0.0024卖出了,那么股票价格就是0.0024,如果以0.0026买入了,股票价格就是0.0026。聪明的你已经想好怎么拉盘控盘了吧(doge

成交对市场的影响就是best bid 和 best ask 的差距进一步的加大。也就是说愿意买的人、和愿意卖的人之间的分歧进一步加大。体现在股价上,流动性不足的股票价格就是忽上忽下,不是连续的折现(因为gap太大了

交易所是靠手续费吃饭的,他们肯定不愿意看到这种情况,所以会引入做市商来缩小gap,提供流动性。

小测验:

你收到了如上的quotes时候,你选择以0.0024 的价格出售10个bnb,为了节约手续费,你选择的是limit order,请问你是maker 还是 taker?