1- import type { LiveSeriesPriceSnapshot , PersistedLivePrice } from '../_utils/eventLiveSeriesChartUtils'
1+ import type {
2+ LiveSeriesPriceSnapshot ,
3+ LiveSeriesPriceSnapshotStatus ,
4+ PersistedLivePrice ,
5+ } from '../_utils/eventLiveSeriesChartUtils'
26import type { EventLiveChartConfig } from '@/types'
3- import { useCallback , useMemo , useState , useSyncExternalStore } from 'react'
7+ import { useCallback , useMemo , useSyncExternalStore } from 'react'
48import {
59 LIVE_DATA_RETENTION_MS ,
610 normalizeLiveChartPrice ,
@@ -17,13 +21,13 @@ interface UseLiveSeriesPriceSnapshotOptions {
1721
1822export interface LiveSeriesPriceSnapshotResult {
1923 referenceSnapshot : LiveSeriesPriceSnapshot | null
20- baselinePrice : number | null
21- setBaselinePrice : React . Dispatch < React . SetStateAction < number | null > >
24+ referenceSnapshotStatus : LiveSeriesPriceSnapshotStatus
2225 persistedFallbackPrice : PersistedLivePrice | null
2326}
2427
2528interface LiveSeriesPriceSnapshotStoreSnapshot {
2629 referenceSnapshot : LiveSeriesPriceSnapshot | null
30+ referenceSnapshotStatus : LiveSeriesPriceSnapshotStatus
2731 persistedFallbackPrice : PersistedLivePrice | null
2832}
2933
@@ -47,8 +51,12 @@ interface LiveSeriesPriceSnapshotRequest {
4751const liveSeriesPriceSnapshotStores = new Map < string , LiveSeriesPriceSnapshotStoreEntry > ( )
4852const liveSeriesPriceStorageKeyPrefix = 'kuest-live-last-price'
4953const LIVE_SERIES_PRICE_SNAPSHOT_STORE_TTL_MS = 10 * 60 * 1000
54+ const BINANCE_CLOSE_FIRST_REFRESH_DELAY_MS = 65 * 1000
55+ const BINANCE_CLOSE_REFRESH_INTERVAL_MS = 10 * 1000
56+ const BINANCE_CLOSE_REFRESH_WINDOW_MS = 5 * 60 * 1000
5057const EMPTY_LIVE_SERIES_PRICE_SNAPSHOT : LiveSeriesPriceSnapshotStoreSnapshot = {
5158 referenceSnapshot : null ,
59+ referenceSnapshotStatus : 'loading' ,
5260 persistedFallbackPrice : null ,
5361}
5462
@@ -199,6 +207,13 @@ async function fetchLiveSeriesPriceSnapshot(
199207 const requestToken = entry . fetchToken + 1
200208 entry . fetchToken = requestToken
201209 entry . abortController = controller
210+ if ( entry . snapshot . referenceSnapshot == null ) {
211+ entry . snapshot = {
212+ ...entry . snapshot ,
213+ referenceSnapshotStatus : 'loading' ,
214+ }
215+ notifyLiveSeriesPriceSnapshotStore ( storeKey )
216+ }
202217 entry . inflightFetch = ( async function runLiveSeriesPriceSnapshotFetch ( ) {
203218 try {
204219 const response = await fetch ( `/api/price-reference/live-series?${ buildLiveSeriesPriceSnapshotQuery ( request ) . toString ( ) } ` , {
@@ -207,13 +222,18 @@ async function fetchLiveSeriesPriceSnapshot(
207222 } )
208223
209224 if ( ! response . ok ) {
225+ entry . snapshot = {
226+ ...entry . snapshot ,
227+ referenceSnapshotStatus : 'unavailable' ,
228+ }
210229 return
211230 }
212231
213232 const payload = await response . json ( ) as LiveSeriesPriceSnapshot
214233 entry . snapshot = {
215234 ...entry . snapshot ,
216235 referenceSnapshot : payload ,
236+ referenceSnapshotStatus : 'ready' ,
217237 }
218238
219239 const fallbackPrice = normalizeLiveChartPrice (
@@ -236,6 +256,10 @@ async function fetchLiveSeriesPriceSnapshot(
236256 }
237257 }
238258 catch {
259+ entry . snapshot = {
260+ ...entry . snapshot ,
261+ referenceSnapshotStatus : 'unavailable' ,
262+ }
239263 }
240264 finally {
241265 if ( entry . fetchToken === requestToken ) {
@@ -260,12 +284,46 @@ function subscribeToLiveSeriesPriceSnapshot(
260284 const storeKey = buildLiveSeriesPriceSnapshotStoreKey ( request )
261285 const entry = getLiveSeriesPriceSnapshotStoreEntry ( storeKey )
262286 entry . listeners . add ( onStoreChange )
287+ let binanceCloseRefreshTimer : ReturnType < typeof setTimeout > | null = null
288+ let isSubscribed = true
263289
264290 if ( syncPersistedLivePriceSnapshot ( storeKey , request ) ) {
265291 onStoreChange ( )
266292 }
267293 void fetchLiveSeriesPriceSnapshot ( storeKey , request )
268294
295+ async function refreshBinanceCloseUntilAvailable ( ) {
296+ if ( ! isSubscribed ) {
297+ return
298+ }
299+
300+ await fetchLiveSeriesPriceSnapshot ( storeKey , request )
301+ const snapshot = entry . snapshot . referenceSnapshot
302+ const eventEndTimestamp = request . explicitEndTimestamp
303+ if ( ! isSubscribed
304+ || ( snapshot != null && snapshot . source !== 'binance' )
305+ || snapshot ?. closing_price != null
306+ || eventEndTimestamp == null
307+ || Date . now ( ) >= eventEndTimestamp + BINANCE_CLOSE_REFRESH_WINDOW_MS ) {
308+ return
309+ }
310+
311+ binanceCloseRefreshTimer = setTimeout (
312+ refreshBinanceCloseUntilAvailable ,
313+ BINANCE_CLOSE_REFRESH_INTERVAL_MS ,
314+ )
315+ }
316+
317+ if ( request . explicitEndTimestamp != null ) {
318+ const firstRefreshAtMs = request . explicitEndTimestamp + BINANCE_CLOSE_FIRST_REFRESH_DELAY_MS
319+ if ( Date . now ( ) < request . explicitEndTimestamp + BINANCE_CLOSE_REFRESH_WINDOW_MS ) {
320+ binanceCloseRefreshTimer = setTimeout (
321+ refreshBinanceCloseUntilAvailable ,
322+ Math . max ( 0 , firstRefreshAtMs - Date . now ( ) ) ,
323+ )
324+ }
325+ }
326+
269327 function refreshSnapshotAfterResume ( ) {
270328 if ( document . hidden ) {
271329 return
@@ -307,6 +365,10 @@ function subscribeToLiveSeriesPriceSnapshot(
307365 document . addEventListener ( 'visibilitychange' , handleVisibilityChange )
308366
309367 return function unsubscribeFromLiveSeriesPriceSnapshot ( ) {
368+ isSubscribed = false
369+ if ( binanceCloseRefreshTimer ) {
370+ clearTimeout ( binanceCloseRefreshTimer )
371+ }
310372 entry . listeners . delete ( onStoreChange )
311373 if ( entry . listeners . size === 0 && entry . abortController ) {
312374 entry . fetchToken += 1
@@ -385,20 +447,9 @@ export function useLiveSeriesPriceSnapshot({
385447 getServerSnapshot ,
386448 )
387449
388- const [ baselinePrice , setBaselinePrice ] = useState < number | null > ( null )
389-
390- const effectiveBaselinePrice = baselinePrice ?? (
391- typeof referenceSnapshot . referenceSnapshot ?. opening_price === 'number'
392- && Number . isFinite ( referenceSnapshot . referenceSnapshot . opening_price )
393- && referenceSnapshot . referenceSnapshot . opening_price > 0
394- ? referenceSnapshot . referenceSnapshot . opening_price
395- : null
396- )
397-
398450 return {
399451 referenceSnapshot : referenceSnapshot . referenceSnapshot ,
400- baselinePrice : effectiveBaselinePrice ,
401- setBaselinePrice,
452+ referenceSnapshotStatus : referenceSnapshot . referenceSnapshotStatus ,
402453 persistedFallbackPrice : referenceSnapshot . persistedFallbackPrice ,
403454 }
404455}
0 commit comments