import { dbDriver , OrderWithTrades , wsServer } from '@lib' ; import { SessionStore } from './session.store' ; import { Order } from 'ccxt' ; import { StoreOpts } from '@m8bTypes' ; const singleton = Symbol ( ) ; interface Opts { exchange : string ; symbol : string ; strategy : string ; } export enum ORDER_STATE { PENDING = 'pending' , PENDING_CANCEL = 'pending_cancel' , CANCELED = 'canceled' , DONE = 'done' } export class OrderStore { public static get instance ( ) : OrderStore { if ( ! this [ singleton ] ) this [ singleton ] = new OrderStore ( ) ; return this [ singleton ] ; } private openOrders : Map < string , Map < string , OrderWithTrades > > = new Map ( ) ; private orderStates : Map < string , Map < string , ORDER_STATE > > = new Map ( ) ; private readonly sessionId = SessionStore . instance . sessionId ; private constructor ( ) { } public addSymbol ( storeOpts ) { const idStr = this . makeIdStr ( storeOpts ) ; this . openOrders . set ( idStr , new Map ( ) ) ; this . orderStates . set ( idStr , new Map ( ) ) ; } public async newOrder ( { exchange , symbol , strategy } , order ) { const idStr = this . makeIdStr ( { exchange , symbol , strategy } ) ; this . openOrders . get ( idStr ) . set ( order . id , order as OrderWithTrades ) ; this . orderStates . get ( idStr ) . set ( order . id , ORDER_STATE . PENDING ) ; const { sessionId } = this ; const data = { ... order , sessionId , exchange , symbol , strategy } ; await dbDriver . order . insertOne ( data ) ; wsServer . broadcast ( 'order-new' , data ) ; } public getOpenOrder ( storeOpts , id ) { const idStr = this . makeIdStr ( storeOpts ) ; return this . openOrders . get ( idStr ) . get ( id ) ; } public closeOpenOrder ( storeOpts , id ) { const idStr = this . makeIdStr ( storeOpts ) ; this . openOrders . get ( idStr ) . delete ( id ) ; } public updateOrder ( storeOpts , order ) { const idStr = this . makeIdStr ( storeOpts ) ; if ( order . status !== 'open' ) this . updateOrderState ( storeOpts , order . id , order . status === 'closed' ? ORDER_STATE . DONE : ORDER_STATE . CANCELED ) ; this . openOrders . get ( idStr ) . set ( order . id , order ) ; } public getAllPendingOrders ( storeOpts ) { const idStr = this . makeIdStr ( storeOpts ) ; return Array . from ( this . orderStates . get ( idStr ) . entries ( ) ) . filter ( ( [ id , state ] ) => state === ORDER_STATE . PENDING ) . map ( ( [ id ] ) => id ) ; } public getOrderState ( storeOpts , id ) { const idStr = this . makeIdStr ( storeOpts ) ; return this . orderStates . get ( idStr ) . get ( id ) ; } public updateOrderState ( storeOpts , id , state ) { const idStr = this . makeIdStr ( storeOpts ) ; this . orderStates . get ( idStr ) . set ( id , state ) ; } public async saveOrder ( storeOpts , order ) { const { exchange } = storeOpts ; const { id , ... updatedOrder } = order ; await dbDriver . order . updateOne ( { id , exchange } , { $set : { ... updatedOrder } } ) ; wsServer . broadcast ( 'order-update' , { ... order , ... storeOpts } ) ; } private makeIdStr ( { exchange , symbol , strategy } ) { return `${ exchange }.${ symbol }.${ strategy }` ; } }