async def main(): scheduler = RealtimeNetworkScheduler() network = scheduler.get_network() values = Interval(scheduler) Do(network, values, lambda: print(f"input values: {values.get_value()}")) window = WindowWithCount(network, values, count=5) Do(network, window, lambda: print(f"window values: {window.get_value()}"))
def init(self, ctx: StrategyContext): self.ctx = ctx big_print_qty = float(ctx.getenv('BIG_PRINT_QTY')) self.trade_qty = float(ctx.getenv('CONTRACT_TRADE_QTY')) api_key = ctx.getenv('PHEMEX_API_KEY') api_secret = ctx.getenv('PHEMEX_API_SECRET') if not api_key: raise ValueError('missing PHEMEX_API_KEY') if not api_secret: raise ValueError('missing PHEMEX_API_SECRET') credentials = AuthCredentials(api_key, api_secret) exchange_instance = ctx.getenv('PHEMEX_INSTANCE', 'prod') if exchange_instance == 'prod': self.trading_conn = PhemexConnection(credentials) elif exchange_instance == 'test': self.trading_conn = PhemexConnection( credentials, api_url='https://testnet-api.phemex.com') else: raise ValueError( f'Unknown PHEMEX_INSTANCE value: {exchange_instance}') self.logger.info(f'Connected to Phemex {exchange_instance} instance') self.spot_feed = ctx.fh_registry.get_feed( f'coinbasepro:{exchange_instance}:BTC-USD') self.futures_feed = ctx.fh_registry.get_feed( f'phemex:{exchange_instance}:BTCUSD') self.logger.info( f'Connected to spot & futures {exchange_instance} feeds') network = self.ctx.get_network() # scan the spot market for large trades spot_trades = self.spot_feed.get_trades() Do( network, spot_trades, lambda: self.logger.info( f'Spot market trade: {spot_trades.get_value()}')) self.big_prints = Filter(network, spot_trades, lambda x: x.get_qty() >= big_print_qty) # compute 5 minute bins for the futures market and extract the volume field self.futures_trades = self.futures_feed.get_trades() buffer_5min = BufferWithTime(network, self.futures_trades, timedelta(minutes=5)) self.ohlc_5min = ComputeOHLC(network, buffer_5min) Do( network, self.ohlc_5min, lambda: self.logger.info( f'OHLC[5min]: {self.ohlc_5min.get_value()}')) self.volume = Map(network, self.ohlc_5min, lambda x: x.volume) # track the exponentially weighted moving average of the futures volume self.ewma = ExponentialMovingAverage(network, self.volume)
def __init__(self, config: BacktestConfig): logger = logging.getLogger(__name__) logger.info('Serenity backtester starting up') sys.path.append(str(config.get_strategy_basedir())) bt_env = config.get_env() exchange_id = bt_env.getenv('EXCHANGE_ID', 'autofill') instance_id = bt_env.getenv('EXCHANGE_INSTANCE', 'prod') account = bt_env.getenv('EXCHANGE_ACCOUNT', 'Main') self.logger.info('Connecting to Serenity database') conn = connect_serenity_db() conn.autocommit = True cur = conn.cursor() self.scheduler = HistoricNetworkScheduler( config.get_start_time_millis(), config.get_end_time_millis()) instrument_cache = InstrumentCache(cur, TypeCodeCache(cur)) oms = OrderManagerService(self.scheduler) md_service = AzureHistoricMarketdataService( self.scheduler, bt_env.getenv('AZURE_CONNECT_STR')) mark_service = MarketdataMarkService(self.scheduler.get_network(), md_service) op_service = OrderPlacerService(self.scheduler, oms) op_service.register_order_placer( f'{exchange_id}:{instance_id}', AutoFillOrderPlacer(self.scheduler, oms, md_service, account)) xps = NullExchangePositionService(self.scheduler) extra_outputs_txt = bt_env.getenv('EXTRA_OUTPUTS') if extra_outputs_txt is None: extra_outputs = [] else: extra_outputs = extra_outputs_txt.split(',') self.dcs = HDF5DataCaptureService(Mode.BACKTEST, self.scheduler, extra_outputs) # wire up orders and fills from OMS Do(self.scheduler.get_network(), oms.get_orders(), lambda: self.dcs.capture_order(oms.get_orders().get_value())) Do(self.scheduler.get_network(), oms.get_order_events(), lambda: self.dcs.capture_fill(oms.get_order_events().get_value())) self.strategy_name = config.get_strategy_name() strategy_env = config.get_strategy_env() ctx = StrategyContext(self.scheduler, instrument_cache, md_service, mark_service, op_service, PositionService(self.scheduler, oms), xps, self.dcs, strategy_env.values) strategy_instance = config.get_strategy_instance() strategy_instance.init(ctx) strategy_instance.start()
def init(self, ctx: StrategyContext): self.ctx = ctx account = ctx.getenv('EXCHANGE_ACCOUNT') if account is None: raise ValueError('Missing EXCHANGE_ACCOUNT') exchange_code, peg_symbol = ctx.getenv('PEG_INSTRUMENT').split(':') self.peg_instrument = ctx.get_instrument_cache( ).get_crypto_exchange_instrument(exchange_code, peg_symbol) peg_type = ctx.getenv('PEG_TYPE', 'Near') if peg_type == 'Near': self.peg_type = PegType.NEAR peg_side = ctx.getenv('PEG_SIDE') if peg_side == 'Buy': self.peg_side = Side.BUY elif peg_side == 'Sell': self.peg_side = Side.SELL else: raise ValueError(f'Unknown or missing PegSide: {peg_side}') self.peg_qty = ctx.getenv('PEG_QTY', 1) exchange_id = ctx.getenv('EXCHANGE_ID', 'phemex') exchange_instance = ctx.getenv('EXCHANGE_INSTANCE', 'prod') op_uri = f'{exchange_id}:{exchange_instance}' self.order_placer = ctx.get_order_placer_service().get_order_placer( op_uri) self.logger.info(f'Connected to Phemex {exchange_instance} instance') # subscribe to top of book self.peg_book = self.ctx.get_marketdata_service().get_order_books( self.peg_instrument) # subscribe to position updates and exchange position updates position = self.ctx.get_position_service().get_position( account, self.peg_instrument) Do(ctx.get_scheduler().get_network(), position, lambda: self.logger.info(position.get_value())) exch_position = self.ctx.get_exchange_position_service( ).get_exchange_positions() Do(ctx.get_scheduler().get_network(), exch_position, lambda: self.logger.info(exch_position.get_value())) # subscribe to order events from the OMS, and log them order_events = self.ctx.get_order_placer_service( ).get_order_manager_service().get_order_events() Do(ctx.get_scheduler().get_network(), order_events, lambda: self.logger.info(order_events.get_value()))
async def main(): scheduler = RealtimeNetworkScheduler() network = scheduler.get_network() values = Interval(scheduler) Do(network, values, lambda: print(f"input values: {values.get_value()}")) buffer1 = BufferWithCount(network, values, count=2) Do(network, buffer1, lambda: print(f"buffer1 values: {buffer1.get_value()}")) buffer2 = BufferWithTime(scheduler, values, timedelta(seconds=5)) Do(network, buffer2, lambda: print(f"buffer2 values: {buffer2.get_value()}"))
def init(self, ctx: StrategyContext): self.ctx = ctx big_print_qty = float(ctx.getenv('BIG_PRINT_QTY')) self.trade_qty = float(ctx.getenv('CONTRACT_TRADE_QTY')) exchange_instance = ctx.getenv('EXCHANGE_INSTANCE', 'prod') op_uri = f'phemex:{exchange_instance}' self.order_placer = ctx.get_order_placer_service().get_order_placer( op_uri) self.logger.info(f'Connected to Phemex {exchange_instance} instance') network = self.ctx.get_network() # scan the spot market for large trades btc_usd_spot = self.ctx.get_instrument_cache( ).get_crypto_exchange_instrument('CoinbasePro', 'BTC-USD') spot_trades = self.ctx.get_marketdata_service().get_trades( btc_usd_spot) Do( network, spot_trades, lambda: self.logger.info( f'Spot market trade: {spot_trades.get_value()}')) self.big_prints = Filter(network, spot_trades, lambda x: x.get_qty() >= big_print_qty) # compute 5 minute bins for the futures market and extract the volume field btc_usd_future = self.ctx.get_instrument_cache( ).get_crypto_exchange_instrument('Phemex', 'BTCUSD') self.futures_trades = self.ctx.get_marketdata_service().get_trades( btc_usd_future) buffer_5min = BufferWithTime(self.ctx.get_scheduler(), self.futures_trades, timedelta(minutes=5)) self.ohlc_5min = ComputeOHLC(network, buffer_5min) Do( network, self.ohlc_5min, lambda: self.logger.info( f'OHLC[5min]: {self.ohlc_5min.get_value()}')) self.volume = Map(network, self.ohlc_5min, lambda x: x.volume) # subscribe to top of book self.futures_book = self.ctx.get_marketdata_service().get_order_books( btc_usd_future) Do( network, self.futures_book, lambda: self.logger.info( f'Futures bid/ask: ' f'{self.futures_book.get_value().get_best_bid()} / ' f'{self.futures_book.get_value().get_best_ask()}')) # track the exponentially weighted moving average of the futures volume self.ewma = ExponentialMovingAverage(network, self.volume)
async def main(): scheduler = RealtimeNetworkScheduler() network = scheduler.get_network() values = From(scheduler, [0.0, 3.2, 2.1, 2.9, 8.3, 5.7]) mapper = Map(network, values, lambda x: round(x)) accumulator = Scan(network, mapper) Do(network, accumulator, lambda: print(f"{accumulator.get_value()}"))
def test_filter(): scheduler = HistoricNetworkScheduler(0, 30 * 1000) network = scheduler.get_network() values = From(scheduler, [0.0, -3.2, 2.1, -2.9, 8.3, -5.7]) filt = Filter(network, values, lambda x: x >= 0.0) Do(network, filt, lambda: print(f'{filt.get_value()}')) scheduler.run() assert filt.get_value() == 8.3
def test_map_reduce(): scheduler = HistoricNetworkScheduler(0, 30 * 1000) network = scheduler.get_network() values = From(scheduler, [0.0, 3.2, 2.1, 2.9, 8.3, 5.7]) mapper = Map(network, values, lambda x: round(x)) accumulator = Scan(network, mapper) Do(network, accumulator, lambda: print(f'{accumulator.get_value()}')) scheduler.run() assert accumulator.get_value() == 22.0
def on_activate(self) -> bool: if fh.get_state().get_value() == FeedHandlerState.LIVE: feed = registry.get_feed(f'{uri_scheme}:{instance_id}:{self.trade_symbol}') instrument_code = feed.get_instrument().get_exchange_instrument_code() journal = Journal(Path(f'{journal_path}/{db}_BOOKS/{instrument_code}')) self.appender = journal.create_appender() books = feed.get_order_books() Do(scheduler.get_network(), books, lambda: self.on_book_update(books.get_value())) return False
def test_repeating_timer(): # run for one minute scheduler = HistoricNetworkScheduler(0, 60 * 1000) tz = pytz.timezone('US/Eastern') timer = RepeatingTimer(scheduler, datetime.timedelta(seconds=15)) timestamps = list() Do(scheduler.get_network(), timer, lambda: timestamps.append(scheduler.get_clock().get_time(tz))) scheduler.run() assert len(timestamps) == 4 assert str(timestamps[3]) == '1969-12-31 19:01:00-05:00'
def test_alarm(): # run for one day scheduler = HistoricNetworkScheduler(0, 60 * 60 * 24 * 1000) # schedule at 4pm US/Eastern tz = pytz.timezone('US/Eastern') alarm = Alarm(scheduler, datetime.time(16, 00, 00), tz) timestamps = list() Do(scheduler.get_network(), alarm, lambda: timestamps.append(scheduler.get_clock().get_time(tz))) scheduler.run() assert len(timestamps) == 1
def on_activate(self) -> bool: if fh.get_state().get_value() == FeedHandlerState.LIVE: feed = registry.get_feed( f'{uri_scheme}:{instance_id}:{self.trade_symbol}') instrument_code = feed.get_instrument( ).get_exchange_instrument_code() txlog = TransactionLog( Path(f'{journal_path}/{db}_TRADES/{instrument_code}')) self.tx_writer = txlog.create_writer() trades = feed.get_trades() Do(scheduler.get_network(), trades, lambda: self.on_trade_print(trades.get_value())) return False
def test_buffer_with_time_historic(): scheduler = HistoricNetworkScheduler(0, 30 * 1000) network = scheduler.get_network() values = MutableSignal() scheduler.schedule_update_at(values, 1.0, 1000) scheduler.schedule_update_at(values, -3.2, 2000) scheduler.schedule_update_at(values, 2.1, 10000) scheduler.schedule_update_at(values, -2.9, 15000) scheduler.schedule_update_at(values, 8.3, 25000) scheduler.schedule_update_at(values, -5.7, 30000) buffer = BufferWithTime(scheduler, values, timedelta(seconds=5)) Do(network, buffer, lambda: print(f'{buffer.get_value()}')) scheduler.run()
def test_oms_cancel_not_pending(mocker: MockFixture): scheduler = HistoricNetworkScheduler(0, 1000) oms = OrderManagerService(scheduler) order_events = [] Do(scheduler.get_network(), oms.get_order_events(), lambda: order_events.append(oms.get_order_events().get_value())) instrument = mocker.MagicMock(ExchangeInstrument) order = MarketOrder(10, instrument, Side.BUY, 'Main') order.set_order_id(str(uuid1())) oms.pending_new(order) oms.new(order, str(uuid1())) oms.apply_cancel(order, str(uuid1())) scheduler.run() assert (isinstance(order_events[2], CancelReject)) assert (order_events[2].get_message() == 'Attempt to apply cancel when not pending')
def test_oms_late_fill(mocker: MockFixture): scheduler = HistoricNetworkScheduler(0, 1000) oms = OrderManagerService(scheduler) order_events = [] Do(scheduler.get_network(), oms.get_order_events(), lambda: order_events.append(oms.get_order_events().get_value())) instrument = mocker.MagicMock(ExchangeInstrument) order = MarketOrder(10, instrument, Side.BUY, 'Main') order.set_order_id(str(uuid1())) oms.pending_new(order) oms.new(order, str(uuid1())) oms.pending_cancel(order) oms.apply_cancel(order, str(uuid1())) oms.apply_fill(order, 1, 10_000, str(uuid1())) scheduler.run() assert (isinstance(order_events[4], Reject)) assert (order_events[4].get_message() == 'Order canceled')
def test_oms_regular_sequence(mocker: MockFixture): scheduler = HistoricNetworkScheduler(0, 1000) oms = OrderManagerService(scheduler) order_statuses = [] Do( scheduler.get_network(), oms.get_order_events(), lambda: order_statuses .append(oms.get_order_events().get_value().get_order_status())) instrument = mocker.MagicMock(ExchangeInstrument) order = MarketOrder(10, instrument, Side.BUY, 'Main') order.set_order_id(str(uuid1())) oms.pending_new(order) oms.new(order, str(uuid1())) oms.apply_fill(order, 1, 10_000, str(uuid1())) oms.pending_cancel(order) oms.apply_cancel(order, str(uuid1())) scheduler.run() assert (order_statuses == [ OrderStatus.PENDING_NEW, OrderStatus.NEW, OrderStatus.PARTIALLY_FILLED, OrderStatus.PENDING_CANCEL, OrderStatus.CANCELED ])
def init(self, ctx: StrategyContext): scheduler = ctx.get_scheduler() network = scheduler.get_network() contract_qty = int(ctx.getenv('BBANDS_QTY', 1)) window = int(ctx.getenv('BBANDS_WINDOW')) num_std = int(ctx.getenv('BBANDS_NUM_STD')) stop_std = int(ctx.getenv('BBANDS_STOP_STD')) bin_minutes = int(ctx.getenv('BBANDS_BIN_MINUTES', 5)) cooling_period_seconds = int(ctx.getenv('BBANDS_COOL_SECONDS', 15)) exchange_code, instrument_code = ctx.getenv( 'TRADING_INSTRUMENT').split(':') instrument = ctx.get_instrument_cache().get_crypto_exchange_instrument( exchange_code, instrument_code) trades = ctx.get_marketdata_service().get_trades(instrument) trades_5m = BufferWithTime(scheduler, trades, timedelta(minutes=bin_minutes)) prices = ComputeOHLC(network, trades_5m) close_prices = Map(network, prices, lambda x: x.close_px) bbands = ComputeBollingerBands(network, close_prices, window, num_std) op_service = ctx.get_order_placer_service() oms = op_service.get_order_manager_service() dcs = ctx.get_data_capture_service() exchange_id = ctx.getenv('EXCHANGE_ID', 'phemex') exchange_instance = ctx.getenv('EXCHANGE_INSTANCE', 'prod') account = ctx.getenv('EXCHANGE_ACCOUNT') op_uri = f'{exchange_id}:{exchange_instance}' # subscribe to marks, position updates and exchange position updates marks = ctx.get_mark_service().get_marks(instrument) Do(scheduler.get_network(), marks, lambda: self.logger.debug(marks.get_value())) position = ctx.get_position_service().get_position(account, instrument) Do(scheduler.get_network(), position, lambda: self.logger.info(position.get_value())) exch_position = ctx.get_exchange_position_service( ).get_exchange_positions() Do(scheduler.get_network(), exch_position, lambda: self.logger.info(exch_position.get_value())) # capture position and Bollinger Band data Do( scheduler.get_network(), position, lambda: dcs.capture( 'Position', { 'time': pd.to_datetime(scheduler.get_time(), unit='ms'), 'position': position.get_value().get_qty() })) Do( scheduler.get_network(), bbands, lambda: dcs.capture( 'BollingerBands', { 'time': pd.to_datetime(scheduler.get_time(), unit='ms'), 'sma': bbands.get_value().sma, 'upper': bbands.get_value().upper, 'lower': bbands.get_value().lower })) # debug log basic marketdata Do(scheduler.get_network(), prices, lambda: self.logger.debug(prices.get_value())) class TraderState(Enum): GOING_LONG = auto() LONG = auto() FLATTENING = auto() FLAT = auto() # order placement logic class BollingerTrader(Event): # noinspection PyShadowingNames def __init__(self, scheduler: NetworkScheduler, op_service: OrderPlacerService, strategy: BollingerBandsStrategy1): self.scheduler = scheduler self.op = op_service.get_order_placer(f'{op_uri}') self.strategy = strategy self.last_entry = 0 self.last_exit = 0 self.cum_pnl = 0 self.stop = None self.trader_state = TraderState.FLAT self.last_trade_time = 0 self.scheduler.get_network().connect(oms.get_order_events(), self) self.scheduler.get_network().connect(position, self) def on_activate(self) -> bool: if self.scheduler.get_network().has_activated( oms.get_order_events()): order_event = oms.get_order_events().get_value() if isinstance(order_event, ExecutionReport) and order_event.is_fill(): order_type = 'stop order' if self.stop is not None and order_event.get_order_id() == \ self.stop.order_id else 'market order' self.strategy.logger.info( f'Received fill event for {order_type}: {order_event}' ) if self.trader_state == TraderState.GOING_LONG: self.last_entry = order_event.get_last_px() if order_event.get_order_status( ) == OrderStatus.FILLED: self.strategy.logger.info( f'Entered long position: entry price={self.last_entry}' ) self.trader_state = TraderState.LONG elif self.trader_state in (TraderState.FLATTENING, TraderState.LONG) and \ order_event.get_order_status() == OrderStatus.FILLED: if order_type == 'stop order': self.strategy.logger.info( f'stop loss filled at {order_event.get_last_px()}' ) self.stop = None trade_pnl = (order_event.get_last_px() - self.last_entry) * \ (contract_qty / self.last_entry) self.cum_pnl += trade_pnl self.strategy.logger.info( f'Trade P&L={trade_pnl}; cumulative P&L={self.cum_pnl}' ) dcs.capture( 'PnL', { 'time': pd.to_datetime(scheduler.get_time(), unit='ms'), 'trade_pnl': trade_pnl, 'cum_pnl': self.cum_pnl }) self.trader_state = TraderState.FLAT elif isinstance(order_event, Reject): self.strategy.logger.error( f'Order rejected: {order_event.get_message()}') self.trader_state = TraderState.FLAT elif self.trader_state == TraderState.FLAT and close_prices.get_value( ) < bbands.get_value().lower: if self.last_trade_time != 0 and (scheduler.get_time() - self.last_trade_time) < \ cooling_period_seconds * 1000: self.strategy.logger.info( 'Cooling off -- not trading again on rapidly repeated signal' ) return False self.strategy.logger.info( f'Close below lower Bollinger band, enter long position ' f'at {scheduler.get_clock().get_time()}') stop_px = close_prices.get_value() - ( (bbands.get_value().sma - bbands.get_value().lower) * (stop_std / num_std)) self.strategy.logger.info( f'Submitting orders: last_px = {close_prices.get_value()}, ' f'stop_px = {stop_px}') order = self.op.get_order_factory().create_market_order( Side.BUY, contract_qty, instrument) self.stop = self.op.get_order_factory().create_stop_order( Side.SELL, contract_qty, stop_px, instrument) self.op.submit(order) self.op.submit(self.stop) self.last_trade_time = scheduler.get_time() self.trader_state = TraderState.GOING_LONG elif self.trader_state == TraderState.LONG and close_prices.get_value( ) > bbands.get_value().upper: self.strategy.logger.info( f'Close above upper Bollinger band, exiting long position at ' f'{scheduler.get_clock().get_time()}') order = self.op.get_order_factory().create_market_order( Side.SELL, contract_qty, instrument) self.op.submit(order) if self.stop is not None: self.op.cancel(self.stop) self.stop = None self.trader_state = TraderState.FLATTENING return False network.connect(bbands, BollingerTrader(scheduler, op_service, self))
async def main(): scheduler = RealtimeNetworkScheduler() signal = From(scheduler, ["world"]) Do(scheduler.get_network(), signal, lambda: print(f"Hello, {signal.get_value()}!"))
def test_hello_world(): scheduler = HistoricNetworkScheduler(0, 30 * 1000) signal = From(scheduler, ['world']) Do(scheduler.get_network(), signal, lambda: print(f'Hello, {signal.get_value()}!')) scheduler.run()
async def main(): scheduler = RealtimeNetworkScheduler() network = scheduler.get_network() values = Interval(scheduler) accumulator = Scan(network, values) Do(network, accumulator, lambda: print(f"{accumulator.get_value()}"))
async def main(): scheduler = RealtimeNetworkScheduler() network = scheduler.get_network() values = From(scheduler, [0.0, -3.2, 2.1, -2.9, 8.3, -5.7]) filt = Filter(network, values, lambda x: x >= 0.0) Do(network, filt, lambda: print(f"{filt.get_value()}"))
def init(self, ctx: StrategyContext): self.ctx = ctx big_print_qty = float(ctx.getenv('BIG_PRINT_QTY')) self.trade_qty = float(ctx.getenv('CONTRACT_TRADE_QTY')) api_key = ctx.getenv('PHEMEX_API_KEY') api_secret = ctx.getenv('PHEMEX_API_SECRET') if not api_key: raise ValueError('missing PHEMEX_API_KEY') if not api_secret: raise ValueError('missing PHEMEX_API_SECRET') credentials = AuthCredentials(api_key, api_secret) exchange_instance = ctx.getenv('PHEMEX_INSTANCE', 'prod') if exchange_instance == 'prod': self.trading_conn = PhemexConnection(credentials) elif exchange_instance == 'test': self.trading_conn = PhemexConnection( credentials, api_url='https://testnet-api.phemex.com') else: raise ValueError( f'Unknown PHEMEX_INSTANCE value: {exchange_instance}') self.logger.info(f'Connected to Phemex {exchange_instance} instance') network = self.ctx.get_network() # subscribe to AOP messages auth = WebsocketAuthenticator(api_key, api_secret) aop_sub = AccountOrderPositionSubscriber(auth, ctx.get_scheduler(), exchange_instance) aop_sub.start() # scan the spot market for large trades btc_usd_spot = self.ctx.get_instrument_cache().get_exchange_instrument( 'CoinbasePro', 'BTC-USD') spot_trades = self.ctx.get_marketdata_service().get_trades( btc_usd_spot) Do( network, spot_trades, lambda: self.logger.info( f'Spot market trade: {spot_trades.get_value()}')) self.big_prints = Filter(network, spot_trades, lambda x: x.get_qty() >= big_print_qty) # compute 5 minute bins for the futures market and extract the volume field btc_usd_future = self.ctx.get_instrument_cache( ).get_exchange_instrument('Phemex', 'BTCUSD') self.futures_trades = self.ctx.get_marketdata_service().get_trades( btc_usd_future) buffer_5min = BufferWithTime(network, self.futures_trades, timedelta(minutes=5)) self.ohlc_5min = ComputeOHLC(network, buffer_5min) Do( network, self.ohlc_5min, lambda: self.logger.info( f'OHLC[5min]: {self.ohlc_5min.get_value()}')) self.volume = Map(network, self.ohlc_5min, lambda x: x.volume) # subscribe to top of book self.futures_book = self.ctx.get_marketdata_service().get_order_books( btc_usd_future) Do( network, self.futures_book, lambda: self.logger.info( f'Futures bid/ask: ' f'{self.futures_book.get_value().get_best_bid()} / ' f'{self.futures_book.get_value().get_best_ask()}')) # track the exponentially weighted moving average of the futures volume self.ewma = ExponentialMovingAverage(network, self.volume)