Skip to content

Commit 6bbcb2f

Browse files
committed
Fix income layer controls
1 parent d38a0bf commit 6bbcb2f

7 files changed

Lines changed: 162 additions & 25 deletions

File tree

.github/workflows/manual-strategy-switch.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ on:
8282
description: "Optional platform minimum reserved cash override."
8383
required: false
8484
type: string
85+
income_layer_start_usd:
86+
description: "Optional income layer activation asset amount."
87+
required: false
88+
type: string
89+
income_layer_max_ratio:
90+
description: "Optional income layer maximum allocation ratio."
91+
required: false
92+
type: string
8593
income_threshold_usd:
8694
description: "Optional TQQQ income threshold override."
8795
required: false
@@ -147,6 +155,8 @@ jobs:
147155
EXTRA_VARIABLES_JSON: ${{ inputs.extra_variables_json }}
148156
RESERVED_CASH_RATIO: ${{ inputs.reserved_cash_ratio }}
149157
MIN_RESERVED_CASH_USD: ${{ inputs.min_reserved_cash_usd }}
158+
INCOME_LAYER_START_USD: ${{ inputs.income_layer_start_usd }}
159+
INCOME_LAYER_MAX_RATIO: ${{ inputs.income_layer_max_ratio }}
150160
INCOME_THRESHOLD_USD: ${{ inputs.income_threshold_usd }}
151161
QQQI_INCOME_RATIO: ${{ inputs.qqqi_income_ratio }}
152162
SERVICE_TARGETS_MODE: ${{ inputs.service_targets_mode }}
@@ -281,6 +291,12 @@ jobs:
281291
if [ -n "${MIN_RESERVED_CASH_USD:-}" ]; then
282292
args+=(--min-reserved-cash-usd "${MIN_RESERVED_CASH_USD}")
283293
fi
294+
if [ -n "${INCOME_LAYER_START_USD:-}" ]; then
295+
args+=(--income-layer-start-usd "${INCOME_LAYER_START_USD}")
296+
fi
297+
if [ -n "${INCOME_LAYER_MAX_RATIO:-}" ]; then
298+
args+=(--income-layer-max-ratio "${INCOME_LAYER_MAX_RATIO}")
299+
fi
284300
if [ -n "${INCOME_THRESHOLD_USD:-}" ]; then
285301
args+=(--income-threshold-usd "${INCOME_THRESHOLD_USD}")
286302
fi

