跳转至

常用数据结构

  • 这些数据结构都在C++中定义,通过pybind导出给Python使用
  • C++提供了API,可以直接读取共享内存的这些数据结构

Ticker

  • 一档的买卖价格和数量
struct alignas(CACHE_LINE_SIZE) Ticker
{
    Exchange exchange;      // 交易所
    CurrencyPair symbol;    // 交易对
    int64_t timestamp;      // 服务器时间戳(微秒)
    int64_t localTimestamp; // 本地时间戳(微秒)
    double askPrice;        // 卖一价
    double askVolume;       // 卖一量
    double bidPrice;        // 买一价
    double bidVolume;       // 买一量
};
namespace py = pybind11;
PYBIND11_MODULE(hft, m)
{
    py::class_<hft::Ticker>(m, "Ticker")
        .def(py::init([](hft::Exchange exchange, hft::CurrencyPair symbol, int64_t timestamp, int64_t localTimestamp,
                         double askPrice, double askVolume, double bidPrice, double bidVolume)
                      { return hft::Ticker{exchange, symbol, timestamp, localTimestamp, askPrice, askVolume, bidPrice, bidVolume}; }),
             py::arg("exchange") = hft::Exchange::UNKNOWN,
             py::arg("symbol") = hft::CurrencyPair::UNKNOWN,
             py::arg("timestamp") = 0,
             py::arg("localTimestamp") = 0,
             py::arg("askPrice") = 0.0,
             py::arg("askVolume") = 0.0,
             py::arg("bidPrice") = 0.0,
             py::arg("bidVolume") = 0.0)
        .def_readwrite("exchange", &hft::Ticker::exchange)
        .def_readwrite("symbol", &hft::Ticker::symbol)
        .def_readwrite("timestamp", &hft::Ticker::timestamp)
        .def_readwrite("localTimestamp", &hft::Ticker::localTimestamp)
        .def_readwrite("askPrice", &hft::Ticker::askPrice)
        .def_readwrite("askVolume", &hft::Ticker::askVolume)
        .def_readwrite("bidPrice", &hft::Ticker::bidPrice)
        .def_readwrite("bidVolume", &hft::Ticker::bidVolume)
        .def("__repr__", [](const hft::Ticker &t)
             { return tickerToStr(t); });
    m.def("tickerToStr", &hft::tickerToStr, "ticker to str");
}

示例:获取CurrencyPair的Ticker

#include "hft/util/shm.h"

Exchange exchange = Exchange::KRAKEN;
CurrencyPair symbol = CurrencyPair::BTC_USD;
Ticker *ticker = ShareMemoryTicker::get(exchange, symbol);

// 输出行情数据
std::cout << "BTC_USD Ticker - Ask: " << ticker->askPrice 
          << " (" << ticker->askVolume << "), "
          << "Bid: " << ticker->bidPrice 
          << " (" << ticker->bidVolume << ")" << std::endl;
import hft

exchange = hft.Exchange.KRAKEN
symbol = hft.CurrencyPair.BTC_USD
ticker = hft.getTicker(exchange, symbol)

# 输出行情数据
print(f"BTC_USD Ticker - Ask: {ticker.askPrice} ({ticker.askVolume}), "
      f"Bid: {ticker.bidPrice} ({ticker.bidVolume})")

Trade

  • 市场的Trade数据
