def test_not_in_when_item_is_in_list_raises_value_error(self): # Arrange # Act # Assert with pytest.raises(KeyError): PyCondition.not_in("a", ["a", 1], "item", "list")
def true(): PyCondition.true(True, "this should be true")
def __init__(self, config: Optional[TradingNodeConfig] = None): if config is None: config = TradingNodeConfig() PyCondition.not_none(config, "config") PyCondition.type(config, TradingNodeConfig, "config") # Configuration self._config = config # Setup loop self._loop = asyncio.get_event_loop() self._executor = concurrent.futures.ThreadPoolExecutor() self._loop.set_default_executor(self._executor) self._loop.set_debug(config.loop_debug) # Components self._clock = LiveClock(loop=self._loop) self._uuid_factory = UUIDFactory() self.created_time = self._clock.utc_now() self._is_running = False # Identifiers self.trader_id = TraderId(config.trader_id) self.machine_id = socket.gethostname() self.instance_id = self._uuid_factory.generate() # Setup logging self._logger = LiveLogger( loop=self._loop, clock=self._clock, trader_id=self.trader_id, machine_id=self.machine_id, instance_id=self.instance_id, level_stdout=LogLevelParser.from_str_py(config.log_level.upper()), ) self._log = LoggerAdapter( component_name=type(self).__name__, logger=self._logger, ) self._log_header() self._log.info("Building...") if platform.system() != "Windows": # Windows does not support signal handling # https://stackoverflow.com/questions/45987985/asyncio-loops-add-signal-handler-in-windows self._setup_loop() ######################################################################## # Build platform ######################################################################## if config.cache_database is None or config.cache_database.type == "in-memory": cache_db = None elif config.cache_database.type == "redis": cache_db = RedisCacheDatabase( trader_id=self.trader_id, logger=self._logger, serializer=MsgPackSerializer(timestamps_as_str=True), config=config.cache_database, ) else: # pragma: no cover (design-time error) raise ValueError( "The cache_db_type in the configuration is unrecognized, " "can one of {{'in-memory', 'redis'}}.", ) self._msgbus = MessageBus( trader_id=self.trader_id, clock=self._clock, logger=self._logger, ) self._cache = Cache( database=cache_db, logger=self._logger, config=config.cache, ) self.portfolio = Portfolio( msgbus=self._msgbus, cache=self._cache, clock=self._clock, logger=self._logger, ) self._data_engine = LiveDataEngine( loop=self._loop, msgbus=self._msgbus, cache=self._cache, clock=self._clock, logger=self._logger, config=config.data_engine, ) self._exec_engine = LiveExecutionEngine( loop=self._loop, msgbus=self._msgbus, cache=self._cache, clock=self._clock, logger=self._logger, config=config.exec_engine, ) self._exec_engine.load_cache() self._risk_engine = LiveRiskEngine( loop=self._loop, portfolio=self.portfolio, msgbus=self._msgbus, cache=self._cache, clock=self._clock, logger=self._logger, config=config.risk_engine, ) self.trader = Trader( trader_id=self.trader_id, msgbus=self._msgbus, cache=self._cache, portfolio=self.portfolio, data_engine=self._data_engine, risk_engine=self._risk_engine, exec_engine=self._exec_engine, clock=self._clock, logger=self._logger, ) if config.load_strategy_state: self.trader.load() # Setup persistence (requires trader) self.persistence_writers: List[Any] = [] if config.persistence: self._setup_persistence(config=config.persistence) self._builder = TradingNodeBuilder( loop=self._loop, data_engine=self._data_engine, exec_engine=self._exec_engine, msgbus=self._msgbus, cache=self._cache, clock=self._clock, logger=self._logger, log=self._log, ) self._log.info("INITIALIZED.") self.time_to_initialize = self._clock.delta(self.created_time) self._log.info(f"Initialized in {int(self.time_to_initialize.total_seconds() * 1000)}ms.") self._is_built = False
def type_or_none(): PyCondition.type_or_none("hello", str, "world")
def default_fx_ccy(symbol: str, venue: Venue = None) -> Instrument: """ Return a default FX currency pair instrument from the given instrument_id. Parameters ---------- symbol : str The currency pair symbol. venue : Venue The currency pair venue. Returns ------- Instrument Raises ------ ValueError If the instrument_id.instrument_id length is not in range [6, 7]. """ if venue is None: venue = Venue("SIM") PyCondition.valid_string(symbol, "symbol") PyCondition.in_range_int(len(symbol), 6, 7, "len(symbol)") instrument_id = InstrumentId( symbol=Symbol(symbol), venue=venue, ) base_currency = symbol[:3] quote_currency = symbol[-3:] # Check tick precision of quote currency if quote_currency == "JPY": price_precision = 3 else: price_precision = 5 return Instrument( instrument_id=instrument_id, asset_class=AssetClass.FX, asset_type=AssetType.SPOT, base_currency=Currency.from_str(base_currency), quote_currency=Currency.from_str(quote_currency), settlement_currency=Currency.from_str(quote_currency), is_inverse=False, price_precision=price_precision, size_precision=0, tick_size=Decimal(f"{1 / 10 ** price_precision:.{price_precision}f}"), multiplier=Decimal("1"), lot_size=Quantity("1000"), max_quantity=Quantity("1e7"), min_quantity=Quantity("1000"), max_price=None, min_price=None, max_notional=Money(50000000.00, USD), min_notional=Money(1000.00, USD), margin_init=Decimal("0.03"), margin_maint=Decimal("0.03"), maker_fee=Decimal("0.00002"), taker_fee=Decimal("0.00002"), financing={}, timestamp_ns=0, )
def test_not_none_when_arg_none_raises_type_error(self): # Arrange # Act # Assert with pytest.raises(TypeError): PyCondition.not_none(None, "param")
def test_type_when_type_is_correct_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.type("a string", str, "param")
def test_not_negative_int_when_arg_negative_raises_value_error(self): # Arrange # Act # Assert with pytest.raises(ValueError): PyCondition.not_negative_int(-1, "param")
def test_not_negative_int_when_args_zero_or_positive_does_nothing( self, value): # Arrange # Act # Assert: ValueError not raised PyCondition.not_negative_int(value, "param")
def test_empty_when_collection_not_empty_raises_value_error(self): # Arrange # Act # Assert with pytest.raises(ValueError): PyCondition.empty([1, 2], "some_collection")
def test_empty_when_collection_empty_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.empty([], "some_collection")
def test_key_not_in_when_key_not_in_dictionary_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.not_in("b", {"a": 1}, "key", "dict")
def test_key_not_in_when_key_is_in_dictionary_raises_key_error(self): # Arrange # Act # Assert with pytest.raises(KeyError): PyCondition.not_in("a", {"a": 1}, "key", "dict")
def test_not_in_when_item_not_in_list_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.not_in("b", ["a", 1], "item", "list")
def test_valid_port_when_in_range_does_nothing(self): # Arrange # Act # Assert PyCondition.valid_port(55555, "port")
def test_positive_when_args_positive_does_nothing(self, value): # Arrange # Act # Assert: AssertionError not raised PyCondition.positive(value, "param")
def test_false_when_predicate_false_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.false(False, "this should be False")
def test_true_when_predicate_true_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.true(True, "this should be True")
def test_not_none_when_arg_not_none_does_nothing(self): # Arrange # Act # Assert: ValueError not raised PyCondition.not_none("something", "param")
def test_positive_int_when_args_positive_does_nothing(self): # Arrange # Act # Assert: AssertionError not raised PyCondition.positive_int(1, "param")
def test_type_or_none_when_type_is_incorrect_raises_type_error(self): # Arrange # Act # Assert with pytest.raises(TypeError): PyCondition.type("a string", int, "param")
def test_in_range_int_when_args_in_range_does_nothing( self, value, start, end): # Arrange # Act # Assert: ValueError not raised PyCondition.in_range_int(value, start, end, "param")
def __init__( self, strategies: List[TradingStrategy], config: Dict[str, object], ): """ Initialize a new instance of the TradingNode class. Parameters ---------- strategies : list[TradingStrategy] The list of strategies to run on the trading node. config : dict[str, object] The configuration for the trading node. Raises ------ ValueError If strategies is None or empty. ValueError If config is None or empty. """ PyCondition.not_none(strategies, "strategies") PyCondition.not_none(config, "config") PyCondition.not_empty(strategies, "strategies") PyCondition.not_empty(config, "config") self._config = config # Extract configs config_trader = config.get("trader", {}) config_system = config.get("system", {}) config_log = config.get("logging", {}) config_cache_db = config.get("cache_database", {}) config_cache = config.get("cache", {}) config_data = config.get("data_engine", {}) config_risk = config.get("risk_engine", {}) config_exec = config.get("exec_engine", {}) config_strategy = config.get("strategy", {}) # System config self._timeout_connection = config_system.get("timeout_connection", 5.0) self._timeout_reconciliation = config_system.get( "timeout_reconciliation", 10.0) self._timeout_portfolio = config_system.get("timeout_portfolio", 10.0) self._timeout_disconnection = config_system.get( "timeout_disconnection", 5.0) self._check_residuals_delay = config_system.get( "check_residuals_delay", 5.0) self._load_strategy_state = config_strategy.get("load_state", True) self._save_strategy_state = config_strategy.get("save_state", True) # Setup loop self._loop = asyncio.get_event_loop() self._executor = concurrent.futures.ThreadPoolExecutor() self._loop.set_default_executor(self._executor) self._loop.set_debug(config_system.get("loop_debug", False)) # Components self._clock = LiveClock(loop=self._loop) self._uuid_factory = UUIDFactory() self.system_id = self._uuid_factory.generate() self.created_time = self._clock.utc_now() self._is_running = False # Setup identifiers self.trader_id = TraderId( f"{config_trader['name']}-{config_trader['id_tag']}", ) # Setup logging level_stdout = LogLevelParser.from_str_py( config_log.get("level_stdout")) self._logger = LiveLogger( loop=self._loop, clock=self._clock, trader_id=self.trader_id, system_id=self.system_id, level_stdout=level_stdout, ) self._log = LoggerAdapter( component=self.__class__.__name__, logger=self._logger, ) self._log_header() self._log.info("Building...") if platform.system() != "Windows": # Requires the logger to be initialized # Windows does not support signal handling # https://stackoverflow.com/questions/45987985/asyncio-loops-add-signal-handler-in-windows self._setup_loop() # Build platform # ---------------------------------------------------------------------- if config_cache_db["type"] == "redis": cache_db = RedisCacheDatabase( trader_id=self.trader_id, logger=self._logger, instrument_serializer=MsgPackInstrumentSerializer(), command_serializer=MsgPackCommandSerializer(), event_serializer=MsgPackEventSerializer(), config={ "host": config_cache_db["host"], "port": config_cache_db["port"], }, ) else: cache_db = BypassCacheDatabase( trader_id=self.trader_id, logger=self._logger, ) cache = Cache( database=cache_db, logger=self._logger, config=config_cache, ) self.portfolio = Portfolio( cache=cache, clock=self._clock, logger=self._logger, ) self._data_engine = LiveDataEngine( loop=self._loop, portfolio=self.portfolio, cache=cache, clock=self._clock, logger=self._logger, config=config_data, ) self._exec_engine = LiveExecutionEngine( loop=self._loop, portfolio=self.portfolio, cache=cache, clock=self._clock, logger=self._logger, config=config_exec, ) self._risk_engine = LiveRiskEngine( loop=self._loop, exec_engine=self._exec_engine, portfolio=self.portfolio, cache=cache, clock=self._clock, logger=self._logger, config=config_risk, ) # Wire up components self._exec_engine.register_risk_engine(self._risk_engine) self._exec_engine.load_cache() self.trader = Trader( trader_id=self.trader_id, strategies=strategies, portfolio=self.portfolio, data_engine=self._data_engine, risk_engine=self._risk_engine, exec_engine=self._exec_engine, clock=self._clock, logger=self._logger, ) if self._load_strategy_state: self.trader.load() self._builder = TradingNodeBuilder( data_engine=self._data_engine, exec_engine=self._exec_engine, clock=self._clock, logger=self._logger, log=self._log, ) self._log.info("state=INITIALIZED.") self.time_to_initialize = self._clock.delta(self.created_time) self._log.info( f"Initialized in {self.time_to_initialize.total_seconds():.3f}s.") self._is_built = False
def test_valid_string_given_none_raises_type_error(self): # Arrange # Act # Assert with pytest.raises(TypeError): PyCondition.valid_string(None, "param")
def none(): PyCondition.none(None, "param")
def test_false_when_predicate_true_raises_value_error(self): # Arrange # Act # Assert with pytest.raises(ValueError): PyCondition.false(True, "predicate")
def valid_string(): PyCondition.valid_string("abc123", "string_param")
def test_valid_string_with_valid_string_does_nothing(self, value): # Arrange # Act # Assert: ValueError not raised PyCondition.valid_string(value, "param")
def test_callable_or_none_when_arg_is_callable_or_none_does_nothing( self, value): # Arrange, Act, Assert: ValueError not raised PyCondition.callable_or_none(value, "param")
def test_raises_custom_exception(self): # Arrange # Act # Assert with pytest.raises(RuntimeError): PyCondition.true(False, "predicate", RuntimeError)