def _handle_market_trades(runner, instrument, timestamp_ns): trade_ticks = [] for price, volume in runner.get("trd", []): if volume == 0: continue # Betfair doesn't publish trade ids, so we make our own # TODO - should we use clk here for ID instead of the hash? trade_id = hash_json(data=( timestamp_ns, instrument.market_id, str(runner["id"]), str(runner.get("hc", "0.0")), price, volume, )) tick = TradeTick( instrument_id=instrument.id, price=Price(price_to_probability(price, force=True)), size=Quantity(volume, precision=4), side=OrderSide.BUY, match_id=TradeMatchId(trade_id), timestamp_ns=timestamp_ns, ) trade_ticks.append(tick) return trade_ticks
def _handle_market_trades( runner, instrument, ts_event_ns, ts_recv_ns, ): trade_ticks = [] for price, volume in runner.get("trd", []): if volume == 0: continue # Betfair doesn't publish trade ids, so we make our own # TODO - should we use clk here for ID instead of the hash? trade_id = hash_json(data=(ts_event_ns, price, volume)) tick = TradeTick( instrument_id=instrument.id, price=price_to_probability( price, force=True), # Already wrapping in Price size=Quantity(volume, precision=4), aggressor_side=AggressorSide.UNKNOWN, match_id=trade_id, ts_event_ns=ts_event_ns, ts_recv_ns=ts_recv_ns, ) trade_ticks.append(tick) return trade_ticks
def _handle_market_snapshot(selection, instrument, timestamp_ns): updates = [] # Check we only have one of [best bets / depth bets / all bets] bid_keys = [k for k in B_BID_KINDS if k in selection] or ["atb"] ask_keys = [k for k in B_ASK_KINDS if k in selection] or ["atl"] assert len(bid_keys) <= 1 assert len(ask_keys) <= 1 # OrderBook Snapshot # TODO Clean this crap up if bid_keys[0] == "atb": bids = selection.get("atb", []) else: bids = [(p, v) for _, p, v in selection.get(bid_keys[0], [])] if ask_keys[0] == "atl": asks = selection.get("atl", []) else: asks = [(p, v) for _, p, v in selection.get(ask_keys[0], [])] snapshot = OrderBookSnapshot( level=OrderBookLevel.L2, instrument_id=instrument.id, bids=[(price_to_probability(p, OrderSide.BUY), v) for p, v in asks], asks=[(price_to_probability(p, OrderSide.SELL), v) for p, v in bids], timestamp_ns=timestamp_ns, ) updates.append(snapshot) # Trade Ticks for price, volume in selection.get("trd", []): trade_id = hash_json((timestamp_ns, price, volume)) tick = TradeTick( instrument_id=instrument.id, price=Price(price_to_probability(price, force=True)), size=Quantity(volume, precision=4), side=OrderSide.BUY, match_id=TradeMatchId(trade_id), timestamp_ns=timestamp_ns, ) updates.append(tick) return updates
def build_market_update_messages( # noqa TODO: cyclomatic complexity 14 raw, instrument_provider: BetfairInstrumentProvider ) -> List[Union[OrderBookOperation, TradeTick]]: updates = [] for market in raw.get("mc", []): market_id = market["id"] for runner in market.get("rc", []): kw = dict( market_id=market_id, selection_id=str(runner["id"]), handicap=str(runner.get("hc") or "0.0"), ) instrument = instrument_provider.get_betting_instrument(**kw) if not instrument: continue operations = [] for side in B_SIDE_KINDS: for upd in runner.get(side, []): # TODO - Fix this crap if len(upd) == 3: _, price, volume = upd else: price, volume = upd operations.append( OrderBookOperation( op_type=OrderBookOperationType.DELETE if volume == 0 else OrderBookOperationType.UPDATE, order=Order( price=price_to_probability( price, side=B2N_MARKET_STREAM_SIDE[side]), volume=volume, side=B2N_MARKET_STREAM_SIDE[side], ), timestamp_ns=millis_to_nanos(raw["pt"]), )) ob_update = OrderBookOperations( level=OrderBookLevel.L2, instrument_id=instrument.id, ops=operations, timestamp_ns=millis_to_nanos(raw["pt"]), ) updates.append(ob_update) for price, volume in runner.get("trd", []): # Betfair doesn't publish trade ids, so we make our own # TODO - should we use clk here? trade_id = hash_json(data=( raw["pt"], market_id, str(runner["id"]), str(runner.get("hc", "0.0")), price, volume, )) trade_tick = TradeTick( instrument_id=instrument.id, price=Price(price_to_probability(price)), size=Quantity(volume, precision=4), side=OrderSide.BUY, match_id=TradeMatchId(trade_id), timestamp_ns=millis_to_nanos(raw["pt"]), ) updates.append(trade_tick) if market.get("marketDefinition", {}).get("status") == "CLOSED": for runner in market["marketDefinition"]["runners"]: kw = dict( market_id=market_id, selection_id=str(runner["id"]), handicap=str(runner.get("hc") or "0.0"), ) instrument = instrument_provider.get_betting_instrument(**kw) assert instrument # TODO - handle market closed # on_market_status() if runner["status"] == "LOSER": # TODO - handle closing valuation = 0 pass elif runner["status"] == "WINNER": # TODO handle closing valuation = 1 pass if (market.get("marketDefinition", {}).get("inPlay") and not market.get("marketDefinition", {}).get("status") == "CLOSED"): for selection in market["marketDefinition"]["runners"]: kw = dict( market_id=market_id, selection_id=str(selection["id"]), handicap=str( selection.get("hc", selection.get("handicap")) or "0.0"), ) instrument = instrument_provider.get_betting_instrument(**kw) assert instrument # TODO - handle instrument status IN_PLAY return updates