Skip to content

Commit d38a0bf

Browse files
authored
Add income layer and account runtime controls (#48)
1 parent 0d3d8b4 commit d38a0bf

6 files changed

Lines changed: 565 additions & 17 deletions

File tree

scripts/build_runtime_switch.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@
5050
"ibkr": "IBKR_MIN_RESERVED_CASH_USD",
5151
"firstrade": "FIRSTRADE_MIN_RESERVED_CASH_USD",
5252
}
53+
INCOME_LAYER_VARIABLES = (
54+
"INCOME_LAYER_ENABLED",
55+
"INCOME_LAYER_MAX_RATIO",
56+
)
57+
RUNTIME_TARGET_VARIABLES = (
58+
"RUNTIME_TARGET_ENABLED",
59+
)
5360
DEFAULT_VARIABLE_SCOPE = {
5461
"longbridge": "environment",
5562
"ibkr": "repository",
@@ -273,6 +280,8 @@ def _preserve_reserved_cash_fields(
273280
for variable in (
274281
PLATFORM_MIN_RESERVED_CASH_VARIABLES.get(platform),
275282
PLATFORM_RESERVED_CASH_RATIO_VARIABLES.get(platform),
283+
*INCOME_LAYER_VARIABLES,
284+
*RUNTIME_TARGET_VARIABLES,
276285
):
277286
if variable and variable not in replacement and variable in current_entry:
278287
replacement[variable] = current_entry[variable]

tests/strategy_switch_worker_validation.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ assert.equal(indexHtml.includes("loginLink.hidden = !state.auth.available || sig
2525
assert.ok(indexHtml.includes('id="min-reserved-cash-input"'));
2626
assert.ok(indexHtml.includes('id="reserved-cash-ratio-input"'));
2727
assert.ok(indexHtml.includes('id="reserve-policy-mode-select"'));
28+
assert.ok(indexHtml.includes('id="runtime-target-enabled-select"'));
2829
assert.ok(indexHtml.includes('id="plugin-mode-select"'));
2930
assert.ok(indexHtml.includes("account-block"));
3031
assert.ok(indexHtml.includes("strategy-block"));
@@ -38,6 +39,10 @@ assert.ok(indexHtml.includes('reservePolicyFloor'));
3839
assert.ok(indexHtml.includes('reservePolicyMax'));
3940
assert.ok(indexHtml.includes('pluginModeAuto'));
4041
assert.ok(indexHtml.includes('pluginModeNone'));
42+
assert.ok(indexHtml.includes('runtimeTargetMode: "账号运行状态"'));
43+
assert.ok(indexHtml.includes('runtimeTargetEnabled: "启用正式运行"'));
44+
assert.ok(indexHtml.includes('runtimeTargetDisabled: "停用正式运行"'));
45+
assert.ok(indexHtml.includes('runtimeTargetMode: "Account status"'));
4146
assert.ok(indexHtml.includes('pluginMode: "插件启用范围"'));
4247
assert.ok(indexHtml.includes('pluginModeAuto: "按策略默认"'));
4348
assert.ok(indexHtml.includes('pluginModeNone: "完全禁用"'));
@@ -62,6 +67,7 @@ assert.ok(indexHtml.includes('.summary-row.pending'));
6267
assert.ok(indexHtml.includes('function currentEntryHasState('));
6368
assert.ok(indexHtml.includes('changes.reserveCashChanged'));
6469
assert.ok(indexHtml.includes('changes.pluginModeChanged'));
70+
assert.ok(indexHtml.includes('changes.runtimeTargetChanged'));
6571
assert.ok(indexHtml.includes('!hasPendingChange'));
6672
assert.ok(indexHtml.includes('hasPendingChange ? t("readyNote") : ""'));
6773
assert.equal(indexHtml.includes('hasPendingChange ? t("readyNote") : t("noChangesNote")'), false);
@@ -420,6 +426,12 @@ globalThis.fetch = async (url) => {
420426
headers: { "Content-Type": "application/json" },
421427
});
422428
}
429+
if (requestUrl.endsWith("/RUNTIME_TARGET_ENABLED")) {
430+
return new Response(JSON.stringify({ value: "false" }), {
431+
status: 200,
432+
headers: { "Content-Type": "application/json" },
433+
});
434+
}
423435
if (requestUrl.endsWith("/RUNTIME_TARGET_JSON")) {
424436
return new Response(JSON.stringify({
425437
value: JSON.stringify({
@@ -443,6 +455,7 @@ try {
443455
assert.equal(currentStrategies.schwab.default.execution_mode, "live");
444456
assert.equal(currentStrategies.schwab.default.min_reserved_cash_usd, "150");
445457
assert.equal(currentStrategies.schwab.default.reserved_cash_ratio, "0.03");
458+
assert.equal(currentStrategies.schwab.default.runtime_target_enabled, false);
446459
assert.equal(currentStrategies.schwab.default.source, "RUNTIME_TARGET_JSON");
447460
} finally {
448461
globalThis.fetch = originalFetch;
@@ -459,6 +472,7 @@ globalThis.fetch = async (url) => {
459472
ACCOUNT_GROUP: "demo-ibkr-tqqq",
460473
IBKR_MIN_RESERVED_CASH_USD: "150",
461474
IBKR_RESERVED_CASH_RATIO: "0.03",
475+
RUNTIME_TARGET_ENABLED: "false",
462476
runtime_target: {
463477
platform_id: "ibkr",
464478
strategy_profile: "tqqq_growth_income",
@@ -482,6 +496,7 @@ try {
482496
assert.equal(currentStrategies.ibkr["ibkr-primary"].strategy_profile, "tqqq_growth_income");
483497
assert.equal(currentStrategies.ibkr["ibkr-primary"].min_reserved_cash_usd, "150");
484498
assert.equal(currentStrategies.ibkr["ibkr-primary"].reserved_cash_ratio, "0.03");
499+
assert.equal(currentStrategies.ibkr["ibkr-primary"].runtime_target_enabled, false);
485500
assert.equal(currentStrategies.ibkr["ibkr-primary"].source, "CLOUD_RUN_SERVICE_TARGETS_JSON");
486501
} finally {
487502
globalThis.fetch = originalFetch;

tests/test_runtime_settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ def test_build_switch_target_patches_ibkr_service_targets_json(self):
312312
"ACCOUNT_GROUP": "demo-ibkr-tqqq",
313313
"IBKR_MIN_RESERVED_CASH_USD": "150",
314314
"IBKR_RESERVED_CASH_RATIO": "0.03",
315+
"INCOME_LAYER_ENABLED": "true",
316+
"INCOME_LAYER_MAX_RATIO": "0.55",
317+
"RUNTIME_TARGET_ENABLED": "false",
315318
"runtime_target": {
316319
"platform_id": "ibkr",
317320
"strategy_profile": "old_strategy",
@@ -372,6 +375,9 @@ def test_build_switch_target_patches_ibkr_service_targets_json(self):
372375
self.assertEqual(selected["IBKR_DRY_RUN_ONLY"], "false")
373376
self.assertEqual(selected["IBKR_MIN_RESERVED_CASH_USD"], "150")
374377
self.assertEqual(selected["IBKR_RESERVED_CASH_RATIO"], "0.03")
378+
self.assertEqual(selected["INCOME_LAYER_ENABLED"], "true")
379+
self.assertEqual(selected["INCOME_LAYER_MAX_RATIO"], "0.55")
380+
self.assertEqual(selected["RUNTIME_TARGET_ENABLED"], "false")
375381
self.assertEqual(
376382
selected["IBKR_STRATEGY_PLUGIN_MOUNTS_JSON"]["strategy_plugins"][0]["plugin"],
377383
"market_regime_control",

0 commit comments

Comments
 (0)