@@ -31,14 +31,24 @@ assert.ok(indexHtml.includes('id="income-layer-start-usd-input"'));
3131assert . ok ( indexHtml . includes ( 'incomeLayerStartUsd: "收入层起始金额"' ) ) ;
3232assert . ok ( indexHtml . includes ( 'incomeLayerStartUsd: "Income layer start amount"' ) ) ;
3333assert . ok ( indexHtml . includes ( 'incomeLayerStartUsdVariable = "INCOME_LAYER_START_USD"' ) ) ;
34+ assert . ok ( indexHtml . includes ( 'id="dca-mode-select"' ) ) ;
35+ assert . ok ( indexHtml . includes ( 'id="dca-base-investment-usd-input"' ) ) ;
36+ assert . ok ( indexHtml . includes ( 'dcaMode: "定投模式"' ) ) ;
37+ assert . ok ( indexHtml . includes ( 'dcaModeFixed: "定额定投"' ) ) ;
38+ assert . ok ( indexHtml . includes ( 'dcaModeSmart: "智能定投"' ) ) ;
39+ assert . ok ( indexHtml . includes ( 'dcaMode: "DCA mode"' ) ) ;
40+ assert . ok ( indexHtml . includes ( 'dcaProfileDefaults' ) ) ;
3441assert . ok ( indexHtml . includes ( 'el("income-layer-mode-select").addEventListener("change"' ) ) ;
3542assert . ok ( indexHtml . includes ( 'el("income-layer-start-usd-input").addEventListener("input"' ) ) ;
3643assert . ok ( indexHtml . includes ( 'el("income-layer-max-ratio-input").addEventListener("input"' ) ) ;
37- assert . ok ( indexHtml . includes ( 'label_zh: "纳指100 / 标普500 智能定投"' ) ) ;
44+ assert . ok ( indexHtml . includes ( 'el("dca-mode-select").addEventListener("change"' ) ) ;
45+ assert . ok ( indexHtml . includes ( 'el("dca-base-investment-usd-input").addEventListener("input"' ) ) ;
46+ assert . ok ( indexHtml . includes ( 'label_zh: "纳指100 / 标普500 定投"' ) ) ;
3847assert . ok ( indexHtml . includes ( 'class="form-section income-layer-section"' ) ) ;
48+ assert . ok ( indexHtml . includes ( 'class="form-section dca-section"' ) ) ;
3949assert . ok ( indexHtml . includes ( 'class="control-block reserve-policy-block section-wide"' ) ) ;
4050assert . ok ( indexHtml . includes ( 'profile: "ibit_smart_dca"' ) ) ;
41- assert . ok ( indexHtml . includes ( 'IBIT 比特币 ETF 智能定投 ' ) ) ;
51+ assert . ok ( indexHtml . includes ( 'IBIT 比特币定投 ' ) ) ;
4252assert . ok ( indexHtml . includes ( 'localStrategyLabels' ) ) ;
4353assert . ok ( indexHtml . includes ( 'function strategyLabelSet(' ) ) ;
4454assert . ok ( indexHtml . includes ( "account-block" ) ) ;
@@ -221,11 +231,21 @@ const strategyProfiles = __test.normalizeStrategyProfilesPayload(
221231 domain : "hk_equity" ,
222232 runtime_enabled : true ,
223233 } ,
234+ {
235+ profile : "nasdaq_sp500_smart_dca" ,
236+ label : "Nasdaq 100 / S&P 500 DCA" ,
237+ label_zh : "纳指100 / 标普500 定投" ,
238+ domain : "us_equity" ,
239+ runtime_enabled : true ,
240+ } ,
224241 ] ,
225242 "test_strategy_profiles" ,
226243) ;
227244assert . equal ( strategyProfiles [ 0 ] . label_en , "TQQQ Growth Income" ) ;
228245assert . equal ( strategyProfiles [ 0 ] . label_zh , "TQQQ 增长收益" ) ;
246+ assert . equal ( strategyProfiles [ 2 ] . dca_enabled , true ) ;
247+ assert . equal ( strategyProfiles [ 2 ] . dca_default_mode , "fixed" ) ;
248+ assert . equal ( strategyProfiles [ 2 ] . dca_default_base_investment_usd , "1000" ) ;
229249
230250const accountOptions = __test . normalizeAccountOptionsPayload (
231251 {
@@ -345,6 +365,45 @@ const normalizedPluginInputs = __test.normalizeSwitchInputs({
345365 plugin_mode : "none" ,
346366} ) ;
347367assert . equal ( normalizedPluginInputs . plugin_mode , "none" ) ;
368+ const normalizedDcaInputs = __test . normalizeSwitchInputs ( {
369+ platform : "ibkr" ,
370+ target_name : "ibkr-primary" ,
371+ strategy_profile : "nasdaq_sp500_smart_dca" ,
372+ execution_mode : "live" ,
373+ plugin_mode : "auto" ,
374+ dca_mode : "smart" ,
375+ dca_base_investment_usd : "500" ,
376+ } ) ;
377+ assert . equal ( normalizedDcaInputs . dca_mode , "smart" ) ;
378+ assert . equal ( normalizedDcaInputs . dca_base_investment_usd , "500" ) ;
379+ assert . throws (
380+ ( ) => __test . normalizeSwitchInputs ( {
381+ platform : "ibkr" ,
382+ target_name : "ibkr-primary" ,
383+ strategy_profile : "tqqq_growth_income" ,
384+ dca_mode : "smart" ,
385+ } ) ,
386+ / D C A s e t t i n g s a r e o n l y s u p p o r t e d / ,
387+ ) ;
388+ assert . throws (
389+ ( ) => __test . normalizeSwitchInputs ( {
390+ platform : "ibkr" ,
391+ target_name : "ibkr-primary" ,
392+ strategy_profile : "nasdaq_sp500_smart_dca" ,
393+ dca_mode : "smart" ,
394+ dca_base_investment_usd : "0" ,
395+ } ) ,
396+ / d c a _ b a s e _ i n v e s t m e n t _ u s d m u s t b e g r e a t e r t h a n 0 / ,
397+ ) ;
398+ assert . throws (
399+ ( ) => __test . normalizeSwitchInputs ( {
400+ platform : "ibkr" ,
401+ target_name : "ibkr-primary" ,
402+ strategy_profile : "tqqq_growth_income" ,
403+ extra_variables_json : JSON . stringify ( { DCA_MODE : "smart" } ) ,
404+ } ) ,
405+ / i n s t e a d o f e x t r a _ v a r i a b l e s _ j s o n / ,
406+ ) ;
348407const normalizedReserveClearInputs = __test . normalizeSwitchInputs ( {
349408 platform : "ibkr" ,
350409 target_name : "ibkr-primary" ,
@@ -466,11 +525,23 @@ globalThis.fetch = async (url) => {
466525 headers : { "Content-Type" : "application/json" } ,
467526 } ) ;
468527 }
528+ if ( requestUrl . endsWith ( "/DCA_MODE" ) ) {
529+ return new Response ( JSON . stringify ( { value : "smart" } ) , {
530+ status : 200 ,
531+ headers : { "Content-Type" : "application/json" } ,
532+ } ) ;
533+ }
534+ if ( requestUrl . endsWith ( "/DCA_BASE_INVESTMENT_USD" ) ) {
535+ return new Response ( JSON . stringify ( { value : "500" } ) , {
536+ status : 200 ,
537+ headers : { "Content-Type" : "application/json" } ,
538+ } ) ;
539+ }
469540 if ( requestUrl . endsWith ( "/RUNTIME_TARGET_JSON" ) ) {
470541 return new Response ( JSON . stringify ( {
471542 value : JSON . stringify ( {
472543 platform_id : "schwab" ,
473- strategy_profile : "soxl_soxx_trend_income " ,
544+ strategy_profile : "nasdaq_sp500_smart_dca " ,
474545 dry_run_only : false ,
475546 account_scope : "schwab" ,
476547 service_name : "charles-schwab-quant-service" ,
@@ -485,13 +556,15 @@ try {
485556 { schwab : accountOptions . schwab } ,
486557 { RUNTIME_SETTINGS_DISPATCH_TOKEN : "test-token" } ,
487558 ) ;
488- assert . equal ( currentStrategies . schwab . default . strategy_profile , "soxl_soxx_trend_income " ) ;
559+ assert . equal ( currentStrategies . schwab . default . strategy_profile , "nasdaq_sp500_smart_dca " ) ;
489560 assert . equal ( currentStrategies . schwab . default . execution_mode , "live" ) ;
490561 assert . equal ( currentStrategies . schwab . default . min_reserved_cash_usd , "150" ) ;
491562 assert . equal ( currentStrategies . schwab . default . reserved_cash_ratio , "0.03" ) ;
492563 assert . equal ( currentStrategies . schwab . default . income_layer_start_usd , "150000" ) ;
493564 assert . equal ( currentStrategies . schwab . default . income_layer_max_ratio , "0.95" ) ;
494565 assert . equal ( currentStrategies . schwab . default . runtime_target_enabled , false ) ;
566+ assert . equal ( currentStrategies . schwab . default . dca_mode , "smart" ) ;
567+ assert . equal ( currentStrategies . schwab . default . dca_base_investment_usd , "500" ) ;
495568 assert . equal ( currentStrategies . schwab . default . source , "RUNTIME_TARGET_JSON" ) ;
496569} finally {
497570 globalThis . fetch = originalFetch ;
@@ -511,9 +584,11 @@ globalThis.fetch = async (url) => {
511584 INCOME_LAYER_START_USD : "250000" ,
512585 INCOME_LAYER_MAX_RATIO : "0.55" ,
513586 RUNTIME_TARGET_ENABLED : "false" ,
587+ DCA_MODE : "smart" ,
588+ DCA_BASE_INVESTMENT_USD : "700" ,
514589 runtime_target : {
515590 platform_id : "ibkr" ,
516- strategy_profile : "tqqq_growth_income " ,
591+ strategy_profile : "ibit_smart_dca " ,
517592 dry_run_only : false ,
518593 account_scope : "demo-ibkr-tqqq" ,
519594 service_name : "interactive-brokers-demo-ibkr-tqqq-service" ,
@@ -531,12 +606,14 @@ try {
531606 { ibkr : accountOptions . ibkr } ,
532607 { RUNTIME_SETTINGS_DISPATCH_TOKEN : "test-token" } ,
533608 ) ;
534- assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . strategy_profile , "tqqq_growth_income " ) ;
609+ assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . strategy_profile , "ibit_smart_dca " ) ;
535610 assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . min_reserved_cash_usd , "150" ) ;
536611 assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . reserved_cash_ratio , "0.03" ) ;
537612 assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . income_layer_start_usd , "250000" ) ;
538613 assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . income_layer_max_ratio , "0.55" ) ;
539614 assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . runtime_target_enabled , false ) ;
615+ assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . dca_mode , "smart" ) ;
616+ assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . dca_base_investment_usd , "700" ) ;
540617 assert . equal ( currentStrategies . ibkr [ "ibkr-primary" ] . source , "CLOUD_RUN_SERVICE_TARGETS_JSON" ) ;
541618} finally {
542619 globalThis . fetch = originalFetch ;
0 commit comments