struct alignas(CACHE_LINE_SIZE) Trade
{
    Exchange exchange;      // 交易所
    CurrencyPair symbol;    // 交易对
    int64_t timestamp;      // 服务器时间戳(微秒)
    int64_t localTimestamp; // 本地时间戳(微秒)
    int64_t tradeTimestamp; // 成交时间戳(微秒)
    Side side;              // 买卖方向
    double price;           // 成交价格
    double volume;          // 成交量
};
namespace py = pybind11;
PYBIND11_MODULE(hft, m)
{
    py::class_<hft::Trade>(m, "Trade")
        .def(py::init([](hft::Exchange exchange, hft::CurrencyPair symbol, int64_t timestamp, int64_t localTimestamp,
                            int64_t tradeTimestamp, hft::Side side, double price, double volume)
                        { return hft::Trade{exchange, symbol, timestamp, localTimestamp, tradeTimestamp, side, price, volume}; }),
                py::arg("exchange") = hft::Exchange::UNKNOWN,
                py::arg("symbol") = hft::CurrencyPair::UNKNOWN,
                py::arg("timestamp") = 0,
                py::arg("localTimestamp") = 0,
                py::arg("tradeTimestamp") = 0,
                py::arg("side") = hft::Side::UNKNOWN,
                py::arg("price") = 0.0,
                py::arg("volume") = 0.0)
        .def_readwrite("exchange", &hft::Trade::exchange)
        .def_readwrite("symbol", &hft::Trade::symbol)
        .def_readwrite("timestamp", &hft::Trade::timestamp)
        .def_readwrite("localTimestamp", &hft::Trade::localTimestamp)
        .def_readwrite("tradeTimestamp", &hft::Trade::tradeTimestamp)
        .def_readwrite("side", &hft::Trade::side)
        .def_readwrite("price", &hft::Trade::price)
        .def_readwrite("volume", &hft::Trade::volume)
        .def("__repr__", [](const hft::Trade &t)
                { return tradeToStr(t); });
    m.def("tradeToStr", &hft::tradeToStr, "trade to str");
}

示例:获取CurrencyPair的Trade

#include "hft/util/shm.h"

Exchange exchange = Exchange::KRAKEN;
CurrencyPair symbol = CurrencyPair::BTC_USD;
Trade *trade = ShareMemoryTrade::get(exchange, symbol);

// 输出成交数据
std::cout << "BTC_USD Trade - Side: " << sideToStr(trade->side)
          << ", Price: " << trade->price
          << ", Volume: " << trade->volume
          << ", Time: " << trade->tradeTimestamp << std::endl;
import hft

exchange = hft.Exchange.KRAKEN
symbol = hft.CurrencyPair.BTC_USD
trade = hft.getTrade(exchange, symbol)

# 输出成交数据
print(f"BTC_USD Trade - Side: {trade.side}, "
      f"Price: {trade.price}, Volume: {trade.volume}, "
      f"Time: {trade.tradeTimestamp}")

Snapshot

  • 订单册快照,每个深度的价格和数量
  • 交易系统底层会把数据流实时合成订单册快照,写入共享内存,上层应用可以直接读取最新的订单册快照
  • 底层C++在填充Snapshot的时候, askCount、bidCount范围内的数据保证有效,超出askCount、bidCount的部分不保证数据有效性,使用时需自行根据count判断数据范围
struct alignas(CACHE_LINE_SIZE) Snapshot
{
    Exchange exchange;      // 交易所
    CurrencyPair symbol;    // 交易对
    int64_t timestamp;      // 服务器时间戳(微秒)
    int64_t localTimestamp; // 本地时间戳(微秒)
    int askCount;           // 卖档数量
    int bidCount;           // 买档数量

    static constexpr int MAX_DEPTH = 256;
    double asksPrice[MAX_DEPTH];  // 卖价列表
    double asksVolume[MAX_DEPTH]; // 卖量列表
    double bidsPrice[MAX_DEPTH];  // 买价列表
    double bidsVolume[MAX_DEPTH]; // 买量列表
};
namespace py = pybind11;
PYBIND11_MODULE(hft, m)
{
    py::class_<hft::Snapshot>(m, "Snapshot")
    .def(py::init<>())
    .def_readonly("exchange", &hft::Snapshot::exchange)
    .def_readonly("symbol", &hft::Snapshot::symbol)
    .def_readonly("timestamp", &hft::Snapshot::timestamp)
    .def_readonly("localTimestamp", &hft::Snapshot::localTimestamp)
    .def_readonly("askCount", &hft::Snapshot::askCount)
    .def_readonly("bidCount", &hft::Snapshot::bidCount)
    .def_property_readonly("asksPrice",
                            [](const hft::Snapshot &s)
                            {
                                return py::array_t<double>(
                                    {hft::Snapshot::MAX_DEPTH}, {sizeof(double)}, s.asksPrice, py::capsule([]() {}));
                            })
    .def_property_readonly("asksVolume",
                            [](const hft::Snapshot &s)
                            {
                                return py::array_t<double>(
                                    {hft::Snapshot::MAX_DEPTH}, {sizeof(double)}, s.asksVolume, py::capsule([]() {}));
                            })
    .def_property_readonly("bidsPrice",
                            [](const hft::Snapshot &s)
                            {
                                return py::array_t<double>(
                                    {hft::Snapshot::MAX_DEPTH}, {sizeof(double)}, s.bidsPrice, py::capsule([]() {}));
                            })
    .def_property_readonly("bidsVolume",
                            [](const hft::Snapshot &s)
                            {
                                return py::array_t<double>(
                                    {hft::Snapshot::MAX_DEPTH}, {sizeof(double)}, s.bidsVolume, py::capsule([]() {}));
                            })
    .def("__repr__", [](const hft::Snapshot &s)
            { return snapshot5ToStr(s); });
    m.def("snapshot5ToStr", &hft::snapshot5ToStr, "snapshot5 to str");
}

