Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1329,8 +1329,10 @@ async def fetch_positions(self, symbols: Optional[List[str]] = None) -> List[Dic
positions = await exchange.fetch_positions(normalized_symbols)
return positions
except Exception as e:
print(f"Warning: Could not fetch positions: {e}")
return []
# Do NOT return an empty list on network/exchange errors; propagate
# so upstream retry/backoff logic can kick in and avoid wiping holdings.
logger.warning(f"⚠️ Could not fetch positions: {e}")
raise

async def cancel_order(self, order_id: str, symbol: str) -> Dict:
"""Cancel an open order.
Expand Down
14 changes: 11 additions & 3 deletions python/valuecell/agents/common/trading/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ async def fetch_free_cash_from_gateway(
logger.info("Fetching exchange balance for LIVE trading mode")
try:
if not hasattr(execution_gateway, "fetch_balance"):
return 0.0, 0.0
raise AttributeError(
f"Execution gateway {execution_gateway.__class__.__name__} "
"does not implement the required 'fetch_balance' method."
)
balance = await execution_gateway.fetch_balance()
except Exception as e:
if retry_cnt < max_retries:
Expand All @@ -44,11 +47,12 @@ async def fetch_free_cash_from_gateway(
return await fetch_free_cash_from_gateway(
execution_gateway, symbols, retry_cnt + 1, max_retries
)
# Propagate after exhausting retries so upstream can keep cached portfolio
logger.error(
f"Failed to fetch free cash from exchange after {max_retries} retries, returning 0.0",
f"Failed to fetch free cash from exchange after {max_retries} retries.",
exception=e,
)
return 0.0, 0.0
raise

logger.info(f"Raw balance response: {balance}")
free_map: dict[str, float] = {}
Expand All @@ -70,6 +74,10 @@ async def fetch_free_cash_from_gateway(
except Exception:
continue

# If balance structure is unrecognized, avoid returning zeros silently
if not isinstance(balance, dict) or (not free_map and free_section is None):
raise ValueError("Unrecognized balance response shape from exchange")

logger.info(f"Parsed free balance map: {free_map}")
# Derive quote currencies from symbols, fallback to common USD-stable quotes
quotes: list[str] = []
Expand Down