@@ -12,6 +12,8 @@ const STRATEGY_PROFILES_KEY = "strategy_profiles";
1212const AUDIT_LOG_KEY = "audit_log" ;
1313const AUDIT_LOG_LIMIT = 50 ;
1414const CURRENT_STRATEGIES_TIMEOUT_MS = 25000 ;
15+ const CURRENT_STRATEGIES_CACHE_KEY = "current_strategies_cache" ;
16+ const CURRENT_STRATEGIES_CACHE_TTL_MS = 60_000 ;
1517const GITHUB_API_TIMEOUT_MS = 8000 ;
1618
1719const SUPPORTED_PLATFORMS = [ "longbridge" , "ibkr" , "schwab" , "firstrade" , "qmt" , "binance" ] ;
@@ -579,12 +581,30 @@ async function configPayload(request, env) {
579581 if ( ! session ?. allowed ) return { accountOptions : null , platformMeta : meta } ;
580582 const accountConfig = await loadAccountOptionsConfig ( env ) ;
581583 const strategyProfiles = await loadStrategyProfilesConfig ( env ) ;
584+
585+ let currentStrategies = null ;
586+ if ( hasConfigStore ( env ) ) {
587+ const cached = await readConfigJson ( env , CURRENT_STRATEGIES_CACHE_KEY ) ;
588+ if ( cached ?. ts && ( Date . now ( ) - cached . ts ) < CURRENT_STRATEGIES_CACHE_TTL_MS && cached . data ) {
589+ currentStrategies = cached . data ;
590+ }
591+ }
592+ if ( ! currentStrategies ) {
593+ currentStrategies = await loadCurrentStrategiesSafely ( accountConfig . options , env ) ;
594+ if ( hasConfigStore ( env ) ) {
595+ // fire-and-forget: don't block the response on cache write
596+ writeConfigJson ( env , CURRENT_STRATEGIES_CACHE_KEY , {
597+ ts : Date . now ( ) ,
598+ data : currentStrategies ,
599+ } ) . catch ( ( ) => { } ) ;
600+ }
601+ }
582602 return {
583603 accountOptions : accountConfig . options ,
584604 platformRepositories : platformRepositories ( env ) ,
585605 platformMeta : meta ,
586606 strategyProfiles,
587- currentStrategies : await loadCurrentStrategiesSafely ( accountConfig . options , env ) ,
607+ currentStrategies,
588608 } ;
589609}
590610
@@ -616,8 +636,8 @@ async function loadCurrentStrategies(accountOptions, env) {
616636 for ( const platform of SUPPORTED_PLATFORMS ) {
617637 const platformStrategies = await loadStrategiesForPlatform ( platform , accountOptions , repositories , readVariable ) ;
618638 if ( Object . keys ( platformStrategies ) . length ) currentStrategies [ platform ] = platformStrategies ;
619- // 500ms gap between platforms to respect GitHub secondary rate limit
620- await new Promise ( ( r ) => setTimeout ( r , 500 ) ) ;
639+ // 100ms gap between platforms to respect GitHub secondary rate limit
640+ await new Promise ( ( r ) => setTimeout ( r , 100 ) ) ;
621641 }
622642 return currentStrategies ;
623643}
@@ -772,16 +792,27 @@ async function resolveCurrentStrategyForAccount({ platform, option, optionsCount
772792 githubEnvironment,
773793 readVariable,
774794 } ) ;
775- // Await sequentially to avoid bursting ~10 concurrent GitHub API calls
776- // per account that would trigger secondary rate limiting.
777- const reservedCashPayload = await reservedCashPayloadPromise ;
778- const incomeLayerPayload = await incomeLayerPayloadPromise ;
779- const optionOverlayPayload = await optionOverlayPayloadPromise ;
780- const runtimeTargetEnabledPayload = await runtimeTargetEnabledPayloadPromise ;
781- const dcaPayload = await dcaPayloadPromise ;
782- const ibitZscorePayload = await ibitZscorePayloadPromise ;
783- const cashOnlyPayload = await cashOnlyPayloadPromise ;
784- const runtimeTargetValue = await readVariable ( repository , variableScope , githubEnvironment , "RUNTIME_TARGET_JSON" ) ;
795+ // Await in parallel: each reads a different variable so
796+ // there is no risk of hammering the same GitHub API endpoint.
797+ const [
798+ reservedCashPayload ,
799+ incomeLayerPayload ,
800+ optionOverlayPayload ,
801+ runtimeTargetEnabledPayload ,
802+ dcaPayload ,
803+ ibitZscorePayload ,
804+ cashOnlyPayload ,
805+ runtimeTargetValue ,
806+ ] = await Promise . all ( [
807+ reservedCashPayloadPromise ,
808+ incomeLayerPayloadPromise ,
809+ optionOverlayPayloadPromise ,
810+ runtimeTargetEnabledPayloadPromise ,
811+ dcaPayloadPromise ,
812+ ibitZscorePayloadPromise ,
813+ cashOnlyPayloadPromise ,
814+ readVariable ( repository , variableScope , githubEnvironment , "RUNTIME_TARGET_JSON" ) ,
815+ ] ) ;
785816 const runtimeTarget = parseJsonObject ( runtimeTargetValue ) ;
786817 const runtimeTargetMatches = runtimeTarget && runtimeTargetMatchesAccount ( runtimeTarget , platform , option ) ;
787818 const runtimeTargetProfile = runtimeTargetMatches ? cleanCurrentStrategy ( runtimeTarget . strategy_profile ) : "" ;
0 commit comments