@@ -1225,6 +1225,20 @@ def _should_bootstrap_whole_share_buy(symbol, *, target_value, limit_price) -> b
12251225 return max (0.0 , float (target_value or 0.0 )) >= effective_limit_price * float (min_target_share_ratio )
12261226
12271227
1228+ def _should_top_up_existing_whole_share_buy (symbol , * , target_gap_value , limit_price , quantity = 0.0 ) -> bool :
1229+ normalized_symbol = str (symbol or "" ).strip ().upper ()
1230+ if normalized_symbol not in SMALL_ACCOUNT_WHOLE_SHARE_BOOTSTRAP_MIN_TARGET_SHARE_RATIO_BY_SYMBOL :
1231+ return False
1232+ if max (0.0 , float (quantity or 0.0 )) <= 0.0 :
1233+ return False
1234+ effective_limit_price = max (0.0 , float (limit_price or 0.0 ))
1235+ if effective_limit_price <= 0.0 :
1236+ return False
1237+ return max (0.0 , float (target_gap_value or 0.0 )) >= (
1238+ effective_limit_price * float (_SMALL_ACCOUNT_RETENTION_MIN_TARGET_SHARE_RATIO_DEFAULT )
1239+ )
1240+
1241+
12281242def _format_symbol_with_suffix (symbol , * , suffix = ".US" ) -> str :
12291243 normalized = str (symbol or "" ).strip ().upper ()
12301244 if not normalized :
@@ -1370,6 +1384,7 @@ def record_quote_snapshot(symbol, snapshot) -> None:
13701384 "small_account_whole_share_cash_notes" : [],
13711385 "small_account_allocation_drift_notes" : [],
13721386 "residual_cash_estimate" : float (account_values .get ("buying_power" , 0.0 ) or 0.0 ),
1387+ "projected_sell_release_value" : 0.0 ,
13731388 "current_stock_weight" : 0.0 ,
13741389 "current_safe_haven_weight" : 0.0 ,
13751390 "price_source_mode" : "market_quote" ,
@@ -1900,6 +1915,7 @@ def cash_sweep_sale_quantity_to_fund_buy(max_quantity: int, candidate_symbols: t
19001915 execution_summary ["execution_status" ] = "executing"
19011916
19021917 sell_executed = False
1918+ projected_sell_release_value = 0.0
19031919 pending_sell_release_symbols : list [str ] = []
19041920 for symbol in all_symbols :
19051921 current = current_mv .get (symbol , 0 )
@@ -1979,6 +1995,7 @@ def cash_sweep_sale_quantity_to_fund_buy(max_quantity: int, candidate_symbols: t
19791995 trade_logs .append (translator ("market_sell" , symbol = symbol , qty = format_quantity (qty )) + f" { status_msg } " )
19801996 if ok :
19811997 sell_executed = True
1998+ projected_sell_release_value += float (qty ) * float (price )
19821999
19832000 if dry_run_only :
19842001 buying_power = max (0.0 , anticipated_buying_power + dry_run_sale_proceeds )
@@ -1992,9 +2009,15 @@ def cash_sweep_sale_quantity_to_fund_buy(max_quantity: int, candidate_symbols: t
19922009 currency = market_currency ,
19932010 cash_only_execution = cash_only_execution ,
19942011 )
2012+ if cash_only_execution and projected_sell_release_value > 0.0 :
2013+ buying_power = max (
2014+ float (buying_power or 0.0 ),
2015+ float (anticipated_buying_power or 0.0 ) + float (projected_sell_release_value ),
2016+ )
19952017 else :
19962018 buying_power = anticipated_buying_power
19972019 investable_buying_power = _investable_buying_power (buying_power , reserved )
2020+ execution_summary ["projected_sell_release_value" ] = float (projected_sell_release_value )
19982021 pending_sell_release_symbols = list (dict .fromkeys (pending_sell_release_symbols ))
19992022 buy_needed_symbols = [
20002023 symbol
@@ -2068,6 +2091,34 @@ def cash_sweep_sale_quantity_to_fund_buy(max_quantity: int, candidate_symbols: t
20682091 buy_value / limit_price ,
20692092 quantity_step = order_quantity_step ,
20702093 )
2094+ held_quantity = max (0.0 , float (positions .get (symbol , {}).get ("quantity" , 0.0 ) or 0.0 ))
2095+ if (
2096+ qty <= 0
2097+ and limit_price > 0.0
2098+ and investable_buying_power >= limit_price
2099+ and (
2100+ _should_top_up_existing_whole_share_buy (
2101+ symbol ,
2102+ target_gap_value = buy_value ,
2103+ limit_price = limit_price ,
2104+ quantity = held_quantity ,
2105+ )
2106+ or _should_bootstrap_whole_share_buy (
2107+ symbol ,
2108+ target_value = buy_value ,
2109+ limit_price = limit_price ,
2110+ )
2111+ )
2112+ ):
2113+ qty = _floor_order_quantity (1.0 , quantity_step = order_quantity_step )
2114+ if qty > 0 and symbol not in execution_summary ["small_account_whole_share_bootstrap_symbols" ]:
2115+ execution_summary ["small_account_whole_share_bootstrap_symbols" ].append (symbol )
2116+ trade_logs .extend (
2117+ _format_small_account_whole_share_bootstrap_notes (
2118+ (symbol ,),
2119+ translator = translator ,
2120+ )
2121+ )
20712122 if qty <= 0 :
20722123 execution_summary ["orders_skipped" ].append ({"symbol" : symbol , "side" : "buy" , "reason" : "quantity_zero" })
20732124 continue
0 commit comments