import { createSelector } from '@reduxjs/toolkit' ; import { SYMBOL , SIDE , Order } from '../../redux/api/bitmex/types' ; import { AppState } from 'redux/modules/state' ; import { INSTRUMENT_PARAMS , RegularOrder , StopLoss } from 'utils' ; import { parseNumber } from 'general/formatting' ; export interface SymbolPrices { symbol : SYMBOL ; askPrice : number ; bidPrice : number ; } interface CurrentPrice { askPrice : number ; bidPrice : number ; } export const getShowPreview = ( { preview : { showPreview } } ) => showPreview ; export const getOrders = ( { preview : { orders } } ) => orders ; const getOrderLoading = ( { preview : { previewLoading } } ) => previewLoading ; const getOrderError = ( { preview : { error } } ) => error ; export const getBalance = ( { preview : { balance } } ) => balance ; export const table_instrument = ( { websocket : { instrument } } ) => instrument ; const table_order = ( { websocket : { order } } ) => order ; const websocketLoading = ( { websocket : { wsLoading } } ) => wsLoading ; const websocketMessage = ( { websocket : { message } } ) => message ; const websocketConnected = ( { websocket : { connected } } ) => connected ; const getTrailingOrderStatus = ( { trailing : { trailOrderStatus } } ) => trailOrderStatus ; const getTrailingOrderId = ( { trailing : { trailOrderId } } ) => trailOrderId ; const getTrailingOrderSide = ( { trailing : { trailOrderSide } } ) => trailOrderSide ; export const getTrailingOrderSymbol = ( { trailing : { trailOrderSymbol } } ) => trailOrderSymbol ; const getCrossOrderSide = ( { cross : { crossOrderSide } } ) => crossOrderSide ; const getHasPriceCrossedOnce = ( { cross : { hasPriceCrossedOnce } } ) => hasPriceCrossedOnce ; const getCrossOrderPrice = ( { cross : { crossOrderPrice } } ) => crossOrderPrice ; const getOpenOrders = ( { orders : { openOrders } } ) => openOrders ; const getProfitOrders = ( { orders : { profitOrders } } ) => profitOrders ; export const trailingOrderStatusSelector = createSelector ( [ table_order , getTrailingOrderId ] , ( open_orders , trailingOrderId ) => { for ( let i = 0 ; i < open_orders . length ; i ++ ) { if ( open_orders [ i ] . orderID === trailingOrderId ) { return open_orders [ i ] . ordStatus || '' ; } } return 'Order not placed.' ; } ) ; export const websocketBidAskPrices = createSelector ( [ table_instrument , getTrailingOrderSymbol ] , ( data , symbol ) : CurrentPrice | undefined => { for ( const dataRow of data ) { if ( dataRow . symbol === symbol && dataRow . askPrice && dataRow . bidPrice ) { return { askPrice : dataRow . askPrice , bidPrice : dataRow . bidPrice , } ; } } return undefined ; } ) ; export const allWebsocketBidAskPrices = createSelector ( [ table_instrument ] , ( data ) : SymbolPrices [ ] | undefined => data . map ( ( { symbol , askPrice , bidPrice } ) => ( { symbol : symbol as SYMBOL , askPrice : askPrice as number , bidPrice : bidPrice as number , } ) ) ) ; export const websocketCurrentPrice = createSelector ( [ websocketBidAskPrices , getTrailingOrderSide ] , ( bidAskPrices , side ) : number | undefined => ( side === SIDE . SELL ? bidAskPrices ?. askPrice : bidAskPrices ?. bidPrice ) ) ; export const websocketTrailingPriceSelector = createSelector ( [ websocketBidAskPrices , getTrailingOrderSide , getTrailingOrderSymbol ] , ( bidAskPrices , side , symbol ) : number | undefined => { if ( bidAskPrices ?. bidPrice && bidAskPrices . askPrice ) { const { decimal_rounding , ticksize } = INSTRUMENT_PARAMS [ symbol ] ; const tick = 1 / ticksize ; return side === SIDE . SELL ? parseNumber ( bidAskPrices . bidPrice + tick , decimal_rounding ) : parseNumber ( bidAskPrices . askPrice - tick , decimal_rounding ) ; } return undefined ; } ) ; export const websocketCrossPriceSelector = createSelector ( [ websocketBidAskPrices , getCrossOrderSide , getHasPriceCrossedOnce ] , ( bidAskPrices , side , hasPriceCrossedOnce ) : number | undefined => { if ( ! hasPriceCrossedOnce ) { return side === SIDE . SELL ? bidAskPrices ?. bidPrice : bidAskPrices ?. askPrice ; } else { return side === SIDE . SELL ? bidAskPrices ?. askPrice : bidAskPrices ?. bidPrice ; } } ) ; export const hasCrossedOnceSelector = createSelector ( [ getCrossOrderPrice , getCrossOrderSide , websocketCrossPriceSelector ] , ( orderPrice , side , currentPrice ) : boolean => { if ( currentPrice && ! ! orderPrice ) { return side === SIDE . BUY ? currentPrice < orderPrice : currentPrice > orderPrice ; } return false ; } ) ; export const hasCrossedSecondTimeSelector = createSelector ( [ getHasPriceCrossedOnce , getCrossOrderSide , getCrossOrderPrice , websocketCrossPriceSelector ] , ( hasPriceCrossedOnce , crossOrderSide , orderPrice , currentPrice ) : boolean => hasPriceCrossedOnce && ! ! currentPrice && ( crossOrderSide === SIDE . BUY ? currentPrice >= orderPrice : currentPrice <= orderPrice ) ) ; export const ordersAverageEntrySelector = createSelector ( [ getOrders , getShowPreview ] , ( orders , previewTable ) : number | void => { if ( previewTable && orders ?. length ) { const regularOrders = orders . filter ( ( order ) => ! ( 'stopPx' in order ) ) as RegularOrder [ ] ; const total_quantity = regularOrders . reduce ( ( total , n ) => total + n . orderQty , 0 ) ; const contract_value = regularOrders . reduce ( ( total , n ) => total + n . orderQty / n . price , 0 ) ; return Math . round ( ( total_quantity / contract_value ) * 10000 ) / 10000 ; } } ) ; export const ordersRiskSelector = createSelector ( [ getOrders , ordersAverageEntrySelector , getShowPreview ] , ( orderObject = [ ] , averageEntry , previewTable ) : number | undefined => { const stopOrder = orderObject . find ( ( order ) => 'stopPx' in order ) as StopLoss ; if ( previewTable && averageEntry > 0 && averageEntry && stopOrder && Object . keys ( stopOrder ) . length ) { let { orderQty } = stopOrder ; if ( stopOrder . symbol === SYMBOL . ETHUSD ) orderQty *= 1e-6 * averageEntry ** 2 ; if ( stopOrder . symbol === SYMBOL . XRPUSD ) orderQty *= 2e-4 * averageEntry ** 2 ; const entryValue = orderQty / averageEntry ; const exitValue = orderQty / stopOrder . stopPx ; return Math . abs ( + ( entryValue - exitValue ) . toFixed ( 5 ) ) ; } return undefined ; } ) ; export const balanceSelector = createSelector ( [ getBalance ] , ( balance ) : number | null => balance ? Math . round ( ( balance / 1e8 ) * 10000 ) / 10000 : null ) ; export const ordersRiskPercSelector = createSelector ( [ balanceSelector , ordersRiskSelector ] , ( balance , risk ) : number => balance !== 0 && balance !== null && risk !== undefined ? + ( ( risk / balance ) * 100 ) . toFixed ( 2 ) : 0 ) ; export const orderSelector = createSelector ( [ getOpenOrders , ( state , props ) => props . orderID ] , ( orders , orderID ) : Order | undefined => orders . find ( ( order ) => order . orderID === orderID ) ) ; export const groupedOrdersSelector = createSelector ( [ getProfitOrders ] , ( orders ) : Record < string , Order [ ] > => { const groupedOrders = { } ; orders . forEach ( ( order ) => { const openOrderId = order . text . split ( '.' ) [ 1 ] ; if ( ! ( openOrderId in groupedOrders ) ) groupedOrders [ openOrderId ] = [ order ] ; else groupedOrders [ openOrderId ] . push ( order ) ; } ) ; return groupedOrders ; } ) ;