def ethusd_ftx() -> CurrencySpot: """ Return the FTX ETH/USD instrument for backtesting. Returns ------- CurrencySpot """ return CurrencySpot( instrument_id=InstrumentId( symbol=Symbol("ETH/USD"), venue=Venue("FTX"), ), local_symbol=Symbol("ETHUSD"), base_currency=ETH, quote_currency=USD, price_precision=1, size_precision=3, price_increment=Price(1e-01, precision=1), size_increment=Quantity(1e-03, precision=3), lot_size=None, max_quantity=Quantity(9000, precision=3), min_quantity=Quantity(1e-05, precision=3), max_notional=None, min_notional=Money(10.00, USD), max_price=None, min_price=Price(0.1, precision=1), margin_init=Decimal("0.9"), margin_maint=Decimal("0.9"), maker_fee=Decimal("0.0002"), taker_fee=Decimal("0.0007"), ts_event=0, ts_init=0, )
async def test_load_all_async_for_futures_markets( self, binance_http_client, live_logger, monkeypatch, ): # Arrange: prepare data for monkey patch # response1 = pkgutil.get_data( # package="tests.integration_tests.adapters.binance.resources.http_responses", # resource="http_wallet_trading_fee.json", # ) response2 = pkgutil.get_data( package= "tests.integration_tests.adapters.binance.resources.http_responses", resource="http_futures_market_exchange_info.json", ) responses = [response2] # Mock coroutine for patch async def mock_send_request( self, # noqa (needed for mock) http_method: str, # noqa (needed for mock) url_path: str, # noqa (needed for mock) payload: Dict[str, str], # noqa (needed for mock) ) -> bytes: return orjson.loads(responses.pop()) # Apply mock coroutine to client monkeypatch.setattr( target=BinanceHttpClient, name="send_request", value=mock_send_request, ) self.provider = BinanceFuturesInstrumentProvider( client=binance_http_client, logger=live_logger, account_type=BinanceAccountType.FUTURES_USDT, ) # Act await self.provider.load_all_async() # Assert assert self.provider.count == 3 assert (self.provider.find( InstrumentId(Symbol("BTCUSDT-PERP"), Venue("BINANCE"))) is not None) assert (self.provider.find( InstrumentId(Symbol("ETHUSDT-PERP"), Venue("BINANCE"))) is not None) assert (self.provider.find( InstrumentId(Symbol("BTCUSDT_220325"), Venue("BINANCE"))) is not None) assert len(self.provider.currencies()) == 3 assert "BTC" in self.provider.currencies() assert "ETH" in self.provider.currencies() assert "USDT" in self.provider.currencies()
def btcusdt_binance() -> CurrencySpot: """ Return the Binance BTC/USDT instrument for backtesting. Returns ------- CurrencySpot """ return CurrencySpot( instrument_id=InstrumentId( symbol=Symbol("BTC/USDT"), venue=Venue("BINANCE"), ), local_symbol=Symbol("BTCUSDT"), base_currency=BTC, quote_currency=USDT, price_precision=2, size_precision=6, price_increment=Price(1e-02, precision=2), size_increment=Quantity(1e-06, precision=6), lot_size=None, max_quantity=Quantity(9000, precision=6), min_quantity=Quantity(1e-06, precision=6), max_notional=None, min_notional=Money(10.00000000, USDT), max_price=Price(1000000, precision=2), min_price=Price(0.01, precision=2), margin_init=Decimal(0), margin_maint=Decimal(0), maker_fee=Decimal("0.001"), taker_fee=Decimal("0.001"), ts_event=0, ts_init=0, )
def parse_future_contract( details: ContractDetails, ) -> Future: price_precision: int = _tick_size_to_precision(details.minTick) timestamp = time.time_ns() instrument_id = InstrumentId( symbol=Symbol(details.contract.localSymbol), venue=Venue(details.contract.primaryExchange or details.contract.exchange), ) return Future( instrument_id=instrument_id, native_symbol=Symbol(details.contract.localSymbol), asset_class=sec_type_to_asset_class(details.underSecType), currency=Currency.from_str(details.contract.currency), price_precision=price_precision, price_increment=Price(details.minTick, price_precision), multiplier=Quantity.from_int(int(details.contract.multiplier)), lot_size=Quantity.from_int(1), underlying=details.underSymbol, expiry_date=datetime.datetime.strptime( details.contract.lastTradeDateOrContractMonth, "%Y%m%d" ).date(), ts_event=timestamp, ts_init=timestamp, )
def parse_forex_contract( details: ContractDetails, ) -> CurrencyPair: price_precision: int = _tick_size_to_precision(details.minTick) timestamp = time.time_ns() instrument_id = InstrumentId( symbol=Symbol(f"{details.contract.symbol}/{details.contract.currency}"), venue=Venue(details.contract.primaryExchange or details.contract.exchange), ) return CurrencyPair( instrument_id=instrument_id, native_symbol=Symbol(details.contract.localSymbol), base_currency=Currency.from_str(details.contract.currency), quote_currency=Currency.from_str(details.contract.symbol), price_precision=price_precision, size_precision=Quantity.from_int(1), price_increment=Price(details.minTick, price_precision), size_increment=Quantity(details.sizeMinTick or 1, 1), lot_size=None, max_quantity=None, min_quantity=None, max_notional=None, min_notional=None, max_price=None, min_price=None, margin_init=Decimal(0), margin_maint=Decimal(0), maker_fee=Decimal(0), taker_fee=Decimal(0), ts_event=timestamp, ts_init=timestamp, )
def parse_option_contract( details: ContractDetails, ) -> Option: price_precision: int = _tick_size_to_precision(details.minTick) timestamp = time.time_ns() instrument_id = InstrumentId( symbol=Symbol(details.contract.localSymbol.replace(" ", "")), venue=Venue(details.contract.primaryExchange or details.contract.exchange), ) asset_class = { "STK": AssetClass.EQUITY, }[details.underSecType] kind = { "C": OptionKind.CALL, "P": OptionKind.PUT, }[details.contract.right] return Option( instrument_id=instrument_id, native_symbol=Symbol(details.contract.localSymbol), asset_class=asset_class, currency=Currency.from_str(details.contract.currency), price_precision=price_precision, price_increment=Price(details.minTick, price_precision), multiplier=Quantity.from_int(int(details.contract.multiplier)), lot_size=Quantity.from_int(1), underlying=details.underSymbol, strike_price=Price.from_str(str(details.contract.strike)), expiry_date=datetime.datetime.strptime( details.contract.lastTradeDateOrContractMonth, "%Y%m%d" ).date(), kind=kind, ts_event=timestamp, ts_init=timestamp, )
def adabtc_binance() -> CurrencySpot: """ Return the Binance ADA/BTC instrument for backtesting. Returns ------- CurrencySpot """ return CurrencySpot( instrument_id=InstrumentId( symbol=Symbol("ADA/BTC"), venue=Venue("BINANCE"), ), local_symbol=Symbol("ADABTC"), base_currency=ADA, quote_currency=BTC, price_precision=8, size_precision=8, price_increment=Price(1e-08, precision=8), size_increment=Quantity(1e-08, precision=8), lot_size=None, max_quantity=Quantity.from_int(90000000), min_quantity=Quantity.from_int(1), max_notional=None, min_notional=Money(0.00010000, BTC), max_price=Price(1000, precision=8), min_price=Price(1e-8, precision=8), margin_init=Decimal("0"), margin_maint=Decimal("0"), maker_fee=Decimal("0.0010"), taker_fee=Decimal("0.0010"), ts_event=0, ts_init=0, )
def ethusdt_binance() -> CurrencyPair: """ Return the Binance ETHUSDT instrument for backtesting. Returns ------- CurrencyPair """ return CurrencyPair( instrument_id=InstrumentId( symbol=Symbol("ETHUSDT"), venue=Venue("BINANCE"), ), native_symbol=Symbol("ETHUSDT"), base_currency=ETH, quote_currency=USDT, price_precision=2, size_precision=5, price_increment=Price(1e-02, precision=2), size_increment=Quantity(1e-05, precision=5), lot_size=None, max_quantity=Quantity(9000, precision=5), min_quantity=Quantity(1e-05, precision=5), max_notional=None, min_notional=Money(10.00, USDT), max_price=Price(1000000, precision=2), min_price=Price(0.01, precision=2), margin_init=Decimal("1.00"), margin_maint=Decimal("0.35"), maker_fee=Decimal("0.0001"), taker_fee=Decimal("0.0001"), ts_event=0, ts_init=0, )
def test_instrument_id_equality(self): # Arrange venue1 = InstrumentId(Symbol("AUD/USD"), Venue("SIM")) venue2 = InstrumentId(Symbol("AUD/USD"), Venue("IDEALPRO")) venue3 = InstrumentId(Symbol("GBP/USD"), Venue("SIM")) # Act, Assert assert venue1 == venue1 assert venue1 != venue2 assert venue1 != venue3
def test_instrument_id_equality(self): # Arrange instrument_id1 = InstrumentId(Symbol("AUD/USD"), Venue("SIM")) instrument_id2 = InstrumentId(Symbol("AUD/USD"), Venue("IDEALPRO")) instrument_id3 = InstrumentId(Symbol("GBP/USD"), Venue("SIM")) # Act, Assert assert instrument_id1 == instrument_id1 assert instrument_id1 != instrument_id2 assert instrument_id1 != instrument_id3
def test_symbol_equality(self): # Arrange symbol1 = Symbol("AUDUSD", Venue('FXCM')) symbol2 = Symbol("AUDUSD", Venue('IDEAL_PRO')) symbol3 = Symbol("GBPUSD", Venue('FXCM')) # Act # Assert self.assertTrue(symbol1 == symbol1) self.assertTrue(symbol1 != symbol2) self.assertTrue(symbol1 != symbol3)
def test_bar_type_equality(self): # Arrange instrument_id1 = InstrumentId(Symbol("AUD/USD"), Venue("SIM")) instrument_id2 = InstrumentId(Symbol("GBP/USD"), Venue("SIM")) bar_spec = BarSpecification(1, BarAggregation.MINUTE, PriceType.BID) bar_type1 = BarType(instrument_id1, bar_spec) bar_type2 = BarType(instrument_id1, bar_spec) bar_type3 = BarType(instrument_id2, bar_spec) # Act, Assert assert bar_type1 == bar_type1 assert bar_type1 == bar_type2 assert bar_type1 != bar_type3
def aapl_equity(): return Equity( instrument_id=InstrumentId(symbol=Symbol("AAPL"), venue=Venue("NASDAQ")), local_symbol=Symbol("AAPL"), currency=USD, price_precision=2, price_increment=Price.from_str("0.01"), multiplier=Quantity.from_int(1), lot_size=Quantity.from_int(1), isin="US0378331005", ts_event=0, ts_init=0, )
def test_bar_type_equality(self): # Arrange symbol1 = Symbol("AUD/USD", Venue("SIM")) symbol2 = Symbol("GBP/USD", Venue("SIM")) bar_spec = BarSpecification(1, BarAggregation.MINUTE, PriceType.BID) bar_type1 = BarType(symbol1, bar_spec) bar_type2 = BarType(symbol1, bar_spec) bar_type3 = BarType(symbol2, bar_spec) # Act # Assert self.assertTrue(bar_type1 == bar_type1) self.assertTrue(bar_type1 == bar_type2) self.assertTrue(bar_type1 != bar_type3)
def test_bar_type_equality(self): # Arrange symbol1 = Symbol("AUDUSD", Venue('FXCM')) symbol2 = Symbol("GBPUSD", Venue('FXCM')) bar_spec = BarSpecification(1, BarStructure.MINUTE, PriceType.BID) bar_type1 = BarType(symbol1, bar_spec) bar_type2 = BarType(symbol1, bar_spec) bar_type3 = BarType(symbol2, bar_spec) # Act # Assert self.assertTrue(bar_type1 == bar_type1) self.assertTrue(bar_type1 == bar_type2) self.assertTrue(bar_type1 != bar_type3)
def test_default_fx_with_3_dp_returns_expected_instrument(self): # Arrange loader = TestInstrumentProvider() # Act instrument = loader.default_fx_ccy(Symbol("USD/JPY", Venue("SIM"))) # Assert self.assertEqual(Symbol("USD/JPY", Venue("SIM")), instrument.symbol) self.assertEqual(3, instrument.price_precision) self.assertEqual(Decimal("0.001"), instrument.tick_size) self.assertEqual( Currency(code='JPY', precision=2, currency_type=CurrencyType.FIAT), instrument.quote_currency)
def test_default_fx_with_5_dp_returns_expected_instrument(self): # Arrange loader = InstrumentLoader() # Act instrument = loader.default_fx_ccy(Symbol("AUD/USD", Venue('FXCM'))) # Assert self.assertEqual(Symbol("AUD/USD", Venue('FXCM')), instrument.symbol) self.assertEqual(5, instrument.price_precision) self.assertEqual(Decimal("0.00001"), instrument.tick_size) self.assertEqual( Currency(code='USD', precision=2, currency_type=CurrencyType.FIAT), instrument.quote_currency)
def test_default_fx_with_3_dp_returns_expected_instrument(self): # Arrange loader = InstrumentLoader() # Act instrument = loader.default_fx_ccy(Symbol('USDJPY', Venue('DUKASCOPY'))) # Assert self.assertEqual(Symbol('USDJPY', Venue('DUKASCOPY')), instrument.symbol) self.assertEqual('USD/JPY', instrument.broker_symbol) self.assertEqual(3, instrument.price_precision) self.assertEqual(Decimal(0.001, 3), instrument.tick_size) self.assertEqual(392, instrument.quote_currency)
def test_get_btcusdt_when_loaded_returns_expected_instrument(self): # Arrange mock_client = MagicMock() mock_client.name = "Binance" with open(TEST_PATH + "res_instruments.json") as response: instruments = json.load(response) mock_client.markets = instruments provider = BinanceInstrumentProvider(client=mock_client) provider.load_all() symbol = Symbol("BTC/USDT", Venue("BINANCE")) # Act instrument = provider.get(symbol) # Assert self.assertEqual(Instrument, type(instrument)) self.assertEqual(AssetClass.CRYPTO, instrument.asset_class) self.assertEqual(AssetType.SPOT, instrument.asset_type) self.assertEqual(BTC, instrument.base_currency) self.assertEqual(USDT, instrument.quote_currency) self.assertEqual(USDT, instrument.settlement_currency)
def test_order_working(self): # Arrange uuid = uuid4() event = OrderWorking( account_id=AccountId("SIM", "000"), cl_ord_id=ClientOrderId("O-2020872378423"), order_id=OrderId("123456"), symbol=Symbol("BTC/USDT", Exchange("BINANCE")), order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=Quantity("0.561000"), price=Price("21015.00"), time_in_force=TimeInForce.DAY, expire_time=None, working_time=UNIX_EPOCH, event_id=uuid, event_timestamp=UNIX_EPOCH, ) # Act self.assertEqual( f"OrderWorking(account_id=SIM-000, cl_ord_id=O-2020872378423, " f"order_id=123456, BUY 0.561000 BTC/USDT.BINANCE LIMIT @ 21015.00 DAY, " f"id={uuid})", str(event)) self.assertEqual( f"OrderWorking(account_id=SIM-000, cl_ord_id=O-2020872378423, " f"order_id=123456, BUY 0.561000 BTC/USDT.BINANCE LIMIT @ 21015.00 DAY, " f"id={uuid})", repr(event))
def test_make_instrument_id(self): self.benchmark.pedantic( target=InstrumentId, args=(Symbol("AUD/USD"), Venue("IDEALPRO")), iterations=100_000, rounds=1, )
def setUp(self): # Fixture Setup self.venue = Venue("SIM") self.usdjpy = TestInstrumentProvider.default_fx_ccy(Symbol("USD/JPY", self.venue)) data = BacktestDataContainer() data.add_instrument(self.usdjpy) data.add_bars(self.usdjpy.symbol, BarAggregation.MINUTE, PriceType.BID, TestDataProvider.usdjpy_1min_bid()) data.add_bars(self.usdjpy.symbol, BarAggregation.MINUTE, PriceType.ASK, TestDataProvider.usdjpy_1min_ask()) self.engine = BacktestEngine( data=data, strategies=[TradingStrategy('000')], bypass_logging=True, use_tick_cache=True, ) 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) self.engine.add_exchange( venue=self.venue, oms_type=OMSType.HEDGING, starting_balances=[Money(1_000_000, USD)], modules=[fx_rollover_interest] )
async def run_test(): # Arrange self.data_engine.start() handler = [] request = DataRequest( venue=Venue("RANDOM"), data_type=QuoteTick, metadata={ "Symbol": Symbol("SOMETHING", Venue("RANDOM")), "FromDateTime": None, "ToDateTime": None, "Limit": 1000, }, callback=handler.append, request_id=self.uuid_factory.generate(), request_timestamp=self.clock.utc_now(), ) # Act self.data_engine.send(request) await asyncio.sleep(0.1) # Assert self.assertEqual(0, self.data_engine.message_qsize()) self.assertEqual(1, self.data_engine.request_count) # Tear Down self.data_engine.stop()
def btcusdt_binance() -> Instrument: """ Return the Binance BTC/USDT instrument for backtesting. """ return Instrument( symbol=Symbol("BTC/USDT", Venue("BINANCE")), asset_class=AssetClass.CRYPTO, asset_type=AssetType.SPOT, base_currency=BTC, quote_currency=USDT, settlement_currency=USDT, is_inverse=False, price_precision=2, size_precision=6, tick_size=Decimal("0.01"), multiplier=Decimal("1"), leverage=Decimal("1"), lot_size=Quantity("1"), max_quantity=Quantity("9000.0"), min_quantity=Quantity("1e-06"), max_notional=None, min_notional=Money("10.00000000", USDT), max_price=Price("1000000.0"), min_price=Price("0.01"), margin_init=Decimal(), margin_maint=Decimal(), maker_fee=Decimal("0.001"), taker_fee=Decimal("0.001"), financing={}, timestamp=UNIX_EPOCH, )
def ethusd_bitmex(leverage: Decimal=Decimal("1.0")) -> Instrument: """ Return the BitMEX ETH/USD perpetual contract for backtesting. """ return Instrument( symbol=Symbol("ETH/USD", Venue("BITMEX")), asset_class=AssetClass.CRYPTO, asset_type=AssetType.SWAP, base_currency=ETH, quote_currency=USD, settlement_currency=BTC, is_inverse=True, price_precision=2, size_precision=0, tick_size=Decimal("0.05"), multiplier=Decimal("1"), leverage=leverage, lot_size=Quantity(1), max_quantity=Quantity("10000000.0"), min_quantity=Quantity("1.0"), max_notional=None, min_notional=None, max_price=Price("1000000.00"), min_price=Price("0.05"), margin_init=Decimal("0.02"), margin_maint=Decimal("0.007"), maker_fee=Decimal("-0.00025"), taker_fee=Decimal("0.00075"), financing={}, timestamp=UNIX_EPOCH, )
def test_data_response_message_str_and_repr(self): # Arrange # Act correlation_id = self.uuid_factory.generate() response_id = self.uuid_factory.generate() instrument_id = InstrumentId(Symbol("AUD/USD"), IDEALPRO) response = DataResponse( provider=BINANCE.value, data_type=DataType(QuoteTick, metadata={"InstrumentId": instrument_id}), data=[], correlation_id=correlation_id, response_id=response_id, response_timestamp=self.clock.utc_now(), ) # Assert self.assertEqual("DataResponse(<QuoteTick> {'InstrumentId': InstrumentId('AUD/USD.IDEALPRO')})", str(response)) self.assertEqual( f"DataResponse(" f"provider=BINANCE, " f"data_type=<QuoteTick> {{'InstrumentId': InstrumentId('AUD/USD.IDEALPRO')}}, " f"correlation_id={correlation_id}, " f"id={response_id}, " f"timestamp=1970-01-01 00:00:00+00:00)", repr(response), )
def test_load_futures_contract_instrument(self): # Arrange mock_client = MagicMock() with open(TEST_PATH + "contract_details_cl.pickle", "rb") as file: details = pickle.load(file) # noqa (S301 possible security issue) print(details) mock_client.reqContractDetails.return_value = [details] provider = IBInstrumentProvider(client=mock_client) provider.connect() instrument_id = InstrumentId( symbol=Symbol("CL"), venue=Venue("NYMEX"), ) details = { "asset_class": "COMMODITY", "expiry": "20211119", "currency": "USD", "multiplier": 1000, } # Act provider.load(instrument_id, details) future = provider.find(instrument_id) # Assert assert instrument_id == future.id assert 1000, future.multiplier assert Price.from_str("0.01") == future.price_increment assert 2, future.price_precision
def test_data_response_message_str_and_repr(self): # Arrange # Act correlation_id = self.uuid_factory.generate() response_id = self.uuid_factory.generate() instrument_id = InstrumentId(Symbol("AUD/USD"), IDEALPRO) response = DataResponse( client_id=ClientId(BINANCE.value), data_type=DataType(QuoteTick, metadata={"instrument_id": instrument_id}), data=[], correlation_id=correlation_id, response_id=response_id, timestamp_ns=self.clock.timestamp_ns(), ) # Assert self.assertEqual( "DataResponse(<QuoteTick> {'instrument_id': InstrumentId('AUD/USD.IDEALPRO')})", str(response), ) self.assertEqual( f"DataResponse(" f"client_id=BINANCE, " f"data_type=<QuoteTick> {{'instrument_id': InstrumentId('AUD/USD.IDEALPRO')}}, " f"correlation_id={correlation_id}, " f"id={response_id})", repr(response), )
def test_message_qsize_at_max_blocks_on_send_request(self): self.data_engine = LiveDataEngine(loop=self.loop, portfolio=self.portfolio, clock=self.clock, logger=self.logger, config={"qsize": 1}) handler = [] request = DataRequest( venue=Venue("RANDOM"), data_type=QuoteTick, metadata={ "Symbol": Symbol("SOMETHING", Venue("RANDOM")), "FromDateTime": None, "ToDateTime": None, "Limit": 1000, }, callback=handler.append, request_id=self.uuid_factory.generate(), request_timestamp=self.clock.utc_now(), ) # Act self.data_engine.send(request) self.data_engine.send(request) # Assert self.assertEqual(1, self.data_engine.message_qsize()) self.assertEqual(0, self.data_engine.command_count)
async def run_test(): # Arrange self.engine.start() handler = [] request = DataRequest( client_id=ClientId("RANDOM"), data_type=DataType( QuoteTick, metadata={ "InstrumentId": InstrumentId(Symbol("SOMETHING"), Venue("RANDOM")), "FromDateTime": None, "ToDateTime": None, "Limit": 1000, }, ), callback=handler.append, request_id=self.uuid_factory.generate(), timestamp_ns=self.clock.timestamp_ns(), ) # Act self.engine.send(request) await asyncio.sleep(0.1) # Assert self.assertEqual(0, self.engine.message_qsize()) self.assertEqual(1, self.engine.request_count) # Tear Down self.engine.stop()