Skip to content

Commit 129c0c9

Browse files
committed
Stabilize strategy switch current-state reads
1 parent cb64eb6 commit 129c0c9

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

tests/strategy_switch_worker_validation.mjs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import worker, { __test } from "../web/strategy-switch-console/worker.js";
77

88
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
99
const indexHtml = readFileSync(resolve(root, "web/strategy-switch-console/index.html"), "utf8");
10+
assert.ok(__test.currentStrategiesTimeoutMs >= 8000);
1011
const renderPlatformsBody = indexHtml.match(/function renderPlatforms\(\) \{([\s\S]*?)\n \}/)?.[1] || "";
1112
assert.ok(!renderPlatformsBody.includes("syncStrategyForAccount("));
1213
assert.equal(indexHtml.includes(".innerHTML"), false);
@@ -406,6 +407,59 @@ try {
406407
globalThis.fetch = originalFetch;
407408
}
408409

410+
let releaseReservedVariables;
411+
let reservedVariableRequests = 0;
412+
let reservedVariablesFinished = false;
413+
let runtimeTargetStartedBeforeReservedVariablesFinished = false;
414+
const reservedVariablesGate = new Promise((resolve) => {
415+
releaseReservedVariables = () => {
416+
reservedVariablesFinished = true;
417+
resolve();
418+
};
419+
});
420+
const reservedVariableFallback = setTimeout(releaseReservedVariables, 100);
421+
globalThis.fetch = async (url) => {
422+
const requestUrl = String(url);
423+
if (requestUrl.endsWith("/CLOUD_RUN_SERVICE_TARGETS_JSON")) {
424+
return new Response("", { status: 404 });
425+
}
426+
if (requestUrl.endsWith("/SCHWAB_MIN_RESERVED_CASH_USD") || requestUrl.endsWith("/SCHWAB_RESERVED_CASH_RATIO")) {
427+
reservedVariableRequests += 1;
428+
await reservedVariablesGate;
429+
return new Response(JSON.stringify({ value: requestUrl.endsWith("/SCHWAB_RESERVED_CASH_RATIO") ? "0.03" : "150" }), {
430+
status: 200,
431+
headers: { "Content-Type": "application/json" },
432+
});
433+
}
434+
if (requestUrl.endsWith("/RUNTIME_TARGET_JSON")) {
435+
runtimeTargetStartedBeforeReservedVariablesFinished = reservedVariableRequests === 2 && !reservedVariablesFinished;
436+
releaseReservedVariables();
437+
return new Response(JSON.stringify({
438+
value: JSON.stringify({
439+
platform_id: "schwab",
440+
strategy_profile: "soxl_soxx_trend_income",
441+
dry_run_only: false,
442+
account_scope: "schwab",
443+
service_name: "charles-schwab-quant-service",
444+
execution_mode: "live",
445+
}),
446+
}), { status: 200, headers: { "Content-Type": "application/json" } });
447+
}
448+
return new Response("", { status: 404 });
449+
};
450+
try {
451+
const currentStrategies = await __test.loadCurrentStrategies(
452+
{ schwab: accountOptions.schwab },
453+
{ RUNTIME_SETTINGS_DISPATCH_TOKEN: "test-token" },
454+
);
455+
assert.equal(currentStrategies.schwab.default.min_reserved_cash_usd, "150");
456+
assert.equal(currentStrategies.schwab.default.reserved_cash_ratio, "0.03");
457+
assert.equal(runtimeTargetStartedBeforeReservedVariablesFinished, true);
458+
} finally {
459+
clearTimeout(reservedVariableFallback);
460+
globalThis.fetch = originalFetch;
461+
}
462+
409463
const longbridgeHk = __test.assertConfiguredAccount(
410464
{
411465
platform: "longbridge",

web/strategy-switch-console/worker.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const ACCOUNT_OPTIONS_KEY = "account_options";
1111
const STRATEGY_PROFILES_KEY = "strategy_profiles";
1212
const AUDIT_LOG_KEY = "audit_log";
1313
const AUDIT_LOG_LIMIT = 50;
14-
const CURRENT_STRATEGIES_TIMEOUT_MS = 3500;
14+
const CURRENT_STRATEGIES_TIMEOUT_MS = 10000;
1515
const GITHUB_API_TIMEOUT_MS = 8000;
1616

1717
const SUPPORTED_PLATFORMS = ["longbridge", "ibkr", "schwab", "firstrade"];
@@ -594,14 +594,18 @@ async function resolveCurrentStrategyForAccount({ platform, option, optionsCount
594594

595595
const variableScope = resolveVariableScope(platform, option);
596596
const githubEnvironment = resolveGithubEnvironment(platform, option, variableScope);
597-
const reservedCashPayload = await readReservedCashVariables({
597+
const reservedCashPayloadPromise = readReservedCashVariables({
598598
platform,
599599
repository,
600600
variableScope,
601601
githubEnvironment,
602602
readVariable,
603603
});
604-
const runtimeTargetValue = await readVariable(repository, variableScope, githubEnvironment, "RUNTIME_TARGET_JSON");
604+
const runtimeTargetValuePromise = readVariable(repository, variableScope, githubEnvironment, "RUNTIME_TARGET_JSON");
605+
const [reservedCashPayload, runtimeTargetValue] = await Promise.all([
606+
reservedCashPayloadPromise,
607+
runtimeTargetValuePromise,
608+
]);
605609
const runtimeTarget = parseJsonObject(runtimeTargetValue);
606610
const runtimeTargetMatches = runtimeTarget && runtimeTargetMatchesAccount(runtimeTarget, platform, option);
607611
const runtimeTargetProfile = runtimeTargetMatches ? cleanCurrentStrategy(runtimeTarget.strategy_profile) : "";
@@ -1805,6 +1809,7 @@ function escapeHtml(value) {
18051809

18061810
export const __test = {
18071811
assertConfiguredAccount,
1812+
currentStrategiesTimeoutMs: CURRENT_STRATEGIES_TIMEOUT_MS,
18081813
assertStrategyAllowedForAccount,
18091814
inferAccountSupportedDomains,
18101815
loadCurrentStrategies,

0 commit comments

Comments
 (0)