Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 103 additions & 1 deletion scripts/build_platform_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,17 +229,119 @@ def build_strategy_profiles(config: dict) -> str:
return payload


def inject_index_html(config: dict) -> None:
"""Inject generated config block into index.html."""
index_path = ROOT / "web" / "strategy-switch-console" / "index.html"
html = index_path.read_text(encoding="utf-8")

platforms = config["platforms"]
strategies = config["strategies"]

# Generate injection block
pc = {}
dao = {}
for pid, pdata in platforms.items():
caps = pdata["capabilities"]
depl = pdata["deployment"]
pc[pid] = {
"dry_run_only": depl.get("dry_run_only", False),
"margin_policy": caps.get("margin_policy", False),
"reserved_cash": caps.get("reserved_cash", False),
"income_layer": caps.get("income_layer", False),
"option_overlay": caps.get("option_overlay", False),
"dca": caps.get("dca", False),
"execution_mode": depl.get("default_execution_mode", "live"),
"service_name": depl.get("service_name", ""),
"default_execution_mode": depl.get("default_execution_mode", "live"),
}
acct = pdata.get("default_account", {})
entry = {
"key": acct.get("key", pid),
"label": acct.get("label", pdata.get("label", pid)),
"target_name": acct.get("target_name", acct.get("key", pid)),
"supported_domains": acct.get("supported_domains", pdata.get("supported_domains", [])),
"cash_currency": acct.get("cash_currency", "USD"),
}
for fld in ("default_strategy_profile", "service_name", "account_scope",
"deployment_selector", "account_selector", "default_execution_mode",
"min_reserved_cash_usd", "reserved_cash_ratio",
"cash_only_execution_mode", "dca_mode", "dca_base_investment_usd"):
val = acct.get(fld)
if val: entry[fld] = val
if "service_name" not in entry:
entry["service_name"] = depl.get("service_name", "")
if "default_execution_mode" not in entry:
entry["default_execution_mode"] = depl.get("default_execution_mode", "live")
dao[pid] = [entry]

dca = {}
inc_layer = {}
opt_overlay = {}
for sid, sdata in strategies.items():
feat = sdata.get("features", {})
dd = sdata.get("dca_defaults")
if dd:
dca[sid] = {"defaultMode": dd.get("default_mode", "fixed"),
"defaultBaseInvestmentUsd": str(dd.get("default_base_investment_usd", "1000"))}
if feat.get("income_layer"):
idl = sdata.get("income_layer_defaults", {})
inc_layer[sid] = {"startUsd": int(idl.get("start_usd", 0)),
"maxRatio": str(idl.get("max_ratio", "")),
"allocations": idl.get("allocations", {})}
if feat.get("option_overlay"):
odl = sdata.get("option_overlay_defaults", {})
families = []
if odl.get("growth_enabled"):
families.append({"family": "growth", "recipe": odl["growth_recipe"],
"startUsd": odl["growth_start_usd"],
"ratio": str(odl.get("nav_budget_ratio", "")), "ratioKind": "budget"})
if odl.get("income_enabled"):
families.append({"family": "income", "recipe": odl["income_recipe"],
"startUsd": odl["income_start_usd"],
"ratio": str(odl.get("nav_risk_ratio", "")), "ratioKind": "risk"})
opt_overlay[sid] = {"liveGate": odl.get("live_gate", ""),
"liveStatus": odl.get("live_status", ""),
"families": families}

block_lines = [
"<!-- Generated by build_platform_config.py from platform-config.json -->",
'<script id="platform-config">',
f"window.__PLATFORM_CONFIG__ = {json.dumps(pc, ensure_ascii=False)};",
f"window.__DEFAULT_ACCOUNT_OPTIONS__ = {json.dumps(dao, ensure_ascii=False)};",
f"window.__DCA_PROFILE_DEFAULTS__ = {json.dumps(dca, ensure_ascii=False)};",
f"window.__INCOME_LAYER_DEFAULTS__ = {json.dumps(inc_layer, ensure_ascii=False)};",
f"window.__OPTION_OVERLAY_DEFAULTS__ = {json.dumps(opt_overlay, ensure_ascii=False)};",
"</script>",
]
block = "\n".join(block_lines)

# Replace existing block or inject before </head>
existing_start = html.find('<script id="platform-config">')
if existing_start >= 0:
existing_end = html.find('</script>', existing_start) + 9
html = html[:existing_start] + block + html[existing_end:]
else:
head_end = html.find('</head>')
html = html[:head_end] + "\n" + block + "\n" + html[head_end:]

index_path.write_text(html, encoding="utf-8")
print(f"Injected config into: {index_path}")


def main() -> int:
config = json.loads(SOURCE.read_text(encoding="utf-8"))

# Generate config.js
# Generate config.js (ES module for worker.js)
module = build_config_module(config)
TARGET.write_text(module, encoding="utf-8")

# Generate strategy_profiles_asset.js
profiles = build_strategy_profiles(config)
STRATEGY_TARGET.write_text(profiles, encoding="utf-8")

