@@ -13,7 +13,11 @@ const sandbox = process.env.NODE_ENV === "staging"
1313
1414// Used to track when the last buy / sell / borrow / repay occurred to allow for balance sync
1515// This should always be updated before and after the transaction is executed on Binance
16- let lastChangeTime = 0
16+ const lastChangeTimes : Dictionary < Number > = { }
17+
18+ // Cached balances for better performance, especially for the fall back wallet that is not used often
19+ const balances : Dictionary < ccxt . Balances > = { }
20+ const balanceTimestamps : Dictionary < Number > = { }
1721
1822if ( process . env . NODE_ENV !== "test" ) {
1923 binanceClient = new ccxt . binance ( {
@@ -56,13 +60,24 @@ export async function fetchBalance(type: WalletType): Promise<ccxt.Balances> {
5660 // Hack, as you can't look up margin balances on testnet (NotSupported error), but this is only for testing
5761 if ( sandbox ) type = WalletType . SPOT
5862
63+ // If a wallet has been touched then it will already be cleared from the cache
64+ if ( balances . hasOwnProperty ( type ) ) {
65+ // Check that the cached balances are less than 24 hours old
66+ const elapsed = Date . now ( ) - ( balanceTimestamps [ type ] as number )
67+ if ( elapsed >= 0 && elapsed < 24 * 60 * 60 * 1000 ) {
68+ // Use the cache
69+ logger . debug ( `Using cached ${ type } balances.` )
70+ return balances [ type ]
71+ }
72+ }
73+
5974 // According to Binance support it can take from 1 to 10 seconds for the balances to sync after making a trade
6075 // Therefore if there are a lot of signals happening at the same time it can give the wrong results, so we're just going to slow it down a bit
6176 // The recommended subscribing to the web socket for user updates, but that would be more work, and hopefully this won't happen too often
6277 // Just in case another trade happens while waiting, check again after waiting
63- while ( timeSinceLastChange ( ) < env ( ) . BALANCE_SYNC_DELAY ) {
78+ while ( timeSinceLastChange ( type ) < env ( ) . BALANCE_SYNC_DELAY ) {
6479 // Add an extra 10ms just so we don't go around again
65- const delay = ( env ( ) . BALANCE_SYNC_DELAY - timeSinceLastChange ( ) ) + 10
80+ const delay = ( env ( ) . BALANCE_SYNC_DELAY - timeSinceLastChange ( type ) ) + 10
6681
6782 logger . debug ( `Waiting ${ delay } milliseconds to allow balances to synchronise.` )
6883
@@ -112,65 +127,83 @@ export function getMarginLoans(marginBalance: ccxt.Balances): Dictionary<Loan> {
112127 }
113128}
114129
115- // Calculate how much time has elapsed since the balances were changed in Binance
116- function timeSinceLastChange ( ) : number {
130+ // Calculate how much time has elapsed since the balances were changed in Binance for the specified wallet
131+ function timeSinceLastChange ( type : WalletType ) : number {
117132 const now = Date . now ( )
118133
134+ // Initialise
135+ if ( ! lastChangeTimes . hasOwnProperty ( type ) ) lastChangeTimes [ type ] = 0
136+
119137 // Maybe a time update, so we can't trust it anymore
120- if ( now < lastChangeTime ) lastChangeTime = 0
138+ if ( now < lastChangeTimes [ type ] ) lastChangeTimes [ type ] = 0
121139
122- return now - lastChangeTime
140+ return now - ( lastChangeTimes [ type ] as number )
123141}
124142
143+ // Update the time of last change and reset the balances for a specified wallet
144+ function balanceChanged ( type : WalletType ) {
145+ lastChangeTimes [ type ] = Date . now ( )
146+
147+ if ( balances . hasOwnProperty ( type ) ) {
148+ delete balances [ type ]
149+ delete balanceTimestamps [ type ]
150+ }
151+ }
152+
153+ // Places a market order for a symbol pair on the specified wallet
125154export async function createMarketOrder (
126155 symbol : string ,
127156 side : "buy" | "sell" ,
128157 amount : BigNumber ,
129- price ?: BigNumber ,
130- params ?: ccxt . Params
158+ walletType : WalletType ,
159+ price ?: BigNumber
131160) : Promise < ccxt . Order > {
132161 if ( ! binanceClient ) return Promise . reject ( logBinanceUndefined )
133- lastChangeTime = Date . now ( )
162+ balanceChanged ( walletType )
134163 return binanceClient . createMarketOrder (
135164 symbol ,
136165 side ,
137166 amount . toNumber ( ) ,
138167 price ?. toNumber ( ) ,
139- params
168+ {
169+ type : walletType
170+ }
140171 ) . then ( ( result ) => {
141- lastChangeTime = Date . now ( )
172+ balanceChanged ( walletType )
142173 return result
143174 } )
144175}
145176
177+ // Borrows an asset on the cross margin wallet
146178export async function marginBorrow (
147179 asset : string ,
148180 amount : BigNumber
149181) : Promise < LoanTransaction > {
150182 if ( ! binanceClient ) return Promise . reject ( logBinanceUndefined )
151- lastChangeTime = Date . now ( )
183+ balanceChanged ( WalletType . MARGIN )
152184 return binanceClient . sapiPostMarginLoan ( {
153185 asset,
154186 //isIsolated: 'FALSE', // "FALSE" for cross margin borrow without specification of a symbol.
155187 amount : amount . toFixed ( )
156188 } ) . then ( ( result : LoanTransaction ) => {
157- lastChangeTime = Date . now ( )
189+ balanceChanged ( WalletType . MARGIN )
158190 return result
159191 } )
160192}
161193
194+ // Repays an asset on the cross margin wallet
162195export async function marginRepay (
163196 asset : string ,
164197 amount : BigNumber
165198) : Promise < LoanTransaction > {
166199 if ( ! binanceClient ) return Promise . reject ( logBinanceUndefined )
167- lastChangeTime = Date . now ( )
200+ balanceChanged ( WalletType . MARGIN )
168201 return binanceClient . sapiPostMarginRepay ( {
169202 asset,
170203 //isIsolated: 'FALSE', // "FALSE" for cross margin repay without specification of a symbol.
171204 amount : amount . toFixed ( )
172205 } ) . then ( ( result : LoanTransaction ) => {
173- lastChangeTime = Date . now ( )
206+ balanceChanged ( WalletType . MARGIN )
174207 return result
175208 } )
176209}
0 commit comments