import ibkr , { EventName } from '@stoqey/ib' ; import isEmpty from 'lodash/isEmpty' ; import AccountSummary from '../account/AccountSummary' ; import { IBKREVENTS , IbkrEvents } from '../events' ; import { publishDataToTopic } from '../events/IbkrEvents.publisher' ; import IBKRConnection from '../connection/IBKRConnection' ; import { PortFolioUpdate } from './portfolios.interfaces' ; import { IBKRAccountSummary } from '../account/account-summary.interfaces' ; import { log , verbose } from '../log' ; import { getRadomReqId } from '../_utils/text.utils' ; const appEvents = IbkrEvents . Instance ; param interface const logPortfolio = ( { marketPrice , averageCost , position , symbol , conId } ) => { const contractIdWithSymbol = `${ symbol } ${ conId }` ; if ( Math . round ( marketPrice ) > Math . round ( averageCost ) ) { verbose ( `logPortfolio:profit shares = ${ position }, costPerShare -> ${ averageCost } marketPrice -> ${ marketPrice } ` , contractIdWithSymbol ) ; } else { log ( `logPortfolio:LOSS shares = ${ position }, costPerShare -> ${ averageCost } marketPrice -> ${ marketPrice }` , contractIdWithSymbol ) ; } } ; export class Portfolios { ib : ibkr ; accountSummary : IBKRAccountSummary ; private static _instance : Portfolios ; currentPortfolios : PortFolioUpdate [ ] = [ ] ; portfoliosSnapshot : PortFolioUpdate [ ] = [ ] ; public static get Instance ( ) : Portfolios { return this . _instance || ( this . _instance = new this ( ) ) ; } private constructor ( ) { } public init = async ( ) : Promise < void > => { const self = this ; this . ib = IBKRConnection . Instance . getIBKR ( ) ; const accountId = AccountSummary . Instance . AccountId ; const ib = this . ib ; ib . on ( EventName . updatePortfolio , ( contract , position , marketPrice , marketValue , averageCost , unrealizedPNL , realizedPNL , accountName ) => { const thisPortfolio = { ... contract , position , marketPrice , marketValue , averageCost , unrealizedPNL , realizedPNL , accountName , } ; logPortfolio ( thisPortfolio as any ) ; self . getPortfolios ( ) ; } ) ; ib . on ( EventName . openOrder , function ( orderId , contract , order , orderState ) { if ( orderState . status === 'Filled' ) { const existingPortfolio = self . currentPortfolios . find ( ( porto ) => porto . symbol === contract . symbol ) ; if ( isEmpty ( existingPortfolio ) ) { self . currentPortfolios . push ( { ... contract , } as any ) ; } else { self . currentPortfolios = self . currentPortfolios . filter ( ( porto ) => porto . symbol !== contract . symbol ) ; } log ( `Portfolio > openOrder FILLED` , ` -> ${ contract . symbol } ${ order . action } ${ order . totalQuantity }  ${ orderState . status }` ) ; verbose ( `Portfolio > ALL PORTFOLIOS` , ` -> ${ JSON . stringify ( self . currentPortfolios . map ( ( por ) => por . symbol ) ) }` ) ; self . getPortfolios ( ) ; } } ) ; log ( 'AccountID' , accountId ) ; ib . reqAccountUpdates ( true , accountId ) ; } ; /**
     * getPortfolios
     */ public getPortfolios = async ( options ? ) : Promise < PortFolioUpdate [ ] > => { const self = this ; const ib = self . ib ; const reqId = getRadomReqId ( ) ; return new Promise ( ( resolve ) => { let done = false ; const portfolios = { } ; const handlePosition = ( account , contract , position , averageCost ) => { const thisPortfolio = { ... contract , position , averageCost } ; let symbol = contract && contract . symbol ; if ( [ 'CASH' , 'OPT' ] . includes ( contract . secType ) ) { symbol = contract && contract . localSymbol ; } if ( position === 0 ) { return ; } thisPortfolio . symbol = symbol ; portfolios [ symbol ] = thisPortfolio ; } ; const handlePositionEnd = ( portfoliosData ) => { if ( ! done ) { done = true ; appEvents . off ( 'position' , handlePosition ) ; verbose ( 'getPortfolios positionEnd' , `********************** =` , portfoliosData && portfoliosData . length ) ; self . currentPortfolios = portfoliosData ; publishDataToTopic ( { topic : IBKREVENTS . PORTFOLIOS , data : portfoliosData , } ) ; resolve ( portfoliosData ) ; } } ; const positionEnd = ( ) => { if ( done ) { return ; } const portfoliosData = Object . keys ( portfolios ) . map ( ( portfolioKey ) => portfolios [ portfolioKey ] ) ; handlePositionEnd ( portfoliosData ) ; ib . off ( EventName . positionEnd , positionEnd ) ; } ; ib . on ( EventName . positionEnd , positionEnd ) ; ib . on ( EventName . position , handlePosition ) ; if ( options ) { const { accountId , modelCode } = options ; return ib . reqPositionsMulti ( reqId , accountId , modelCode ) ; } ib . reqPositions ( ) ; } ) ; } ; } export default Portfolios ;