# Inject config block into index.html
inject_index_html(config)

print(f"Generated: {TARGET}")
print(f"Generated: {STRATEGY_TARGET}")
return 0
Expand Down
20 changes: 15 additions & 5 deletions web/strategy-switch-console/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1317,6 +1317,16 @@
}
}
</style>

<!-- Generated by build_platform_config.py -->
<!-- Generated by build_platform_config.py from platform-config.json -->
<script id="platform-config">
window.__PLATFORM_CONFIG__ = {"longbridge": {"dry_run_only": false, "margin_policy": true, "reserved_cash": true, "income_layer": true, "option_overlay": true, "dca": true, "execution_mode": "live", "service_name": "", "default_execution_mode": "live"}, "ibkr": {"dry_run_only": false, "margin_policy": true, "reserved_cash": true, "income_layer": true, "option_overlay": true, "dca": false, "execution_mode": "live", "service_name": "", "default_execution_mode": "live"}, "schwab": {"dry_run_only": false, "margin_policy": true, "reserved_cash": true, "income_layer": true, "option_overlay": true, "dca": true, "execution_mode": "live", "service_name": "charles-schwab-quant-service", "default_execution_mode": "live"}, "firstrade": {"dry_run_only": false, "margin_policy": true, "reserved_cash": true, "income_layer": true, "option_overlay": true, "dca": true, "execution_mode": "live", "service_name": "firstrade-quant-service", "default_execution_mode": "live"}, "qmt": {"dry_run_only": true, "margin_policy": false, "reserved_cash": false, "income_layer": false, "option_overlay": false, "dca": false, "execution_mode": "paper", "service_name": "qmt-quant-service", "default_execution_mode": "paper"}, "binance": {"dry_run_only": false, "margin_policy": false, "reserved_cash": false, "income_layer": false, "option_overlay": false, "dca": true, "execution_mode": "live", "service_name": "", "default_execution_mode": "live"}};
window.__DEFAULT_ACCOUNT_OPTIONS__ = {"longbridge": [{"key": "preview", "label": "LongBridge", "target_name": "preview", "supported_domains": ["us_equity", "hk_equity"], "cash_currency": "USD", "default_strategy_profile": "tqqq_growth_income", "default_execution_mode": "live", "reserved_cash_ratio": "0.03", "service_name": ""}], "ibkr": [{"key": "preview", "label": "IBKR", "target_name": "preview", "supported_domains": ["us_equity", "hk_equity"], "cash_currency": "USD", "default_strategy_profile": "soxl_soxx_trend_income", "default_execution_mode": "live", "reserved_cash_ratio": "0.03", "service_name": ""}], "schwab": [{"key": "preview", "label": "Schwab", "target_name": "preview", "supported_domains": ["us_equity"], "cash_currency": "USD", "default_strategy_profile": "soxl_soxx_trend_income", "default_execution_mode": "live", "min_reserved_cash_usd": "150", "reserved_cash_ratio": "0.03", "service_name": "charles-schwab-quant-service"}], "firstrade": [{"key": "preview", "label": "Firstrade", "target_name": "preview", "supported_domains": ["us_equity"], "cash_currency": "USD", "default_strategy_profile": "ibit_smart_dca", "default_execution_mode": "live", "reserved_cash_ratio": "0.03", "service_name": "firstrade-quant-service"}], "qmt": [{"key": "default", "label": "QMT", "target_name": "default", "supported_domains": ["cn_equity"], "cash_currency": "CNY", "default_strategy_profile": "cn_industry_etf_rotation", "service_name": "qmt-quant-service", "default_execution_mode": "paper"}], "binance": [{"key": "default", "label": "Binance", "target_name": "default", "supported_domains": ["crypto"], "cash_currency": "USD", "default_strategy_profile": "crypto_live_pool_rotation", "service_name": "", "default_execution_mode": "live"}]};
window.__DCA_PROFILE_DEFAULTS__ = {"nasdaq_sp500_smart_dca": {"defaultMode": "fixed", "defaultBaseInvestmentUsd": "1000"}, "ibit_smart_dca": {"defaultMode": "fixed", "defaultBaseInvestmentUsd": "1000"}, "crypto_btc_dca": {"defaultMode": "fixed", "defaultBaseInvestmentUsd": "100"}};
window.__INCOME_LAYER_DEFAULTS__ = {"tqqq_growth_income": {"startUsd": 250000, "maxRatio": "0.55", "allocations": {"SCHD": 0.3, "DGRO": 0.2, "SGOV": 0.4, "SPYI": 0.08, "QQQI": 0.02}}, "soxl_soxx_trend_income": {"startUsd": 150000, "maxRatio": "0.95", "allocations": {"SCHD": 0.15, "DGRO": 0.1, "SGOV": 0.7, "SPYI": 0.04, "QQQI": 0.01}}, "global_etf_rotation": {"startUsd": 500000, "maxRatio": "0.15", "allocations": {"SCHD": 0.4, "DGRO": 0.25, "SGOV": 0.3, "SPYI": 0.05}}, "russell_top50_leader_rotation": {"startUsd": 300000, "maxRatio": "0.25", "allocations": {"SCHD": 0.45, "DGRO": 0.3, "SGOV": 0.25}}, "us_equity_combo": {"startUsd": 300000, "maxRatio": "0.25", "allocations": {"SCHD": 0.25, "DGRO": 0.25, "SGOV": 0.2, "SPYI": 0.15, "QQQI": 0.15}}};
window.__OPTION_OVERLAY_DEFAULTS__ = {"tqqq_growth_income": {"liveGate": "promotion_required", "liveStatus": "research_only", "families": [{"family": "growth", "recipe": "tqqq_leaps_growth_v1", "startUsd": "250000", "ratio": "0.03", "ratioKind": "budget"}]}, "soxl_soxx_trend_income": {"liveGate": "promotion_required", "liveStatus": "research_only", "families": [{"family": "income", "recipe": "soxx_put_credit_spread_income_v1", "startUsd": "150000", "ratio": "0.01", "ratioKind": "risk"}]}, "global_etf_rotation": {"liveGate": "promotion_required", "liveStatus": "research_only", "families": [{"family": "growth", "recipe": "spy_leaps_growth_v1", "startUsd": "500000", "ratio": "0.015", "ratioKind": "budget"}]}, "russell_top50_leader_rotation": {"liveGate": "promotion_required", "liveStatus": "research_only", "families": [{"family": "growth", "recipe": "spy_leaps_growth_v1", "startUsd": "300000", "ratio": "0.015", "ratioKind": "budget"}]}, "us_equity_combo": {"liveGate": "promotion_required", "liveStatus": "research_only", "families": [{"family": "growth", "recipe": "spy_leaps_growth_v1", "startUsd": "300000", "ratio": "0.015", "ratioKind": "budget"}]}};
</script>
</head>
<body class="app-loading">
<header class="topbar">
Expand Down Expand Up @@ -1525,7 +1535,7 @@ <h2 data-i18n="summary">切换摘要</h2>
// Alias for backward compatibility
const defaultRepositories = platformRepositories;