scripts/build_runtime_switch.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
}
5353
INCOME_LAYER_VARIABLES = (
5454
"INCOME_LAYER_ENABLED",
55+
"INCOME_LAYER_START_USD",
5556
"INCOME_LAYER_MAX_RATIO",
5657
)
5758
RUNTIME_TARGET_VARIABLES = (
@@ -353,6 +354,10 @@ def build_switch_target(args: argparse.Namespace) -> dict[str, Any]:
353354
extra_variables[PLATFORM_RESERVED_CASH_RATIO_VARIABLES[platform]] = args.reserved_cash_ratio
354355
if args.min_reserved_cash_usd:
355356
extra_variables[PLATFORM_MIN_RESERVED_CASH_VARIABLES[platform]] = args.min_reserved_cash_usd
357+
if args.income_layer_start_usd:
358+
extra_variables["INCOME_LAYER_START_USD"] = args.income_layer_start_usd
359+
if args.income_layer_max_ratio:
360+
extra_variables["INCOME_LAYER_MAX_RATIO"] = args.income_layer_max_ratio
356361
if args.income_threshold_usd:
357362
extra_variables["INCOME_THRESHOLD_USD"] = args.income_threshold_usd
358363
if args.qqqi_income_ratio:
@@ -418,6 +423,8 @@ def build_parser() -> argparse.ArgumentParser:
418423
parser.add_argument("--extra-variable", action="append", default=[], help="NAME=VALUE non-secret extra variable")
419424
parser.add_argument("--reserved-cash-ratio", default="")
420425
parser.add_argument("--min-reserved-cash-usd", default="")
426+
parser.add_argument("--income-layer-start-usd", default="")
427+
parser.add_argument("--income-layer-max-ratio", default="")
421428
parser.add_argument("--income-threshold-usd", default="")
422429
parser.add_argument("--qqqi-income-ratio", default="")
423430
parser.add_argument("--existing-service-targets-json-file", default="")

tests/strategy_switch_worker_validation.mjs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ assert.ok(indexHtml.includes('id="reserved-cash-ratio-input"'));
2727
assert.ok(indexHtml.includes('id="reserve-policy-mode-select"'));
2828
assert.ok(indexHtml.includes('id="runtime-target-enabled-select"'));
2929
assert.ok(indexHtml.includes('id="plugin-mode-select"'));
30+
assert.ok(indexHtml.includes('id="income-layer-start-usd-input"'));
31+
assert.ok(indexHtml.includes('incomeLayerStartUsd: "收入层起始金额"'));
32+
assert.ok(indexHtml.includes('incomeLayerStartUsd: "Income layer start amount"'));
33+
assert.ok(indexHtml.includes('incomeLayerStartUsdVariable = "INCOME_LAYER_START_USD"'));
34+
assert.ok(indexHtml.includes('el("income-layer-mode-select").addEventListener("change"'));
35+
assert.ok(indexHtml.includes('el("income-layer-start-usd-input").addEventListener("input"'));
36+
assert.ok(indexHtml.includes('el("income-layer-max-ratio-input").addEventListener("input"'));
3037
assert.ok(indexHtml.includes("account-block"));
3138
assert.ok(indexHtml.includes("strategy-block"));
3239
assert.ok(indexHtml.includes("grid-template-columns: repeat(4, minmax(0, 1fr));"));
@@ -312,9 +319,13 @@ const normalizedReservedCashInputs = __test.normalizeSwitchInputs({
312319
trigger_platform_sync: "true",
313320
reserved_cash_ratio: "0.03",
314321
min_reserved_cash_usd: "150",
322+
income_layer_start_usd: "250000",
323+
income_layer_max_ratio: "0.55",
315324
});
316325
assert.equal(normalizedReservedCashInputs.reserved_cash_ratio, "0.03");
317326
assert.equal(normalizedReservedCashInputs.min_reserved_cash_usd, "150");
327+
assert.equal(normalizedReservedCashInputs.income_layer_start_usd, "250000");
328+
assert.equal(normalizedReservedCashInputs.income_layer_max_ratio, "0.55");
318329
const normalizedPluginInputs = __test.normalizeSwitchInputs({
319330
platform: "ibkr",
320331
target_name: "ibkr-primary",
@@ -426,6 +437,18 @@ globalThis.fetch = async (url) => {
426437
headers: { "Content-Type": "application/json" },
427438
});
428439
}
440+
if (requestUrl.endsWith("/INCOME_LAYER_START_USD")) {
441+
return new Response(JSON.stringify({ value: "150000" }), {
442+
status: 200,
443+
headers: { "Content-Type": "application/json" },
444+
});
445+
}
446+
if (requestUrl.endsWith("/INCOME_LAYER_MAX_RATIO")) {
447+
return new Response(JSON.stringify({ value: "0.95" }), {
448+
status: 200,
449+
headers: { "Content-Type": "application/json" },
450+
});
451+
}
429452
if (requestUrl.endsWith("/RUNTIME_TARGET_ENABLED")) {
430453
return new Response(JSON.stringify({ value: "false" }), {
431454
status: 200,
@@ -455,6 +478,8 @@ try {
455478
assert.equal(currentStrategies.schwab.default.execution_mode, "live");
456479
assert.equal(currentStrategies.schwab.default.min_reserved_cash_usd, "150");
457480
assert.equal(currentStrategies.schwab.default.reserved_cash_ratio, "0.03");
481+
assert.equal(currentStrategies.schwab.default.income_layer_start_usd, "150000");
482+
assert.equal(currentStrategies.schwab.default.income_layer_max_ratio, "0.95");
458483
assert.equal(currentStrategies.schwab.default.runtime_target_enabled, false);
459484
assert.equal(currentStrategies.schwab.default.source, "RUNTIME_TARGET_JSON");
460485
} finally {
@@ -472,6 +497,8 @@ globalThis.fetch = async (url) => {
472497
ACCOUNT_GROUP: "demo-ibkr-tqqq",
473498
IBKR_MIN_RESERVED_CASH_USD: "150",
474499
IBKR_RESERVED_CASH_RATIO: "0.03",
500+
INCOME_LAYER_START_USD: "250000",
501+
INCOME_LAYER_MAX_RATIO: "0.55",
475502
RUNTIME_TARGET_ENABLED: "false",
476503
runtime_target: {
477504
platform_id: "ibkr",
@@ -496,6 +523,8 @@ try {
496523
assert.equal(currentStrategies.ibkr["ibkr-primary"].strategy_profile, "tqqq_growth_income");
497524
assert.equal(currentStrategies.ibkr["ibkr-primary"].min_reserved_cash_usd, "150");
498525
assert.equal(currentStrategies.ibkr["ibkr-primary"].reserved_cash_ratio, "0.03");
526+
assert.equal(currentStrategies.ibkr["ibkr-primary"].income_layer_start_usd, "250000");
527+
assert.equal(currentStrategies.ibkr["ibkr-primary"].income_layer_max_ratio, "0.55");
499528
assert.equal(currentStrategies.ibkr["ibkr-primary"].runtime_target_enabled, false);
500529
assert.equal(currentStrategies.ibkr["ibkr-primary"].source, "CLOUD_RUN_SERVICE_TARGETS_JSON");
501530
} finally {

tests/test_runtime_settings.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ def test_build_switch_target_patches_ibkr_service_targets_json(self):
313313
"IBKR_MIN_RESERVED_CASH_USD": "150",
314314
"IBKR_RESERVED_CASH_RATIO": "0.03",
315315
"INCOME_LAYER_ENABLED": "true",
316+
"INCOME_LAYER_START_USD": "250000",
316317
"INCOME_LAYER_MAX_RATIO": "0.55",
317318
"RUNTIME_TARGET_ENABLED": "false",
318319
"runtime_target": {
@@ -376,6 +377,7 @@ def test_build_switch_target_patches_ibkr_service_targets_json(self):
376377
self.assertEqual(selected["IBKR_MIN_RESERVED_CASH_USD"], "150")
377378
self.assertEqual(selected["IBKR_RESERVED_CASH_RATIO"], "0.03")
378379
self.assertEqual(selected["INCOME_LAYER_ENABLED"], "true")
380+
self.assertEqual(selected["INCOME_LAYER_START_USD"], "250000")
379381
self.assertEqual(selected["INCOME_LAYER_MAX_RATIO"], "0.55")
380382
self.assertEqual(selected["RUNTIME_TARGET_ENABLED"], "false")
381383
self.assertEqual(

0 commit comments

Comments
 (0)