Skip to content

Better typescript typing for the NewFuturesOrder type #594

Description

@nothing9537

Hello. I noticed one inconvenience when using the package.

When you want to send a new order via USDMClient,

binanceClient.submitNewOrder({ type: 'MARKET' })

there is a general type NewFutureOrder. But in the Binance documentation, there are some combinations of type and everything else. I suggest rewriting the types to make them more convenient, so that the arguments are defined via type and picked up dynamically depending on the transmitted type.

import { BooleanString, FuturesOrderType, OrderSide, OrderTimeInForce, PositionSide, PriceMatchMode, SelfTradePreventionMode, WorkingType } from 'binance';

type BooleanStringUpper = 'TRUE' | 'FALSE';
type PriceOrMatch = { price: string; priceMatch?: never } | { price?: never; priceMatch: PriceMatchMode };
type QtyOrCloseAll = { closePosition: 'true'; quantity?: never; reduceOnly?: never } | { closePosition?: 'false'; quantity: string };
type TIFRequired = { timeInForce: Exclude<OrderTimeInForce, 'GTD'>; goodTillDate?: never } | { timeInForce: 'GTD'; goodTillDate: number | string };
type TIFOptional = { timeInForce?: Exclude<OrderTimeInForce, 'GTD'>; goodTillDate?: never } | { timeInForce: 'GTD'; goodTillDate: number | string };

export interface BaseRequest {
  recvWindow?: number; // default 5000, max 60000
}

export interface NewFuturesOrderBase extends BaseRequest {
  symbol: string;
  side: OrderSide;
  positionSide?: PositionSide; // mandatory in Hedge Mode
  type: FuturesOrderType;
  newClientOrderId?: string; // ^[\.A-Z\:/a-z0-9_-]{1,36}$
  newOrderRespType?: 'ACK' | 'RESULT'; // default ACK
  reduceOnly?: BooleanString; // not allowed in Hedge Mode and with closePosition='true'
  selfTradePreventionMode?: SelfTradePreventionMode; // applies when TIF=GTC/IOC/GTD
}

/** LIMIT: quantity + TIF mandatory + (price XOR priceMatch) */
export type LimitNewFuturesOrder = NewFuturesOrderBase & { type: 'LIMIT'; quantity: string } & TIFRequired & PriceOrMatch;

/** MARKET: quantity */
export type MarketNewFuturesOrder = NewFuturesOrderBase & { type: 'MARKET'; quantity: string };

/** STOP: quantity + stopPrice + (price XOR priceMatch) + optional TIF */
export type StopNewFuturesOrder = NewFuturesOrderBase & {
  type: 'STOP';
  quantity: string;
  stopPrice: string;
  workingType?: WorkingType; // MARK_PRICE | CONTRACT_PRICE
  priceProtect?: BooleanStringUpper; // TRUE | FALSE
} & TIFOptional &
  PriceOrMatch;

/** TAKE_PROFIT: as STOP, priceProtect is also allowed */
export type TakeProfitNewFuturesOrder = NewFuturesOrderBase & {
  type: 'TAKE_PROFIT';
  quantity: string;
  stopPrice: string;
  workingType?: WorkingType;
  priceProtect?: BooleanStringUpper;
} & TIFOptional &
  PriceOrMatch;

/** STOP_MARKET: stopPrice + (quantity XOR closePosition) */
export type StopMarketNewFuturesOrder = NewFuturesOrderBase & {
  type: 'STOP_MARKET';
  stopPrice: string;
  workingType?: WorkingType;
  priceProtect?: BooleanStringUpper;
} & QtyOrCloseAll;

/** TAKE_PROFIT_MARKET: as STOP_MARKET */
export type TakeProfitMarketNewFuturesOrder = NewFuturesOrderBase & {
  type: 'TAKE_PROFIT_MARKET';
  stopPrice: string;
  workingType?: WorkingType;
  priceProtect?: BooleanStringUpper;
} & QtyOrCloseAll;

/** TRAILING_STOP_MARKET: callbackRate mandatory; closePosition not allowed */
export type TrailingStopMarketNewFuturesOrder = NewFuturesOrderBase & {
  type: 'TRAILING_STOP_MARKET';
  quantity: string;
  callbackRate: string; // min 0.1, max 10 (1 => 1%)
  activationPrice?: string;
  workingType?: WorkingType;
};

/** Type map (useful for generics) */
export interface NewFuturesOrderMap {
  LIMIT: LimitNewFuturesOrder;
  MARKET: MarketNewFuturesOrder;
  STOP: StopNewFuturesOrder;
  TAKE_PROFIT: TakeProfitNewFuturesOrder;
  STOP_MARKET: StopMarketNewFuturesOrder;
  TAKE_PROFIT_MARKET: TakeProfitMarketNewFuturesOrder;
  TRAILING_STOP_MARKET: TrailingStopMarketNewFuturesOrder;
}

/** Universal union of all orders */
export type NewFuturesOrder = NewFuturesOrderMap[keyof NewFuturesOrderMap]; // ! That's what we use in binanceRESTClient.submitNewOrder()

/** Useful generic: get form by type constant */
export type NewFuturesOrderByType<T extends keyof NewFuturesOrderMap> = NewFuturesOrderMap[T];

/** Utility type to restrict additional fields when declaring (useful in function signatures) */
export type Exact<T> = T & Record<Exclude<string, keyof T>, never>;

So, in the end, we can use NewFuturesOrder in the REST client and possibly in the WebSocket client.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions