def test_descriptive_stats(): check_values = [] scheduler = HistoricNetworkScheduler(0, 1000) network = scheduler.get_network() values = From(scheduler, [0.0, 3.2, 2.1, 2.9, 8.3, 5.7]) max_value = Max(network, values) min_value = Min(network, values) avg = Mean(network, values) stddev = Stddev(network, values) check_values.extend([max_value, min_value, avg, stddev]) # noinspection PyUnusedLocal def print_stats(params): print(f"min = {min_value.get_value()}; max = {max_value.get_value()}; " f"avg = {avg.get_value():.2f}; stddev = {stddev.get_value():.2f}") Lambda(network, [min_value, max_value, avg, stddev], print_stats) scheduler.run() assert check_values[0].get_value() == 8.3 assert check_values[1].get_value() == 0.0 assert math.isclose(check_values[2].get_value(), 3.7, abs_tol=0.00001) assert math.isclose(check_values[3].get_value(), 3.24507, abs_tol=0.00001)
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 __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 test_running_sum(): check_values = [] scheduler = HistoricNetworkScheduler(0, 1000) network = scheduler.get_network() values = From(scheduler, [0.0, 3.2, 2.1, 2.9, 8.3, 5.7]) total = RunningSum(network, values) check_values.append(total) Lambda(network, total, lambda x: print(f'{x[0].get_value():.2f}')) scheduler.run() assert check_values[0].get_value() == 22.2
def __init__(self, scheduler: HistoricNetworkScheduler, azure_connect_str: str): self.scheduler = scheduler self.azure_connect_str = azure_connect_str self.subscribed_instruments = MutableSignal() self.scheduler.get_network().attach(self.subscribed_instruments) self.start_time = scheduler.get_clock().get_start_time() self.end_time = scheduler.get_clock().get_end_time() self.all_subscribed = set() self.book_signal_by_symbol = {} self.trade_signal_by_symbol = {}
def __init__(self, scheduler: HistoricNetworkScheduler): self.scheduler = scheduler self.subscribed_instruments = MutableSignal() self.scheduler.get_network().attach(self.subscribed_instruments) self.start_time = scheduler.get_clock().get_start_time() self.end_time = scheduler.get_clock().get_end_time() self.all_subscribed = set() self.book_signal_by_symbol = {} self.trade_signal_by_symbol = {} self.credential = DeviceCodeCredential(client_id=get_global_defaults()['azure']['client_id'], tenant_id=get_global_defaults()['azure']['tenant_id'])
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 test_routing_marketdata_service(mocker: MockFixture): scheduler = HistoricNetworkScheduler(0, 1000) mds1 = mocker.MagicMock(MarketdataService) mds2 = mocker.MagicMock(MarketdataService) rules = CompositeRoutingRule([ ExchangeRoutingRule('POLYGON', mds1), ExchangeRoutingRule('PHEMEX', mds2) ]) router = RoutingMarketdataService(scheduler.get_network(), rules) # noinspection DuplicatedCode venue_type = VenueType(-1, 'DataAggregator') exch1 = Exchange(-1, venue_type, 'POLYGON', 'Polygon.io') instr_type1 = InstrumentType(-1, 'ETF') instr1 = Instrument(-1, instr_type1, 'SPY') xinstr1 = ExchangeInstrument(-1, exch1, instr1, 'SPY') venue_type = VenueType(-1, 'CryptoExchange') exch2 = Exchange(-1, venue_type, 'PHEMEX', 'Phemex') instr_type2 = InstrumentType(-1, 'CurrencyPair') instr2 = Instrument(-1, instr_type2, 'BTCUSD') xinstr2 = ExchangeInstrument(-1, exch2, instr2, 'BTCUSD') assert router.get_subscribed_instruments() is not None assert router.get_order_book_events( xinstr1) is mds1.get_order_book_events() assert router.get_order_book_events( xinstr2) is mds2.get_order_book_events() assert router.get_order_books(xinstr1) is mds1.get_order_books() assert router.get_order_books(xinstr2) is mds2.get_order_books() assert router.get_trades(xinstr1) is mds1.get_trades() assert router.get_trades(xinstr2) is mds2.get_trades() scheduler.run()
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 main(config_path: str, strategy_dir: str, start_time: str, end_time: str): init_logging() config = toml.load(config_path) strategy_module = config['strategy']['module'] strategy_class = config['strategy']['class'] loader = StrategyLoader(Path(strategy_dir)) strategy_instance = loader.load(strategy_module, strategy_class) scheduler = HistoricNetworkScheduler.new_instance(start_time, end_time) backtester = InvestmentStrategyBacktester(scheduler) portfolio = backtester.run(strategy_instance, config) scheduler.run() for account in portfolio.get_accounts(): print(f'Account: [{account.get_name()}]') print(f'\tcash: {account.get_cash_balance().get_balance()}') for position in account.get_positions(): print( f'\t{position.get_tradable().get_symbol()}: {position.get_qty()} shares ({position.get_notional()})' )
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()
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()