@@ -58,9 +58,10 @@ const SLEEP_MS = 500;
5858const CU_EST_MULTIPLIER = 1.25 ;
5959
6060const FILTER_FOR_MARKET = undefined ; // undefined;
61- const EMPTY_USER_SETTLE_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour
62- const POSITIVE_PNL_SETTLE_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour
63- const ALL_NEGATIVE_PNL_SETTLE_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour
61+
62+ const EMPTY_USER_SETTLE_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour (after initial delay)
63+ const POSITIVE_PNL_SETTLE_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour (after initial delay)
64+ const ALL_NEGATIVE_PNL_SETTLE_INTERVAL_MS = 60 * 60 * 1000 ; // 1 hour (after initial delay)
6465const MIN_MARGIN_RATIO_FOR_POSITIVE_PNL = 0.1 ; // 10% of account value
6566
6667const errorCodesToSuppress = [
@@ -115,6 +116,7 @@ export class UserPnlSettlerBot implements Bot {
115116 // =============================================================================
116117
117118 private intervalIds : Array < NodeJS . Timer > = [ ] ;
119+ private timeoutIds : Array < NodeJS . Timeout > = [ ] ;
118120 private inProgress = false ;
119121 private watchdogTimerMutex = new Mutex ( ) ;
120122 private watchdogTimerLastPatTime = Date . now ( ) ;
@@ -182,6 +184,11 @@ export class UserPnlSettlerBot implements Bot {
182184 }
183185 this . intervalIds = [ ] ;
184186
187+ for ( const timeoutId of this . timeoutIds ) {
188+ clearTimeout ( timeoutId ) ;
189+ }
190+ this . timeoutIds = [ ] ;
191+
185192 await this . priorityFeeSubscriber ! . unsubscribe ( ) ;
186193 await this . userMap ?. unsubscribe ( ) ;
187194 }
@@ -205,26 +212,54 @@ export class UserPnlSettlerBot implements Bot {
205212 this . intervalIds . push (
206213 setInterval ( this . trySettleNegativePnl . bind ( this ) , negativeInterval )
207214 ) ;
208- // Comprehensive negative PnL settlement every hour
209- this . intervalIds . push (
210- setInterval (
211- this . trySettleAllNegativePnl . bind ( this ) ,
212- ALL_NEGATIVE_PNL_SETTLE_INTERVAL_MS
213- )
215+
216+ // Calculate delay until next hour's 1st minute for hourly tasks
217+ const delayUntilNextHourFirstMinute =
218+ this . getMillisecondsUntilNextHourFirstMinute ( ) ;
219+ const nextRunTime = new Date ( Date . now ( ) + delayUntilNextHourFirstMinute ) ;
220+ logger . info (
221+ `Hourly settlement tasks will start at ${ nextRunTime . toISOString ( ) } (in ${ Math . round (
222+ delayUntilNextHourFirstMinute / 1000
223+ ) } s)`
214224 ) ;
215- // Users with no positions every hour
216- this . intervalIds . push (
217- setInterval (
218- this . trySettleUsersWithNoPositions . bind ( this ) ,
219- EMPTY_USER_SETTLE_INTERVAL_MS
220- )
225+
226+ // Comprehensive negative PnL settlement - start at next hour's 1st minute, then every hour
227+ this . timeoutIds . push (
228+ setTimeout ( ( ) => {
229+ this . trySettleAllNegativePnl ( ) ;
230+ this . intervalIds . push (
231+ setInterval (
232+ this . trySettleAllNegativePnl . bind ( this ) ,
233+ ALL_NEGATIVE_PNL_SETTLE_INTERVAL_MS
234+ )
235+ ) ;
236+ } , delayUntilNextHourFirstMinute )
221237 ) ;
222- // Positive PnL settlement for low margin users every hour
223- this . intervalIds . push (
224- setInterval (
225- this . trySettlePositivePnlForLowMargin . bind ( this ) ,
226- POSITIVE_PNL_SETTLE_INTERVAL_MS
227- )
238+
239+ // Users with no positions - start at next hour's 1st minute, then every hour
240+ this . timeoutIds . push (
241+ setTimeout ( ( ) => {
242+ this . trySettleUsersWithNoPositions ( ) ;
243+ this . intervalIds . push (
244+ setInterval (
245+ this . trySettleUsersWithNoPositions . bind ( this ) ,
246+ EMPTY_USER_SETTLE_INTERVAL_MS
247+ )
248+ ) ;
249+ } , delayUntilNextHourFirstMinute )
250+ ) ;
251+
252+ // Positive PnL settlement for low margin users - start at next hour's 1st minute, then every hour
253+ this . timeoutIds . push (
254+ setTimeout ( ( ) => {
255+ this . trySettlePositivePnlForLowMargin ( ) ;
256+ this . intervalIds . push (
257+ setInterval (
258+ this . trySettlePositivePnlForLowMargin . bind ( this ) ,
259+ POSITIVE_PNL_SETTLE_INTERVAL_MS
260+ )
261+ ) ;
262+ } , delayUntilNextHourFirstMinute )
228263 ) ;
229264 }
230265 }
@@ -1074,6 +1109,13 @@ export class UserPnlSettlerBot implements Bot {
10741109 // UTILITY METHODS
10751110 // =============================================================================
10761111
1112+ private getMillisecondsUntilNextHourFirstMinute ( ) : number {
1113+ const now = new Date ( ) ;
1114+ const nextHour = new Date ( now ) ;
1115+ nextHour . setHours ( now . getHours ( ) + 1 , 1 , 0 , 0 ) ; // Next hour, 1st minute, 0 seconds, 0 ms
1116+ return nextHour . getTime ( ) - now . getTime ( ) ;
1117+ }
1118+
10771119 private async updateWatchdogTimer ( ) {
10781120 await this . watchdogTimerMutex . runExclusive ( async ( ) => {
10791121 this . watchdogTimerLastPatTime = Date . now ( ) ;
0 commit comments