import _ from 'lodash/fp' ; import { SYMBOL , SIDE , ORD_TYPE , EXEC_INST } from 'redux/api/bitmex/types' ; import { skewedProbabilityMap , uniformProbabilityMap } from './mathHelpers' ; import { tickerRound , parseNumber } from '../general/formatting' ; import { Order } from 'redux/api/bitmex/types' ; export enum DISTRIBUTION { Uniform = 'Uniform' , Normal = 'Normal' , Positive = 'Positive' , Negative = 'Negative' } export const INSTRUMENT_PARAMS = { XBTUSD : { decimal_rounding : 1 , ticksize : 2 } , ETHUSD : { decimal_rounding : 2 , ticksize : 20 } , XRPUSD : { decimal_rounding : 4 , ticksize : 10000 } , } ; export interface DistributionProps { orderQty : number ; n_tp : number ; start : number ; end : number ; stop : number ; side : SIDE ; symbol : SYMBOL ; } export type ScaledOrder = RegularOrder | StopLoss ; export interface ScaledOrdersProps { ordersProps : DistributionProps ; distribution : DISTRIBUTION ; } export const createScaledOrders = ( { ordersProps , distribution } ) : ScaledOrder [ ] => { switch ( distribution ) { case DISTRIBUTION . Positive : return Positive ( ordersProps ) ; case DISTRIBUTION . Negative : return Negative ( ordersProps ) ; case DISTRIBUTION . Normal : return Normal ( ordersProps ) ; case DISTRIBUTION . Uniform : default : return Uniform ( ordersProps ) ; } } ; const Uniform = ( distrProps ) : ScaledOrder [ ] => skewedDistribution ( distrProps , - 1 , 1 , - 1 , false ) ; const Positive = ( distrProps ) : ScaledOrder [ ] => skewedDistribution ( distrProps , - 1 , 1 , - 1 ) ; const Negative = ( distrProps ) : ScaledOrder [ ] => skewedDistribution ( distrProps , - 1 , 1 , 1 ) ; const Normal = ( distrProps ) : ScaledOrder [ ] => skewedDistribution ( distrProps , - 2 , 2 , 0 ) ; param { object } distributionProps param { number } START_CFG param { number } END_CFG param { number } mean param { boolean } isSkewed returns { object } const skewedDistribution = ( distributionProps , START_CFG , END_CFG , mean , isSkewed = true ) : ScaledOrder [ ] => { const { orderQty , n_tp , start , end , side , symbol , stop } = distributionProps ; const { decimal_rounding } = INSTRUMENT_PARAMS [ symbol ] ; const probabilityDistribution = isSkewed ? skewedProbabilityMap ( n_tp , START_CFG , END_CFG , mean ) : uniformProbabilityMap ( n_tp ) ; const totalProbability = _ . sum ( probabilityDistribution ) ; const start_ = tickerRound ( start , symbol ) ; const end_ = tickerRound ( end , symbol ) ; const incrPrice = tickerRound ( ( end_ - start_ ) / ( n_tp - 1 ) , symbol ) ; const totalOrders = [ ] ; for ( let i = 0 ; i < n_tp ; i ++ ) { const partQuantity = ( probabilityDistribution [ i ] / totalProbability ) * orderQty ; const quantityX = partQuantity / 100 ; const lotSizedQuantity = quantityX > 1 ? Math . round ( quantityX ) * 100 : 100 ; const quantity = symbol === SYMBOL . XBTUSD ? lotSizedQuantity : Math . floor ( partQuantity ) ; totalOrders . push ( createOrder ( { symbol : symbol , side : side , orderQty : quantity , price : parseNumber ( start_ + i * incrPrice , decimal_rounding ) , ordType : ORD_TYPE . Limit , text : `order_${ i + 1 }` , } ) ) ; } if ( totalOrders [ totalOrders . length - 1 ] . price !== end ) { totalOrders [ totalOrders . length - 1 ] . price = end ; } const totalQuantity = _ . sumBy ( 'orderQty' , totalOrders ) ; if ( totalQuantity !== orderQty ) { totalOrders [ totalOrders . length - 1 ] . orderQty += orderQty - totalQuantity ; } if ( stop > 0 ) totalOrders . push ( createStopLoss ( { orderQty , stop , symbol , side } ) as Order ) ; return totalOrders ; } ; type StopLossProps = Pick < Order , 'symbol' | 'orderQty' | 'side' > & { stop : number ; } ; export type StopLoss = Pick < Order , 'symbol' | 'orderQty' | 'side' | 'stopPx' | 'ordType' | 'execInst' > ; const createStopLoss = ( { orderQty , stop , symbol , side } ) => { const { decimal_rounding } = INSTRUMENT_PARAMS [ symbol ] ; const stopPx = parseNumber ( tickerRound ( stop , symbol ) , decimal_rounding ) ; const execInst = [ EXEC_INST . LastPrice , EXEC_INST . ReduceOnly ] . join ( ',' ) ; const stopSide = side === SIDE . BUY ? SIDE . SELL : SIDE . BUY ; return { symbol , side : stopSide , orderQty , stopPx , ordType : ORD_TYPE . Stop , execInst , text : 'stop' } ; } ; export type MarketOrderProps = Pick < Order , 'symbol' | 'orderQty' | 'side' > ; export type RegularOrder = Pick < Order , 'symbol' | 'price' | 'orderQty' | 'side' | 'ordType' | 'text' > ; const createOrder = ( props ) => ( { ... props , execInst : EXEC_INST . ParticipateDoNotInitiate } ) ; export type ProfitTargetProps = Pick < Order , 'orderID' | 'symbol' | 'orderQty' | 'side' | 'price' > & { stop : number ; } ; export type ProfitTarget = ReturnType < typeof createProfitTarget > ; export const createProfitTarget = ( { orderQty , symbol , stop , price , side , orderID } ) => { const { decimal_rounding } = INSTRUMENT_PARAMS [ symbol ] ; const stopPx = parseNumber ( tickerRound ( stop , symbol ) , decimal_rounding ) ; const stopSide = side === SIDE . BUY ? SIDE . SELL : SIDE . BUY ; return { symbol , orderQty , stopPx , price , side : stopSide , ordType : ORD_TYPE . StopLimit , text : `profit-target.${ orderID }` , } ; } ;