示例:获取CurrencyPair的Snapshot

#include "hft/util/shm.h"

Exchange exchange = Exchange::KRAKEN;
CurrencyPair symbol = CurrencyPair::BTC_USD;
Snapshot *snapshot = ShareMemorySnapshot::get(exchange, symbol);

// 输出快照数据
std::cout << "BTC_USD Snapshot:" << std::endl;
std::cout << "Ask Count: " << snapshot->askCount << std::endl;
std::cout << "Best Ask: " << snapshot->asksPrice[0] 
          << " (" << snapshot->asksVolume[0] << ")" << std::endl;
std::cout << "Bid Count: " << snapshot->bidCount << std::endl;
std::cout << "Best Bid: " << snapshot->bidsPrice[0] 
          << " (" << snapshot->bidsVolume[0] << ")" << std::endl;
import hft

exchange = hft.Exchange.KRAKEN
symbol = hft.CurrencyPair.BTC_USD
snapshot = hft.getSnapshot(exchange, symbol)

# 输出快照数据
print(f"BTC_USD Snapshot:")
print(f"Ask Count: {snapshot.askCount}")
print(f"Best Ask: {snapshot.asksPrice[0]} ({snapshot.asksVolume[0]})")
print(f"Bid Count: {snapshot.bidCount}")
print(f"Best Bid: {snapshot.bidsPrice[0]} ({snapshot.bidsVolume[0]})")

# 遍历所有卖盘档位
print(f"\nAsks (Top 5):")
for i in range(min(5, snapshot.askCount)):
    print(f"  Level {i+1}: {snapshot.asksPrice[i]} @ {snapshot.asksVolume[i]}")

# 遍历所有买盘档位
print(f"\nBids (Top 5):")
for i in range(min(5, snapshot.bidCount)):
    print(f"  Level {i+1}: {snapshot.bidsPrice[i]} @ {snapshot.bidsVolume[i]}")

PySnapshot

为什么要有PySnapshot这个结构?因为C++导出的Snapshot这个结构,所有字段都是只读的,不可以修改。这在实盘的场景中是没有问题的。我们实盘的场景是底层C++合成订单册快照,写入共享内存。上层的python应用只需要读取共享内存中的订单册快照进行使用,并不需要修改订单册快照。如果python一侧要做一些其他的逻辑,例如:用历史数据自己合成订单册,可以使用PySnapshot这个结构

import hft
Snapshot = hft.Snapshot # C++定义的snapshot

# python中定义的snapshot
class PySnapshot(msgspec.Struct):
    exchange: Exchange          # 交易所
    symbol: CurrencyPair        # 交易对
    timestamp: int              # 服务器时间戳(微秒)
    localTimestamp: int        # 本地时间戳(微秒)
    askCount: int              # 卖档数量
    bidCount: int              # 买档数量
    asksPrice: List[float]     # 卖价列表
    asksVolume: List[float]    # 卖量列表
    bidsPrice: List[float]     # 买价列表
    bidsVolume: List[float]    # 买量列表

PySnapshot这个结构的字段和Snapshot完全一致,对于使用者来说,传入PySnapshotSnapshot是没有任何区别的。

def foo(snapshot: Union[PySnapshot, Snapshot]):
    pass