Esempio n. 1
0
async def get_symbols_ftx(
    client: ntypes.CLIENT,
    #  prevent unintentional passing of following args
    *,
    logger: typing.Optional[typing.Callable] = None,
    base_url: pydantic.AnyHttpUrl = endpoints.FTX_ENDPOINTS.public.url,
    endpoint: str = endpoints.FTX_ENDPOINTS.public.endpoints.symbols,
) -> Result[NoobitResponseSymbols, pydantic.ValidationError]:

    # ftx has variable urls besides query params
    # format: https://ftx.com/api/markets/
    req_url = "/".join([base_url, endpoint])
    method = "GET"
    headers: typing.Dict = {}

    # no query params but needs to wrapped in a result that contains an instance of FrozenBaseModel
    valid_ftx_req = Ok(FrozenBaseModel())
    result_content = await get_result_content_from_req(client, method, req_url,
                                                       valid_ftx_req.value,
                                                       headers)
    if isinstance(result_content, Err):
        return result_content

    if logger:
        logger(f"Symbols - Result Content : {result_content.value}")

    valid_result_content = _validate_data(
        FtxResponseSymbols, pmap({"symbols": result_content.value}))
    if valid_result_content.is_err():
        return valid_result_content

    parsed_result = parse_result(valid_result_content.value)

    valid_parsed_response_data = _validate_data(
        NoobitResponseSymbols,
        pmap({
            **parsed_result, "rawJson": result_content.value,
            "exchange": "FTX"
        }),
    )
    return valid_parsed_response_data
Esempio n. 2
0
async def get_wstoken_binance(
    client: ntypes.CLIENT,
    # prevent unintentional passing of following args
    *,
    auth=BinanceAuth(),
    base_url: pydantic.AnyHttpUrl = endpoints.BINANCE_ENDPOINTS.private.url,
    endpoint: str = endpoints.BINANCE_ENDPOINTS.private.endpoints.ws_token
) -> Result[BinanceResponseWsToken, pydantic.ValidationError]:

    req_url = urljoin(base_url, endpoint)
    method = "POST"
    headers: typing.Dict = auth.headers()

    result_content = await get_result_content_from_req(client, method, req_url,
                                                       FrozenBaseModel(),
                                                       headers)
    if result_content.is_err():
        return result_content

    valid_result_content = _validate_data(BinanceResponseWsToken,
                                          result_content.value)
    return valid_result_content
Esempio n. 3
0
async def get_req_content(
    result_or_err: result_or_err_sig,
    parse_err_content: parse_err_content_sig,
    client: ntypes.CLIENT,
    method: str,
    url: pydantic.AnyHttpUrl,
    valid_req: FrozenBaseModel,
    headers: typing.Mapping,
) -> Result:
    """meant to be derived using functools.partial in `exchange`.rest.base.py
    """

    payload = {"method": method, "url": url, "headers": headers}

    query = valid_req.dict(exclude_none=True)

    if method in ["GET"]:
        payload["params"] = query

    elif method in ["POST", "DELETE"]:
        payload["data"] = query

    else:
        raise NotImplementedError(f"Unsupported method : {method}")

    # TODO handle timeout (httpx._exceptions.ConnectTimeout)
    try:
        resp = await client.request(**payload)  #type: ignore
    except Exception as e:
        req_url = urllib.parse.urljoin("url", urllib.parse.urlencode(payload))
        return Err(RequestTimeout(str(e), f"<{method} {req_url}>"))

    content = await result_or_err(resp)
    if content.is_err():
        parsed_err_content = parse_err_content(content.value,
                                               get_sent_request(resp))
        return Err(parsed_err_content)
    else:
        return content
Esempio n. 4
0
async def get_symbols_binance(
    client: ntypes.CLIENT,
    # prevent unintentional passing of following args
    *,
    logger: typing.Optional[typing.Callable] = None,
    base_url: pydantic.AnyHttpUrl = endpoints.BINANCE_ENDPOINTS.public.url,
    endpoint: str = endpoints.BINANCE_ENDPOINTS.public.endpoints.symbols
) -> Result[NoobitResponseSymbols, Exception]:

    req_url = urljoin(base_url, endpoint)
    method = "GET"
    headers: typing.Dict = {}

    # no query params but needs to wrapped in a result that contains an instance of FrozenBaseModel
    valid_binance_req = Ok(FrozenBaseModel())

    result_content = await get_result_content_from_req(client, method, req_url,
                                                       valid_binance_req.value,
                                                       headers)
    if result_content.is_err():
        return result_content

    if logger:
        logger(f"Symbols - Result Content : {result_content.value}")

    valid_result_content = _validate_data(BinanceResponseSymbols,
                                          result_content.value)
    if valid_result_content.is_err():
        return valid_result_content

    parsed_result = parse_result(valid_result_content.value)

    valid_parsed_response_data = _validate_data(
        NoobitResponseSymbols,
        pmap({
            **parsed_result, "rawJson": result_content.value,
            "exchange": "BINANCE"
        }))
    return valid_parsed_response_data
