From 6915fb7a7ab086d76d4d19e25a0d21bf200085bc Mon Sep 17 00:00:00 2001
From: Pigbibi <20649888+Pigbibi@users.noreply.github.com>
Date: Tue, 9 Jun 2026 13:04:00 +0800
Subject: [PATCH] Sync switch execution mode display
---
web/strategy-switch-console/index.html | 59 ++++++++++++++++++++---
web/strategy-switch-console/page_asset.js | 2 +-
web/strategy-switch-console/worker.js | 43 +++++++++++++++--
3 files changed, 91 insertions(+), 13 deletions(-)
diff --git a/web/strategy-switch-console/index.html b/web/strategy-switch-console/index.html
index 697576b..75f80ce 100644
--- a/web/strategy-switch-console/index.html
+++ b/web/strategy-switch-console/index.html
@@ -681,7 +681,7 @@
切换摘要
strategy: "策略",
mode: "模式",
live: "实盘",
- paper: "模拟",
+ paper: "Dry run",
summary: "切换摘要",
copySummary: "复制摘要",
loginToRun: "登录后切换",
@@ -718,7 +718,7 @@ 切换摘要
strategy: "Strategy",
mode: "Mode",
live: "Live",
- paper: "Paper",
+ paper: "Dry run",
summary: "Switch Summary",
copySummary: "Copy summary",
loginToRun: "Sign in to switch",
@@ -827,18 +827,57 @@ 切换摘要
return strategyLabels[profile] || profile;
}
- function currentStrategyForAccount(platform, account) {
+ function modeLabel(mode) {
+ return mode === "paper" ? t("paper") : t("live");
+ }
+
+ function currentEntryForAccount(platform, account) {
const byPlatform = state.currentStrategies[platform] || {};
const keys = [account?.key, account?.target_name, account?.label]
.filter(Boolean)
.map((value) => String(value));
for (const key of keys) {
- const profile = cleanStrategyProfile(byPlatform[key]?.strategy_profile);
- if (profile) return profile;
+ const entry = byPlatform[key];
+ const profile = cleanStrategyProfile(entry?.strategy_profile);
+ if (profile) return entry;
}
+ return null;
+ }
+
+ function currentStrategyForAccount(platform, account) {
+ const entry = currentEntryForAccount(platform, account);
+ const profile = cleanStrategyProfile(entry?.strategy_profile);
+ return profile || "";
+ }
+
+ function normalizeExecutionMode(value, dryRunOnly) {
+ const mode = String(value || "").trim().toLowerCase();
+ if (mode === "live" || mode === "paper") return mode;
+ if (dryRunOnly === true || dryRunOnly === "true" || dryRunOnly === "1" || dryRunOnly === 1) return "paper";
+ if (dryRunOnly === false || dryRunOnly === "false" || dryRunOnly === "0" || dryRunOnly === 0) return "live";
return "";
}
+ function defaultExecutionModeForAccount(platform, account, fallback = "live") {
+ const currentMode = normalizeExecutionMode(
+ currentEntryForAccount(platform, account)?.execution_mode,
+ currentEntryForAccount(platform, account)?.dry_run_only,
+ );
+ if (currentMode) return currentMode;
+ const hint = [
+ account?.key,
+ account?.label,
+ account?.target_name,
+ account?.deployment_selector,
+ account?.account_scope,
+ account?.service_name,
+ ].join(" ").toLowerCase();
+ if (hint.split(/\s+/).includes("paper") || hint.includes("-paper") || hint.includes("_paper")) {
+ return "paper";
+ }
+ return fallback;
+ }
+
function defaultStrategyForAccount(platform, account, fallback = "tqqq_growth_income") {
const currentProfile = currentStrategyForAccount(platform, account);
if (currentProfile) return currentProfile;
@@ -862,6 +901,10 @@ 切换摘要
const account = selectedAccount(platform);
if (!account) return;
state.forms[platform].strategy = defaultStrategyForAccount(platform, account, state.forms[platform].strategy);
+ state.forms[platform].executionMode = defaultExecutionModeForAccount(
+ platform,
+ account,
+ );
}
function ensureAccountSelection(platform) {
@@ -923,7 +966,7 @@ 切换摘要
[t("repository"), repositories[state.selected]],
[t("selectedAccount"), account.label],
[t("selectedStrategy"), strategyLabel(inputs.strategy_profile)],
- [t("selectedMode"), inputs.execution_mode],
+ [t("selectedMode"), modeLabel(inputs.execution_mode)],
[t("target"), inputs.target_name],
[t("accountSelector"), inputs.account_selector || "auto"],
[t("service"), inputs.service_name || derivedService(state.selected, inputs.target_name)],
@@ -1001,7 +1044,7 @@ 切换摘要
`).join("");
- el("mode-pill").textContent = inputs.execution_mode;
+ el("mode-pill").textContent = modeLabel(inputs.execution_mode);
}
function renderAuth() {
@@ -1125,6 +1168,8 @@ 切换摘要
if (!profile) continue;
normalized[platform][String(key)] = {
strategy_profile: profile,
+ execution_mode: normalizeExecutionMode(entry?.execution_mode, entry?.dry_run_only),
+ dry_run_only: entry?.dry_run_only === true || entry?.dry_run_only === "true" || entry?.dry_run_only === "1",
source: entry?.source ? String(entry.source) : "",
};
}
diff --git a/web/strategy-switch-console/page_asset.js b/web/strategy-switch-console/page_asset.js
index f44e9e8..b1b33ac 100644
--- a/web/strategy-switch-console/page_asset.js
+++ b/web/strategy-switch-console/page_asset.js
@@ -1,2 +1,2 @@
// Generated by scripts/sync_strategy_switch_page_asset.py; do not edit by hand.
-export const PAGE_HTML = "\n\n\n \n \n \n QuantRuntimeSettings Strategy Switch\n \n\n\n \n \n
策略切换
\n
选平台、目标账号和策略,一次执行完成切换。
\n
\n \n \n\n \n \n\n \n \n
\n 当前平台\n \n
\n\n
\n\n
\n
\n
登录后才可执行切换。
\n
\n
\n
\n\n \n \n \n\n \n\n\n";
+export const PAGE_HTML = "\n\n\n \n \n \n QuantRuntimeSettings Strategy Switch\n \n\n\n \n \n
策略切换
\n
选平台、目标账号和策略,一次执行完成切换。
\n
\n \n \n\n \n \n\n \n \n
\n 当前平台\n \n
\n\n
\n\n
\n
\n
登录后才可执行切换。
\n
\n
\n
\n\n \n \n \n\n \n\n\n";
diff --git a/web/strategy-switch-console/worker.js b/web/strategy-switch-console/worker.js
index e13ef38..1e9a79c 100644
--- a/web/strategy-switch-console/worker.js
+++ b/web/strategy-switch-console/worker.js
@@ -480,10 +480,12 @@ async function loadCurrentStrategies(accountOptions, env) {
async function resolveCurrentStrategyForAccount({ platform, option, optionsCount, repository, readVariable }) {
const serviceTargetsValue = await readVariable(repository, "repository", "", "CLOUD_RUN_SERVICE_TARGETS_JSON");
- const serviceTargetProfile = strategyFromServiceTargets(serviceTargetsValue, platform, option);
+ const serviceTarget = runtimeTargetFromServiceTargets(serviceTargetsValue, platform, option);
+ const serviceTargetProfile = cleanCurrentStrategy(serviceTarget?.strategy_profile);
if (serviceTargetProfile) {
return {
strategy_profile: serviceTargetProfile,
+ ...runtimeModePayload(serviceTarget),
source: "CLOUD_RUN_SERVICE_TARGETS_JSON",
variable_scope: "repository",
};
@@ -498,6 +500,7 @@ async function resolveCurrentStrategyForAccount({ platform, option, optionsCount
if (runtimeTargetProfile) {
return {
strategy_profile: runtimeTargetProfile,
+ ...runtimeModePayload(runtimeTarget),
source: "RUNTIME_TARGET_JSON",
variable_scope: variableScope,
github_environment: githubEnvironment || "",
@@ -508,12 +511,16 @@ async function resolveCurrentStrategyForAccount({ platform, option, optionsCount
const profileValue = await readVariable(repository, variableScope, githubEnvironment, "STRATEGY_PROFILE");
const profile = cleanCurrentStrategy(profileValue);
if (profile) {
- return {
+ const current = {
strategy_profile: profile,
source: "STRATEGY_PROFILE",
variable_scope: variableScope,
github_environment: githubEnvironment || "",
};
+ if (variableScope === "environment" && normalizeMatchValue(option?.target_name) === "paper") {
+ current.execution_mode = "paper";
+ }
+ return current;
}
}
@@ -834,7 +841,7 @@ function resolveGithubEnvironment(platform, option, variableScope) {
return targetName;
}
-function strategyFromServiceTargets(rawValue, platform, option) {
+function runtimeTargetFromServiceTargets(rawValue, platform, option) {
const payload = parseJsonObject(rawValue);
const targets = Array.isArray(payload?.targets) ? payload.targets : [];
for (const entry of targets) {
@@ -843,12 +850,38 @@ function strategyFromServiceTargets(rawValue, platform, option) {
? entry.runtime_target
: {};
if (!runtimeTargetMatchesAccount(runtimeTarget, platform, option, entry)) continue;
- const profile = cleanCurrentStrategy(runtimeTarget.strategy_profile || entry.strategy_profile);
- if (profile) return profile;
+ return {
+ ...runtimeTarget,
+ strategy_profile: runtimeTarget.strategy_profile || entry.strategy_profile,
+ };
}
+ return null;
+}
+
+function runtimeModePayload(runtimeTarget) {
+ const executionMode = normalizeRuntimeExecutionMode(runtimeTarget?.execution_mode, runtimeTarget?.dry_run_only);
+ const payload = {};
+ if (executionMode) payload.execution_mode = executionMode;
+ const dryRunOnly = cleanOptionalBoolean(runtimeTarget?.dry_run_only);
+ if (dryRunOnly !== null) payload.dry_run_only = dryRunOnly;
+ return payload;
+}
+
+function normalizeRuntimeExecutionMode(value, dryRunOnly) {
+ const mode = String(value || "").trim().toLowerCase();
+ if (mode === "live" || mode === "paper") return mode;
+ const dryRun = cleanOptionalBoolean(dryRunOnly);
+ if (dryRun === true) return "paper";
+ if (dryRun === false) return "live";
return "";
}
+function cleanOptionalBoolean(value) {
+ if (value === true || value === "true" || value === "1" || value === 1) return true;
+ if (value === false || value === "false" || value === "0" || value === 0) return false;
+ return null;
+}
+
function runtimeTargetMatchesAccount(runtimeTarget, platform, option, entry = {}) {
const runtimePlatform = String(runtimeTarget?.platform_id || "").trim().toLowerCase();
if (runtimePlatform && runtimePlatform !== platform) return false;