Market Data¶
All market data types are C-level structures exposed to Python via Cython.
They provide Python-friendly accessors with near-C performance. Every type
inherits from MarketData.
Common MarketData properties: ticker, timestamp (float, Unix
seconds), market_price, market_time, dtype, topic,
session_date, session_time.
Data Types¶
TickDataLite¶
Level-1 snapshot without order book depth.
from algo_engine.base import TickDataLite
tick = TickDataLite(
ticker="000001.SH",
timestamp=1718400000.0,
last_price=15.28,
bid_price=15.27,
bid_volume=5000.0,
ask_price=15.29,
ask_volume=3200.0,
open_price=15.20,
prev_close=15.35,
total_traded_volume=15000000.0,
total_traded_notional=228000000.0,
total_trade_count=3421,
)
Properties: last_price, bid_price, bid_volume, ask_price,
ask_volume, open_price, prev_close, total_traded_volume,
total_traded_notional, total_trade_count.
Computed: mid_price ((bid+ask)/2), spread (ask-bid), market_price.
TickData¶
Level-2 snapshot with full order book. The order book depth is fixed at
compile time (BOOK_SIZE, default 10).
from algo_engine.base import TickData
tick = TickData(
ticker="000001.SH",
timestamp=1718400000.0,
last_price=15.28,
total_traded_volume=15000000.0,
total_bid_volume=120000.0,
total_ask_volume=80000.0,
weighted_bid_price=15.271,
weighted_ask_price=15.292,
# Order book via kwargs:
bid_price_1=15.27, bid_volume_1=5000.0, bid_n_orders_1=12,
ask_price_1=15.29, ask_volume_1=3200.0, ask_n_orders_1=8,
bid_price_2=15.26, bid_volume_2=8000.0,
ask_price_2=15.30, ask_volume_2=6000.0,
)
# Best prices
tick.best_bid_price # 15.27
tick.best_ask_price # 15.29
# Order book access
tick.bid.at_level(0) # (15.27, 5000.0, 12)
tick.bid.at_price(15.27) # same, by price lookup
tick.bid.loc_volume(15.25, 15.28) # total vol in price range
tick.bid.price # numpy array of prices
tick.bid.volume # numpy array of volumes
# Extract lite (zero-copy view or owned copy)
lite = tick.lite(copy=False) # non-owning view
lite_copy = tick.lite(copy=True) # independent copy
Additional properties: total_bid_volume, total_ask_volume,
weighted_bid_price, weighted_ask_price.
Parse order book data from a dict:
tick.parse({"bid_price_1": 15.27, "bid_volume_1": 5000.0, ...})
OrderBook¶
Represents one side (bid or ask). Construct standalone or access via
tick.bid / tick.ask.
from algo_engine.base import OrderBook, TransactionDirection
ob = OrderBook(
direction=TransactionDirection.DIRECTION_LONG, # bid side
price=[15.27, 15.26, 15.25],
volume=[5000.0, 8000.0, 3000.0],
n_orders=[12, 20, 5],
is_sorted=True,
)
ob.sort() # sort by price (ascending for bids, descending for asks)
ob.to_numpy() # 2D numpy array
raw = ob.to_bytes() # serialize
Properties: price (float64 array), volume (float64 array),
n_orders (uint64 array), side (TransactionSide), direction,
size, capacity, sorted.
BarData¶
OHLCV candlestick. Timestamp is the END of the bar to prevent look-ahead bias.
from algo_engine.base import BarData
bar = BarData(
ticker="000001.SH",
timestamp=1718400300.0, # bar end
high_price=15.50,
low_price=15.10,
open_price=15.20,
close_price=15.28,
volume=1500000.0,
notional=22800000.0,
trade_count=3421,
start_timestamp=1718400000.0, # bar start
bar_span=300.0, # 5 min in seconds
)
bar.vwap # volume-weighted average price
bar.bar_type # 'Minute', 'Hourly', 'Sub-Minute', etc.
bar.bar_span # timedelta
bar.bar_start_time # datetime
bar.bar_end_time # datetime
# Dict-style access and mutation
bar["close_price"] # 15.28
bar["close_price"] = 15.30
DailyBar¶
Full-day candlestick. Uses market_date (datetime.date) instead of
timestamp.
from algo_engine.base import DailyBar
from datetime import date
daily = DailyBar(
ticker="000001.SH",
market_date=date(2024, 6, 15),
high_price=15.80,
low_price=15.00,
open_price=15.20,
close_price=15.45,
volume=50000000.0,
notional=760000000.0,
trade_count=50000,
bar_span=1, # number of days
)
daily.market_date # date(2024, 6, 15)
daily.bar_type # 'Daily' or 'Daily-Plus'
Transaction / Order / Trade¶
from algo_engine.base import (
TransactionData, OrderData, TradeData,
TransactionDirection, TransactionOffset, TransactionSide,
OrderType,
)
# side = direction | offset (int enum via | operator)
side = TransactionDirection.DIRECTION_LONG | TransactionOffset.OFFSET_OPEN
txn = TransactionData(
ticker="000001.SH",
timestamp=1718400000.0,
price=15.28,
volume=1000.0,
side=side,
multiplier=1.0,
transaction_id="trd_001",
buy_id="bid_001",
sell_id="ask_001",
)
txn.side # TransactionSide.SIDE_LONG_OPEN
txn.side_sign # 1 (long), -1 (short), 0 (cancel/unknown)
txn.volume_flow # signed volume (volume * side_sign)
txn.notional_flow # signed notional
# Merge multiple transactions
merged = TransactionData.merge([txn1, txn2, txn3])
# Order — placed into the order book
order = OrderData(
ticker="000001.SH",
timestamp=1718400000.0,
price=15.25,
volume=500.0,
side=TransactionDirection.DIRECTION_LONG | TransactionOffset.OFFSET_ORDER,
order_id="ord_001",
order_type=OrderType.ORDER_LIMIT,
)
# TradeData — alias with trade_* parameter names
trade = TradeData(
ticker="000001.SH",
timestamp=1718400000.0,
trade_price=15.28,
trade_volume=1000.0,
trade_side=side,
)
Enums:
TransactionDirection:DIRECTION_UNKNOWN,DIRECTION_SHORT,DIRECTION_LONG,DIRECTION_NEUTRALTransactionOffset:OFFSET_CANCEL,OFFSET_ORDER,OFFSET_OPEN,OFFSET_CLOSEOrderType:ORDER_UNKNOWN,ORDER_CANCEL,ORDER_GENERIC,ORDER_LIMIT,ORDER_LIMIT_MAKER,ORDER_MARKET,ORDER_FOK,ORDER_FAK,ORDER_IOC
Trade Utilities¶
from algo_engine.base import OrderState, TradeReport, TradeInstruction
# TradeReport — execution confirmation
report = TradeReport(
ticker="000001.SH", timestamp=1718400000.0,
price=15.28, volume=500.0,
side=TransactionDirection.DIRECTION_LONG | TransactionOffset.OFFSET_OPEN,
fee=2.5, order_id="ord_001", trade_id="trd_001",
)
# TradeInstruction — order lifecycle tracking
instr = TradeInstruction(
ticker="000001.SH", timestamp=1718400000.0,
side=TransactionDirection.DIRECTION_LONG | TransactionOffset.OFFSET_OPEN,
volume=1000.0, limit_price=15.25,
order_type=OrderType.ORDER_LIMIT,
order_id="ord_001",
)
instr.set_order_state(OrderState.STATE_PLACED)
instr.fill(report)
instr.cancel_order()
print(instr.filled_volume, instr.average_price)
print(instr.is_working, instr.is_done)
Data Buffers¶
MarketDataBuffer¶
Resizable block buffer for collecting and sorting market data.
from algo_engine.base import MarketDataBuffer
buf = MarketDataBuffer(ptr_cap=128, data_cap=16384)
# Single puts
buf.put(tick)
buf.put(bar)
# Batch writes via cache (reduces reallocations)
with buf.cache() as cache:
for md in many_data:
cache.put(md)
buf.sort() # sort by timestamp
for md in buf: # iterate chronologically
print(md.ticker)
first = buf[0] # indexed access
raw = buf.to_bytes() # serialize
buf2 = MarketDataBuffer.from_bytes(raw)
Properties: ptr_capacity, ptr_tail (= len), data_capacity, data_tail.
MarketDataRingBuffer¶
Fixed-capacity ring buffer with blocking read/write.
from algo_engine.base import MarketDataRingBuffer
ring = MarketDataRingBuffer(ptr_cap=256, data_cap=32768)
ring.put(tick, block=True, timeout=1.0) # may raise BufferFull, PipeTimeoutError
next_md = ring.listen(block=True, timeout=1.0) # may raise BufferEmpty
print(ring.is_empty)
MarketDataConcurrentBuffer¶
Multi-consumer buffer backed by shared memory.
from algo_engine.base import MarketDataConcurrentBuffer
cbuf = MarketDataConcurrentBuffer(n_workers=4, capacity=1024)
cbuf.put(tick, block=True, timeout=1.0)
md = cbuf.listen(worker_id=0, block=True, timeout=1.0)
cbuf.disable_worker(2)
cbuf.enable_worker(2) # resets read pointer to current write position
print(cbuf.is_full, cbuf.is_empty)
Allocator Protocols¶
Context managers control how MarketData allocates its backing buffer:
from algo_engine.base import MD_SHARED, MD_LOCKED, MD_LOCKFREE, MD_FREELIST
with MD_SHARED | MD_LOCKED:
data = TickDataLite(ticker="TEST", timestamp=ts,
last_price=100.0, bid_price=99.0,
bid_volume=10.0, ask_price=101.0, ask_volume=10.0)
MD_SHARED— allocate in shared memory (default: True)MD_LOCKED— thread-safe allocation (default: False)MD_FREELIST— use freelist on deallocation (default: True)MD_LOCKFREE— lock-free allocation
FilterMode provides bitmask-based filtering for market data streams:
from algo_engine.base import FilterMode
f = FilterMode.NO_TICK | FilterMode.NO_CANCEL
f.mask_data(tick) # True if data passes filter
f.freeze() # prevent further mutation