11from __future__ import annotations
22
3+ import time
34from typing import Any , Callable , Iterable
45
56from .market_data import fetch_last_prices
@@ -11,19 +12,61 @@ def fetch_strategy_account_state(
1112 strategy_assets : Iterable [str ],
1213 * ,
1314 position_log_fn : Callable [[str ], None ] | None = None ,
15+ warning_log_fn : Callable [[str ], None ] | None = None ,
1416) -> dict [str , Any ]:
17+ def warn (message : str ) -> None :
18+ if warning_log_fn is not None :
19+ warning_log_fn (message )
20+
21+ def load_account_balance () -> tuple [Any , ...]:
22+ attempts = (
23+ ("all" , {}),
24+ ("USD" , {"currency" : "USD" }),
25+ ("HKD" , {"currency" : "HKD" }),
26+ ("CNH" , {"currency" : "CNH" }),
27+ )
28+ errors : list [str ] = []
29+ for label , kwargs in attempts :
30+ for attempt in range (1 , 4 ):
31+ try :
32+ account_balance = t_ctx .account_balance (** kwargs )
33+ except TypeError as exc :
34+ errors .append (f"{ label } =TypeError:{ exc } " )
35+ break
36+ except Exception as exc :
37+ errors .append (f"{ label } [attempt={ attempt } ]={ type (exc ).__name__ } :{ exc } " )
38+ if attempt < 3 :
39+ warn (
40+ "[longbridge_account_balance_retrying] "
41+ f"currency={ label } attempt={ attempt } /3 error_type={ type (exc ).__name__ } "
42+ )
43+ time .sleep (0.5 * attempt )
44+ continue
45+ break
46+
47+ if kwargs :
48+ warn (f"[longbridge_account_balance_retry_succeeded] currency={ label } " )
49+ return tuple (account_balance or ())
50+
51+ if errors :
52+ warn ("[longbridge_account_balance_failed] " + " | " .join (errors ))
53+ return ()
54+
1555 available_cash = 0.0
1656 cash_by_currency : dict [str , float ] = {}
17- account_balance = t_ctx . account_balance ()
57+ account_balance = load_account_balance ()
1858 for account in account_balance :
59+ account_buy_power = max (0.0 , float (getattr (account , "buy_power" , 0.0 ) or 0.0 ))
60+ account_usd_cash = 0.0
1961 for cash_info in getattr (account , "cash_infos" , []):
2062 currency = str (getattr (cash_info , "currency" , "" ) or "" ).strip ().upper ()
2163 if not currency :
2264 continue
2365 cash_amount = float (getattr (cash_info , "available_cash" , 0.0 ))
2466 cash_by_currency [currency ] = cash_by_currency .get (currency , 0.0 ) + cash_amount
2567 if currency == "USD" :
26- available_cash += cash_amount
68+ account_usd_cash += cash_amount
69+ available_cash += max (account_buy_power , account_usd_cash )
2770
2871 assets = [str (symbol ).strip ().upper () for symbol in strategy_assets if str (symbol ).strip ()]
2972 market_values = {symbol : 0.0 for symbol in assets }
@@ -32,7 +75,25 @@ def fetch_strategy_account_state(
3275 filter_enabled = bool (assets )
3376
3477 position_rows : list [tuple [str , str , Any , Any ]] = []
35- positions_response = t_ctx .stock_positions ()
78+ positions_response = None
79+ position_errors : list [str ] = []
80+ for attempt in range (1 , 4 ):
81+ try :
82+ positions_response = t_ctx .stock_positions ()
83+ break
84+ except Exception as exc :
85+ position_errors .append (f"attempt={ attempt } { type (exc ).__name__ } :{ exc } " )
86+ if attempt < 3 :
87+ warn (
88+ "[longbridge_stock_positions_retrying] "
89+ f"attempt={ attempt } /3 error_type={ type (exc ).__name__ } "
90+ )
91+ time .sleep (0.5 * attempt )
92+ continue
93+ warn (
94+ "[longbridge_stock_positions_failed] "
95+ f"errors={ ' | ' .join (position_errors )} "
96+ )
3697 if positions_response and hasattr (positions_response , "channels" ):
3798 for channel in positions_response .channels :
3899 for position in getattr (channel , "positions" , []):
0 commit comments