def betfair_account_to_account_state( account_detail, account_funds, event_id, timestamp_ns, account_id="001", ) -> AccountState: currency = Currency.from_str(account_detail["currencyCode"]) balance = float(account_funds["availableToBetBalance"]) balance_locked = -float(account_funds["exposure"]) balance_free = balance - balance_locked return AccountState( AccountId(issuer=BETFAIR_VENUE.value, identifier=account_id), [Money(value=balance, currency=currency)], [Money(value=balance_free, currency=currency)], [Money(value=balance_locked, currency=currency)], { "funds": account_funds, "detail": account_detail }, event_id, timestamp_ns, )
def parse_trade_report_http( account_id: AccountId, instrument_id: InstrumentId, data: Dict[str, Any], report_id: UUID4, ts_init: int, ) -> TradeReport: return TradeReport( account_id=account_id, instrument_id=instrument_id, venue_order_id=VenueOrderId(str(data["orderId"])), trade_id=TradeId(str(data["id"])), order_side=OrderSide.BUY if data["isBuyer"] else OrderSide.SELL, last_qty=Quantity.from_str(data["qty"]), last_px=Price.from_str(data["price"]), commission=Money(data["commission"], Currency.from_str(data["commissionAsset"])), liquidity_side=LiquiditySide.MAKER if data["isMaker"] else LiquiditySide.TAKER, report_id=report_id, ts_event=millis_to_nanos(data["time"]), ts_init=ts_init, )
def _parse_balances( raw_balances: List[Dict[str, str]], asset_key: str, free_key: str, locked_key: str, ) -> List[AccountBalance]: parsed_balances: Dict[Currency, Tuple[Decimal, Decimal, Decimal]] = {} for b in raw_balances: currency = Currency.from_str(b[asset_key]) free = Decimal(b[free_key]) locked = Decimal(b[locked_key]) total: Decimal = free + locked parsed_balances[currency] = (total, locked, free) balances: List[AccountBalance] = [ AccountBalance( currency=currency, total=Money(values[0], currency), locked=Money(values[1], currency), free=Money(values[2], currency), ) for currency, values in parsed_balances.items() ] return balances
def default_fx_ccy(symbol: str, venue: Venue = None, leverage: Decimal = Decimal("50")) -> 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. leverage : Decimal The leverage for the instrument. 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"), leverage=leverage, 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=UNIX_EPOCH, )
def default_fx_ccy(symbol: str, venue: Venue = None) -> CurrencySpot: """ 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 ------- CurrencySpot Raises ------ ValueError If `symbol` 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 CurrencySpot( instrument_id=instrument_id, local_symbol=Symbol(symbol), base_currency=Currency.from_str(base_currency), quote_currency=Currency.from_str(quote_currency), price_precision=price_precision, size_precision=0, price_increment=Price(1 / 10**price_precision, price_precision), size_increment=Quantity.from_int(1), lot_size=Quantity.from_str("1000"), max_quantity=Quantity.from_str("1e7"), min_quantity=Quantity.from_str("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"), ts_event=0, ts_init=0, )
def create( loop: asyncio.AbstractEventLoop, name: str, config: Dict[str, Any], msgbus: MessageBus, cache: Cache, clock: LiveClock, logger: LiveLogger, client_cls=None, ) -> BetfairExecutionClient: """ Create a new Betfair execution client. Parameters ---------- loop : asyncio.AbstractEventLoop The event loop for the client. name : str The client name. config : dict[str, Any] The configuration for the client. msgbus : MessageBus The message bus for the client. cache : Cache The cache for the client. clock : LiveClock The clock for the client. logger : LiveLogger The logger for the client. client_cls : class, optional The internal client constructor. This allows external library and testing dependency injection. Returns ------- BetfairExecutionClient """ market_filter = config.get("market_filter", {}) client = get_cached_betfair_client( username=config.get("username"), password=config.get("password"), app_key=config.get("app_key"), cert_dir=config.get("cert_dir"), loop=loop, logger=logger, ) provider = get_cached_betfair_instrument_provider( client=client, logger=logger, market_filter=tuple(market_filter.items())) # Get account ID env variable or set default account_id_env_var = os.getenv(config.get("account_id", ""), "001") # Set account ID account_id = AccountId(BETFAIR_VENUE.value, account_id_env_var) # Create client exec_client = BetfairExecutionClient( loop=loop, client=client, account_id=account_id, base_currency=Currency.from_str(config.get("base_currency")), msgbus=msgbus, cache=cache, clock=clock, logger=logger, market_filter=market_filter, instrument_provider=provider, ) return exec_client
def test_from_str(self, string, expected): # Arrange, Act result = Currency.from_str(string) # Assert assert result == expected
def test_from_str_given_unknown_code_returns_none(self): # Arrange, Act result = Currency.from_str("SOME_CURRENCY") # Assert assert result is None
def create( loop: asyncio.AbstractEventLoop, name: str, config: Dict[str, Any], msgbus: MessageBus, cache: Cache, clock: LiveClock, logger: LiveLogger, ) -> BetfairExecutionClient: """ Create a new Betfair execution client. Parameters ---------- loop : asyncio.AbstractEventLoop The event loop for the client. name : str The client name. config : dict[str, Any] The configuration for the client. msgbus : MessageBus The message bus for the client. cache : Cache The cache for the client. clock : LiveClock The clock for the client. logger : LiveLogger The logger for the client. Returns ------- BetfairExecutionClient """ market_filter = config.get("market_filter", {}) client = get_cached_betfair_client( username=config.get("username"), password=config.get("password"), app_key=config.get("app_key"), cert_dir=config.get("cert_dir"), loop=loop, logger=logger, ) provider = get_cached_betfair_instrument_provider( client=client, logger=logger, market_filter=tuple(market_filter.items())) # Create client exec_client = BetfairExecutionClient( loop=loop, client=client, base_currency=Currency.from_str(config.get("base_currency")), msgbus=msgbus, cache=cache, clock=clock, logger=logger, market_filter=market_filter, instrument_provider=provider, ) return exec_client
def parse_instrument( account_info: Dict[str, Any], data: Dict[str, Any], ts_init: int, ) -> Instrument: native_symbol = Symbol(data["name"]) asset_type = data["type"] # Create base asset if asset_type == "future": base_asset: str = data["underlying"] base_currency = Currency( code=base_asset, precision=8, iso4217=0, # Currently undetermined for crypto assets name=base_asset, currency_type=CurrencyType.CRYPTO, ) quote_currency: Currency = USD elif asset_type == "spot": base_asset = data["baseCurrency"] base_currency = Currency( code=base_asset, precision=8, iso4217=0, # Currently undetermined for crypto assets name=base_asset, currency_type=CurrencyType.CRYPTO, ) # Create quote asset quote_asset: str = data["quoteCurrency"] quote_currency = Currency.from_str(quote_asset) if quote_currency is None: quote_currency = Currency( code=quote_asset, precision=precision_from_str(str(data["priceIncrement"])), iso4217=0, # Currently undetermined for crypto assets name=quote_asset, currency_type=CurrencyType.CRYPTO, ) else: # pragma: no cover (design-time error) raise RuntimeError(f"unknown asset type, was {asset_type}") # symbol = Symbol(base_currency.code + "/" + quote_currency.code) instrument_id = InstrumentId(symbol=native_symbol, venue=FTX_VENUE) price_precision = precision_from_str(str(data["priceIncrement"])) size_precision = precision_from_str(str(data["sizeIncrement"])) price_increment = Price.from_str(str(data["priceIncrement"])) size_increment = Quantity.from_str(str(data["sizeIncrement"])) min_provide_size = data.get("minProvideSize") lot_size = (Quantity.from_str(str(min_provide_size)) if min_provide_size else Quantity.from_int(1)) margin_init = Decimal(str(account_info["initialMarginRequirement"])) margin_maint = Decimal(str(account_info["maintenanceMarginRequirement"])) maker_fee = Decimal(str(account_info.get("makerFee"))) taker_fee = Decimal(str(account_info.get("takerFee"))) if asset_type == "spot": # Create instrument return CurrencyPair( instrument_id=instrument_id, native_symbol=native_symbol, base_currency=base_currency, quote_currency=quote_currency, price_precision=price_precision, size_precision=size_precision, price_increment=price_increment, size_increment=size_increment, lot_size=lot_size, max_quantity=None, min_quantity=None, max_notional=None, min_notional=None, max_price=None, min_price=None, margin_init=margin_init, margin_maint=margin_maint, maker_fee=maker_fee, taker_fee=taker_fee, ts_event=ts_init, ts_init=ts_init, info=data, ) elif asset_type == "future": # Create instrument if native_symbol.value.endswith("-PERP"): return CryptoPerpetual( instrument_id=instrument_id, native_symbol=native_symbol, base_currency=base_currency, quote_currency=quote_currency, settlement_currency=USD, is_inverse=False, price_precision=price_precision, size_precision=size_precision, price_increment=price_increment, size_increment=size_increment, max_quantity=None, min_quantity=None, max_notional=None, min_notional=None, max_price=None, min_price=None, margin_init=margin_init, margin_maint=margin_maint, maker_fee=maker_fee, taker_fee=taker_fee, ts_event=ts_init, ts_init=ts_init, info=data, ) else: return Future( instrument_id=instrument_id, native_symbol=native_symbol, asset_class=AssetClass.CRYPTO, currency=USD, price_precision=price_precision, price_increment=price_increment, multiplier=Quantity.from_int(1), lot_size=Quantity.from_int(1), underlying=data["underlying"], expiry_date=datetime.utcnow().date(), # TODO(cs): Implement # margin_init=margin_init, # TODO(cs): Implement # margin_maint=margin_maint, # TODO(cs): Implement # maker_fee=maker_fee, # TODO(cs): Implement # taker_fee=taker_fee, # TODO(cs): Implement ts_event=ts_init, ts_init=ts_init, ) else: # pragma: no cover (design-time error) raise ValueError( f"Cannot parse market instrument: unknown asset type {asset_type}")