def test_load_pickled_data(self): # Arrange bar_type = BarType( instrument_id=GBPUSD_SIM.id, bar_spec=TestDataStubs.bar_spec_1min_bid(), aggregation_source=AggregationSource.EXTERNAL, # <-- important ) config = EMACrossConfig( instrument_id=str(GBPUSD_SIM.id), bar_type=str(bar_type), trade_size=Decimal(100_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) data = self.engine.dump_pickled_data() # Act self.engine.load_pickled_data(data) self.engine.run() # Assert assert strategy.fast_ema.count == 30117 assert self.engine.iteration == 60234 assert self.engine.portfolio.account( self.venue).balance_total(USD) == Money(1001736.86, USD)
def test_create_from_source(self): # Arrange config = EMACrossConfig( instrument_id="AUD/USD.SIM", bar_type="AUD/USD.SIM-1000-TICK-MID-INTERNAL", trade_size=1_000_000, ) source = pkgutil.get_data("nautilus_trader.examples.strategies", "ema_cross.py") importable = ImportableStrategyConfig( module="my_ema_cross", source=source, config=config, ) # Act strategy = StrategyFactory.create(importable) # Assert assert isinstance(strategy, EMACross) assert ( repr(config) == "EMACrossConfig(order_id_tag='000', oms_type='HEDGING', instrument_id='AUD/USD.SIM', bar_type='AUD/USD.SIM-1000-TICK-MID-INTERNAL', fast_ema_period=10, slow_ema_period=20, trade_size=Decimal('1000000'))" # noqa )
def test_create_from_path(self): # Arrange config = EMACrossConfig( instrument_id="AUD/USD.SIM", bar_type="AUD/USD.SIM-15-MINUTE-BID-EXTERNAL", trade_size=1_000_000, fast_ema_period=10, slow_ema_period=20, ) importable = ImportableStrategyConfig( path="nautilus_trader.examples.strategies.ema_cross:EMACross", config=config, ) # Act strategy = StrategyFactory.create(importable) # Assert assert isinstance(strategy, EMACross) assert ( repr(config) == "EMACrossConfig(component_id=None, order_id_tag='000', oms_type='HEDGING', " "instrument_id='AUD/USD.SIM', bar_type='AUD/USD.SIM-15-MINUTE-BID-EXTERNAL', " "fast_ema_period=10, slow_ema_period=20, trade_size=Decimal('1000000'))" # noqa )
def setup(): config = BacktestEngineConfig(bypass_logging=True) engine = BacktestEngine(config=config) # Setup data wrangler = QuoteTickDataWrangler(USDJPY_SIM) provider = TestDataProvider() ticks = wrangler.process_bar_data( bid_data=provider.read_csv_bars("fxcm-usdjpy-m1-bid-2013.csv"), ask_data=provider.read_csv_bars("fxcm-usdjpy-m1-ask-2013.csv"), ) engine.add_instrument(USDJPY_SIM) engine.add_ticks(ticks) engine.add_venue( venue=Venue("SIM"), oms_type=OMSType.HEDGING, account_type=AccountType.MARGIN, base_currency=USD, starting_balances=[Money(1_000_000, USD)], ) config = EMACrossConfig( instrument_id=str(USDJPY_SIM.id), bar_type=str(TestDataStubs.bartype_usdjpy_1min_bid()), trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) start = datetime(2013, 2, 1, 0, 0, 0, 0, tzinfo=pytz.utc) end = datetime(2013, 2, 10, 0, 0, 0, 0, tzinfo=pytz.utc) return (engine, start, end, strategy), {}
def test_run_multiple_strategies(self): # Arrange config1 = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type="USD/JPY.SIM-15-MINUTE-BID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, order_id_tag="001", ) strategy1 = EMACross(config=config1) config2 = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type="USD/JPY.SIM-15-MINUTE-BID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=20, slow_ema=40, order_id_tag="002", ) strategy2 = EMACross(config=config2) # Note since these strategies are operating on the same instrument_id as per # the EMACross BUY/SELL logic they will be flattening each others positions. # The purpose of the test is just to ensure multiple strategies can run together. self.engine.add_strategies(strategies=[strategy1, strategy2]) # Act self.engine.run() # Assert assert strategy1.fast_ema.count == 2689 assert strategy2.fast_ema.count == 2689 assert self.engine.iteration == 115044 assert self.engine.portfolio.account( self.venue).balance_total(USD) == Money(985622.52, USD)
def setup(): config = BacktestEngineConfig(bypass_logging=True) engine = BacktestEngine(config=config) # Setup data wrangler = QuoteTickDataWrangler(USDJPY_SIM) provider = TestDataProvider() ticks = wrangler.process_bar_data( bid_data=provider.read_csv_bars("fxcm-usdjpy-m1-bid-2013.csv"), ask_data=provider.read_csv_bars("fxcm-usdjpy-m1-ask-2013.csv"), ) engine.add_instrument(USDJPY_SIM) engine.add_ticks(ticks) interest_rate_data = pd.read_csv( os.path.join(PACKAGE_ROOT, "data", "short-term-interest.csv")) fx_rollover_interest = FXRolloverInterestModule( rate_data=interest_rate_data) engine.add_venue( venue=Venue("SIM"), venue_type=VenueType.BROKERAGE, oms_type=OMSType.HEDGING, account_type=AccountType.MARGIN, base_currency=USD, starting_balances=[Money(1_000_000, USD)], modules=[fx_rollover_interest], ) config = EMACrossConfig( instrument_id=str(USDJPY_SIM.id), bar_type=str(TestStubs.bartype_usdjpy_1min_bid()), trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) start = datetime(2013, 2, 1, 0, 0, 0, 0, tzinfo=pytz.utc) end = datetime(2013, 3, 1, 0, 0, 0, 0, tzinfo=pytz.utc) return (engine, start, end, [strategy]), {}
def test_run_ema_cross_strategy(self): # Arrange config = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type="USD/JPY.SIM-15-MINUTE-BID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert - Should return expected PnL assert strategy.fast_ema.count == 2689 assert self.engine.iteration == 115044 assert self.engine.portfolio.account( self.venue).balance_total(USD) == Money(992811.26, USD)
def test_run_ema_cross_with_tick_bar_spec(self): # Arrange config = EMACrossConfig( instrument_id=str(self.audusd.id), bar_type="AUD/USD.SIM-100-TICK-MID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 1000 assert self.engine.iteration == 100000 assert self.engine.portfolio.account( self.venue).balance_total(AUD) == Money(994441.60, AUD)
def test_run_ema_cross_with_minute_bar_spec(self): # Arrange config = EMACrossConfig( instrument_id="AUD/USD.SIM", bar_type="AUD/USD.SIM-1-MINUTE-MID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 1771 assert self.engine.iteration == 100000 assert self.engine.portfolio.account( self.venue).balance_total(AUD) == Money(987920.04, AUD)
def test_run_ema_cross_with_minute_bar_spec(self): # Arrange config = EMACrossConfig( instrument_id=str(self.gbpusd.id), bar_type="GBP/USD.SIM-5-MINUTE-MID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 8353 assert self.engine.iteration == 120468 assert self.engine.portfolio.account( self.venue).balance_total(GBP) == Money(931346.81, GBP)
def test_exec_cache_check_integrity_when_index_cleared_fails(self): # Arrange config = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type="USD/JPY.SIM-15-MINUTE-BID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Generate a lot of data self.engine.run() # Clear index self.engine.cache.clear_index() # Act, Assert assert not self.engine.cache.check_integrity()
def test_exec_cache_check_integrity_when_cache_cleared_fails(self): # Arrange config = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type=str(TestStubs.bartype_usdjpy_1min_bid()), trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Generate a lot of data self.engine.run() # Remove data self.engine.cache.clear_cache() # Act, Assert assert not self.engine.cache.check_integrity()
def test_run_ema_cross_with_tick_bar_spec(self): # Arrange config = EMACrossConfig( instrument_id=str(self.ethusdt.id), bar_type="ETH/USDT.BINANCE-250-TICK-LAST-INTERNAL", trade_size=Decimal(100), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 279 assert self.engine.iteration == 69806 assert self.engine.portfolio.account( self.venue).balance_total(USDT) == Money(998450.62196820, USDT)
def test_run_ema_cross_with_minute_bar_spec(self): # Arrange config = EMACrossConfig( instrument_id=str(self.gbpusd.id), bar_type="GBP/USD.SIM-1-MINUTE-BID-EXTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 30117 assert self.engine.iteration == 60234 ending_balance = self.engine.portfolio.account( self.venue).balance_total(USD) assert ending_balance == Money(1016188.45, USD)
def setup(self): data_catalog_setup() self.catalog = DataCatalog.from_env() self.venue_config = BacktestVenueConfig( name="SIM", venue_type="ECN", oms_type="HEDGING", account_type="MARGIN", base_currency="USD", starting_balances=["1000000 USD"], # fill_model=fill_model, # TODO(cs): Implement next iteration ) self.data_config = BacktestDataConfig( catalog_path="/root", catalog_fs_protocol="memory", data_cls_path="nautilus_trader.model.data.tick.QuoteTick", instrument_id="AUD/USD.SIM", start_time=1580398089820000000, end_time=1580504394501000000, ) self.backtest_configs = [ BacktestRunConfig( engine=BacktestEngineConfig(), venues=[self.venue_config], data=[self.data_config], ) ] self.strategies = [ ImportableStrategyConfig( path="nautilus_trader.examples.strategies.ema_cross:EMACross", config=EMACrossConfig( instrument_id="AUD/USD.SIM", bar_type="AUD/USD.SIM-100-TICK-MID-INTERNAL", fast_ema_period=10, slow_ema_period=20, trade_size=Decimal(1_000_000), order_id_tag="001", ), ) ]
def test_run_ema_cross_with_trade_ticks_from_bar_data(self): # Arrange wrangler = QuoteTickDataWrangler(instrument=self.btcusdt) provider = TestDataProvider() # Build ticks from bar data ticks = wrangler.process_bar_data( bid_data=provider.read_csv_bars( "ftx-btc-perp-20211231-20220201_1m.csv")[:10000], ask_data=provider.read_csv_bars( "ftx-btc-perp-20211231-20220201_1m.csv")[:10000], ) self.engine.add_ticks(ticks) config = EMACrossConfig( instrument_id=str(self.btcusdt.id), bar_type="BTCUSDT.BINANCE-1-MINUTE-BID-INTERNAL", trade_size=Decimal(0.001), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 10000 assert self.engine.iteration == 40000 btc_ending_balance = self.engine.portfolio.account( self.venue).balance_total(BTC) usdt_ending_balance = self.engine.portfolio.account( self.venue).balance_total(USDT) assert btc_ending_balance == Money(9.57200000, BTC) assert usdt_ending_balance == Money(10017114.27716700, USDT)
def test_run_ema_cross_with_minute_trade_bars(self): # Arrange wrangler = BarDataWrangler( bar_type=BarType.from_str( "BTCUSDT.BINANCE-1-MINUTE-LAST-EXTERNAL"), instrument=self.btcusdt, ) provider = TestDataProvider() # Build externally aggregated bars bars = wrangler.process(data=provider.read_csv_bars( "ftx-btc-perp-20211231-20220201_1m.csv")[:10000], ) self.engine.add_bars(bars) config = EMACrossConfig( instrument_id=str(self.btcusdt.id), bar_type="BTCUSDT.BINANCE-1-MINUTE-LAST-EXTERNAL", trade_size=Decimal(0.001), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Act self.engine.run() # Assert assert strategy.fast_ema.count == 10000 assert self.engine.iteration == 10000 btc_ending_balance = self.engine.portfolio.account( self.venue).balance_total(BTC) usdt_ending_balance = self.engine.portfolio.account( self.venue).balance_total(USDT) assert btc_ending_balance == Money(9.57200000, BTC) assert usdt_ending_balance == Money(10017114.27716700, USDT)
def test_rerunning_backtest_with_redis_db_builds_correct_index(self): # Arrange config = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type=str(TestStubs.bartype_usdjpy_1min_bid()), trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) # Generate a lot of data self.engine.run() # Reset engine self.engine.reset() self.engine.add_instrument(self.usdjpy) # Act self.engine.run() # Assert assert self.engine.cache.check_integrity()
def test_rerun_ema_cross_strategy_returns_identical_performance(self): # Arrange config = EMACrossConfig( instrument_id=str(self.usdjpy.id), bar_type="USD/JPY.SIM-15-MINUTE-BID-INTERNAL", trade_size=Decimal(1_000_000), fast_ema=10, slow_ema=20, ) strategy = EMACross(config=config) self.engine.add_strategy(strategy) self.engine.run() result1 = self.engine.trader.analyzer.get_performance_stats_pnls() # Act self.engine.reset() self.engine.add_instrument( self.usdjpy) # TODO(cs): Having to replace instrument self.engine.run() result2 = self.engine.trader.analyzer.get_performance_stats_pnls() # Assert assert all(result2) == all(result1)
# Add an exchange (multiple exchanges possible) # Add starting balances for single-currency or multi-currency accounts engine.add_venue( venue=SIM, oms_type=OMSType.HEDGING, # Venue will generate position IDs account_type=AccountType.MARGIN, base_currency=USD, # Standard single-currency account starting_balances=[Money(1_000_000, USD)], fill_model=fill_model, modules=[fx_rollover_interest], ) # Configure your strategy config = EMACrossConfig( instrument_id=str(AUDUSD_SIM.id), bar_type="AUD/USD.SIM-100-TICK-MID-INTERNAL", fast_ema_period=10, slow_ema_period=20, trade_size=Decimal(1_000_000), order_id_tag="001", ) # Instantiate and add your strategy strategy = EMACross(config=config) engine.add_strategy(strategy=strategy) input("Press Enter to continue...") # noqa (always Python 3) # Run the engine (from start to end of data) engine.run() # Optionally view reports with pd.option_context(
# Add starting balances for single-currency or multi-currency accounts engine.add_venue( venue=BINANCE, oms_type=OMSType.NETTING, account_type=AccountType.CASH, # Spot cash account base_currency=None, # Multi-currency account starting_balances=[Money(1_000_000, USDT), Money(10, ETH)], fill_model=fill_model, ) # Configure your strategy config = EMACrossConfig( instrument_id=str(ETHUSDT_BINANCE.id), bar_type="ETHUSDT.BINANCE-250-TICK-LAST-INTERNAL", trade_size=Decimal("0.05"), fast_ema=10, slow_ema=20, order_id_tag="001", ) # Instantiate and add your strategy strategy = EMACross(config=config) engine.add_strategy(strategy=strategy) input("Press Enter to continue...") # noqa (always Python 3) # Run the engine (from start to end of data) engine.run() # Optionally view reports with pd.option_context( "display.max_rows",
"FTX": { # "api_key": "YOUR_FTX_API_KEY", # "api_secret": "YOUR_FTX_API_SECRET", # "account_id": "YOUR_FTX_ACCOUNT_ID", (optional) "sandbox_mode": False, # If client uses the testnet, }, }, ) # Instantiate the node with a configuration node = TradingNode(config=config_node) # Configure your strategy strat_config = EMACrossConfig( instrument_id="ETH/USD.FTX", bar_type="ETH/USD.FTX-1-MINUTE-LAST-INTERNAL", fast_ema_period=10, slow_ema_period=20, trade_size=Decimal("0.01"), order_id_tag="001", ) # Instantiate your strategy strategy = EMACross(config=strat_config) # Add your strategies and modules node.trader.add_strategy(strategy) # Register your client factories with the node (can take user defined factories) node.add_data_client_factory("FTX", FTXLiveDataClientFactory) node.add_exec_client_factory("FTX", FTXLiveExecutionClientFactory) node.build() # Stop and dispose of the node with SIGINT/CTRL+C
), }, timeout_connection=5.0, timeout_reconciliation=5.0, timeout_portfolio=5.0, timeout_disconnection=5.0, check_residuals_delay=2.0, ) # Instantiate the node with a configuration node = TradingNode(config=config_node) # Configure your strategy strat_config = EMACrossConfig( instrument_id="ETHUSDT.BINANCE", bar_type="ETHUSDT.BINANCE-1-MINUTE-LAST-EXTERNAL", fast_ema_period=10, slow_ema_period=20, trade_size=Decimal("0.005"), order_id_tag="001", ) # Instantiate your strategy strategy = EMACross(config=strat_config) # Add your strategies and modules node.trader.add_strategy(strategy) # Register your client factories with the node (can take user defined factories) node.add_data_client_factory("BINANCE", BinanceLiveDataClientFactory) node.add_exec_client_factory("BINANCE", BinanceLiveExecClientFactory) node.build()