Esempio n. 5
0
async def post_neworder_binance(
    client: ntypes.CLIENT,
    symbol: ntypes.SYMBOL,
    symbols_resp: NoobitResponseSymbols,
    side: ntypes.ORDERSIDE,
    ordType: ntypes.ORDERTYPE,
    clOrdID: str,
    orderQty: Decimal,
    price: Decimal,
    timeInForce: ntypes.TIMEINFORCE,
    stopPrice: typing.Optional[Decimal] = None,
    quoteOrderQty: typing.Optional[Decimal] = None,
    # prevent unintentional passing of following args
    *,
    logger: typing.Optional[typing.Callable] = None,
    auth=BinanceAuth(),
    base_url: pydantic.AnyHttpUrl = endpoints.BINANCE_ENDPOINTS.private.url,
    endpoint: str = endpoints.BINANCE_ENDPOINTS.private.endpoints.new_order,
) -> Result[NoobitResponseItemOrder, ValidationError]:

    symbol_to_exchange = lambda x: {
        k: v.exchange_pair
        for k, v in symbols_resp.asset_pairs.items()
    }[x]

    req_url = urljoin(base_url, endpoint)
    method = "POST"
    headers: typing.Dict = auth.headers()

    valid_noobit_req = _validate_data(
        NoobitRequestAddOrder,
        pmap({
            "exchange": "BINANCE",
            "symbol": symbol,
            "symbols_resp": symbols_resp,
            "side": side,
            "ordType": ordType,
            "clOrdID": clOrdID,
            "orderQty": orderQty,
            "price": price,
            "timeInForce": timeInForce,
            "quoteOrderQty": quoteOrderQty,
            "stopPrice": stopPrice,
        }))

    if valid_noobit_req.is_err():
        return valid_noobit_req

    if logger:
        logger(f"New Order - Noobit Request : {valid_noobit_req.value}")

    parsed_req = parse_request(valid_noobit_req.value, symbol_to_exchange)
    parsed_req["timestamp"] = auth.nonce

    valid_binance_req = _validate_data(BinanceRequestNewOrder,
                                       pmap({**parsed_req}))
    if valid_binance_req.is_err():
        return valid_binance_req

    if logger:
        logger(f"New Order - Parsed Request : {valid_binance_req.value}")

    #! sign after validation, otherwise we aill get all the non values too
    signed_req: dict = auth._sign(
        valid_binance_req.value.dict(exclude_none=True))

    #! we should not pass in "params" to the client, but construct the whole url + query string ourself, so we can make sure its sorted properly

    #! ====>
    qstrings = sorted([(k, v)
                       for k, v in signed_req.items() if not "signature" in k],
                      reverse=True)
    qstrings_join = urlencode(qstrings)
    full_url = "?".join([req_url, qstrings_join])
    full_url += f"&signature={signed_req['signature']}"
    #! <=====

    result_content = await get_result_content_from_req(client, method,
                                                       full_url,
                                                       FrozenBaseModel(),
                                                       headers)
    if result_content.is_err():
        return result_content

    if logger:
        logger(f"New Order - Result Content : {result_content.value}")

    valid_result_content = _validate_data(BinanceResponseNewOrder,
                                          result_content.value)
    if valid_result_content.is_err():
        return valid_result_content

    parsed_result = parse_result(valid_result_content.value, symbol)

    valid_parsed_response_data = _validate_data(
        NoobitResponseItemOrder,
        pmap({
            **parsed_result, "rawJson": result_content.value,
            "exchange": "BINANCE"
        }))
    return valid_parsed_response_data
