Exemple #1
0
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,
    )
Exemple #2
0
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,
    )
Exemple #3
0
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
Exemple #4
0
    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,
        )
Exemple #5
0
    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,
        )
Exemple #6
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
Exemple #9
0
    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
Exemple #10
0
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}")