const defaultAccountOptions = {
const defaultAccountOptions = window.__DEFAULT_ACCOUNT_OPTIONS__ || {
binance: [{"key": "default", "label": "Binance", "target_name": "default", "cash_currency": "USD", "default_strategy_profile": "crypto_equity_combo", "supported_domains": ["crypto"]}],
firstrade: [{"key": "preview", "label": "Firstrade", "target_name": "preview", "supported_domains": ["us_equity"], "cash_currency": "USD", "default_execution_mode": "live", "service_name": "firstrade-quant-service"}],
ibkr: [{"key": "preview", "label": "IBKR", "target_name": "preview", "supported_domains": ["us_equity", "hk_equity"], "cash_currency": "USD", "default_execution_mode": "live"}],
Expand All @@ -1541,7 +1551,7 @@ <h2 data-i18n="summary">切换摘要</h2>
us_equity: { zh: "美股", en: "US Equity" },
};

const platformConfig = {
const platformConfig = window.__PLATFORM_CONFIG__ || {
binance: {
dry_run_only: false,
margin_policy: false,
Expand Down Expand Up @@ -1655,7 +1665,7 @@ <h2 data-i18n="summary">切换摘要</h2>
const incomeLayerEnabledVariable = "INCOME_LAYER_ENABLED";
const incomeLayerStartUsdVariable = "INCOME_LAYER_START_USD";
const incomeLayerMaxRatioVariable = "INCOME_LAYER_MAX_RATIO";
const dcaProfileDefaults = {
const dcaProfileDefaults = window.__DCA_PROFILE_DEFAULTS__ || {
nasdaq_sp500_smart_dca: { defaultMode: "fixed", defaultBaseInvestmentUsd: "1000" },
ibit_smart_dca: { defaultMode: "fixed", defaultBaseInvestmentUsd: "1000" },
crypto_btc_dca: { defaultMode: "fixed", defaultBaseInvestmentUsd: "100" },
Expand Down Expand Up @@ -1733,8 +1743,8 @@ <h2 data-i18n="summary">切换摘要</h2>
maxRatio: "0.25",
allocations: { SCHD: 0.25, DGRO: 0.25, SGOV: 0.20, SPYI: 0.15, QQQI: 0.15 },
}};
let incomeLayerDefaults = {};
const fallbackOptionOverlayDefaults = {
let incomeLayerDefaults = window.__INCOME_LAYER_DEFAULTS__ || {};
const fallbackOptionOverlayDefaults = window.__OPTION_OVERLAY_DEFAULTS__ || {
tqqq_growth_income: {
liveGate: "promotion_required",
liveStatus: "research_only",
Expand Down
2 changes: 1 addition & 1 deletion web/strategy-switch-console/page_asset.js

Large diffs are not rendered by default.

Loading
Loading