Esempio n. 6
0
async def get_closedorders_ftx(
    client: ntypes.CLIENT,
    symbol: ntypes.SYMBOL,
    symbols_resp: NoobitResponseSymbols,
    #  prevent unintentional passing of following args
    *,
    logger: typing.Optional[typing.Callable] = None,
    auth=FtxAuth(),
    base_url: pydantic.AnyHttpUrl = endpoints.FTX_ENDPOINTS.private.url,
    endpoint: str = endpoints.FTX_ENDPOINTS.private.endpoints.closed_orders,
) -> Result[NoobitResponseClosedOrders, pydantic.ValidationError]:

    symbol_from_exchange = lambda x: {
        f"{v.noobit_base}{v.noobit_quote}": k
        for k, v in symbols_resp.asset_pairs.items()
    }[x]
    symbol_to_exchange = lambda x: {
        k: v.exchange_pair
        for k, v in symbols_resp.asset_pairs.items()
    }[x]

    req_url = "/".join([base_url, "orders/history"])
    method = "GET"

    valid_noobit_req = _validate_data(
        NoobitRequestClosedOrders,
        pmap({
            "symbol": symbol,
            "symbols_resp": symbols_resp
        }),
    )
    if valid_noobit_req.is_err():
        return valid_noobit_req

    parsed_req = parse_request(valid_noobit_req.value, symbol_to_exchange)

    valid_ftx_req = _validate_data(FtxRequestClosedOrder, pmap(parsed_req))
    if valid_ftx_req.is_err():
        return valid_ftx_req

    querystr = f"?market={valid_ftx_req.value.market}"
    req_url += querystr
    headers = auth.headers(method, f"/api/orders/history{querystr}")

    if logger:
        logger(f"Closed Orders - Parsed Request : {valid_ftx_req.value}")

    result_content = await get_result_content_from_req(client, method, req_url,
                                                       FrozenBaseModel(),
                                                       headers)
    if result_content.is_err():
        return result_content

    if logger:
        logger(f"Open Orders - Result content : {result_content.value}")

    valid_result_content = _validate_data(
        FtxResponseOrder, pmap({"orders": result_content.value}))
    if valid_result_content.is_err():
        return valid_result_content

    parsed_result_data = parse_result(valid_result_content.value,
                                      symbol_from_exchange, symbol)

    valid_parsed_response = _validate_data(
        NoobitResponseClosedOrders,
        pmap({
            "orders": parsed_result_data,
            "rawJson": result_content.value,
            "exchange": "FTX",
        }),
    )
    return valid_parsed_response
Esempio n. 7
0
async def post_neworder_ftx(
    client: ntypes.CLIENT,
    symbol: ntypes.SYMBOL,
    symbols_resp: NoobitResponseSymbols,
    side: ntypes.ORDERSIDE,
    ordType: ntypes.ORDERTYPE,
    clOrdID: str,
    orderQty: Decimal,
    price: Decimal,
    timeInForce: ntypes.TIMEINFORCE,
    stopPrice: typing.Optional[Decimal] = None,
    quoteOrderQty: typing.Optional[Decimal] = None,
    # prevent unintentional passing of following args
    *,
    logger: typing.Optional[typing.Callable] = None,
    auth=FtxAuth(),
    base_url: pydantic.AnyHttpUrl = endpoints.FTX_ENDPOINTS.private.url,
    endpoint: str = endpoints.FTX_ENDPOINTS.private.endpoints.new_order,
) -> Result[NoobitResponseItemOrder, pydantic.ValidationError]:

    symbol_from_exchange = lambda x: {
        f"{v.noobit_base}{v.noobit_quote}": k
        for k, v in symbols_resp.asset_pairs.items()
    }[x]
    symbol_to_exchange = lambda x: {
        k: v.exchange_pair
        for k, v in symbols_resp.asset_pairs.items()
    }[x]

    req_url = "/".join([base_url, "orders"])
    method = "POST"

    valid_noobit_req = _validate_data(
        NoobitRequestAddOrder,
        pmap({
            "exchange": "FTX",
            "symbol": symbol,
            "symbols_resp": symbols_resp,
            "side": side,
            "ordType": ordType,
            "clOrdID": clOrdID,
            "orderQty": orderQty,
            "price": price,
            "timeInForce": timeInForce,
            "quoteOrderQty": quoteOrderQty,
            "stopPrice": stopPrice,
        }))
    if valid_noobit_req.is_err():
        return valid_noobit_req

    # if logger:
    #     logger(f"New Order - Noobit Request : {valid_noobit_req.value}")

    parsed_req = parse_request(valid_noobit_req.value, symbol_to_exchange)

    valid_ftx_req = _validate_data(FtxRequestNewOrder, pmap(parsed_req))
    if valid_ftx_req.is_err():
        return valid_ftx_req

    # ? should be more elegant way to do this
    querystr = f"?market={valid_ftx_req.value.market}"
    req_url += querystr
    headers = auth.headers(method, f"/api/orders{querystr}")

    if logger:
        logger(f"New Order - Parsed Request : {valid_ftx_req.value}")

    result_content = await get_result_content_from_req(client, method, req_url,
                                                       FrozenBaseModel(),
                                                       headers)
    if result_content.is_err():
        return result_content

    if logger:
        logger(f"New Order - Result content : {result_content.value}")

    valid_result_content = _validate_data(FtxResponseNewOrder,
                                          result_content.value)
    if valid_result_content.is_err():
        return valid_result_content

    parsed_result = parse_result(valid_result_content.value,
                                 symbol_from_exchange)

    valid_parsed_response = _validate_data(
        NoobitResponseNewOrder,
        pmap({
            **parsed_result,
            "rawJson": result_content.value,
            "exchange": "FTX",
        }),
    )
    return valid_parsed_response