|
|
@@ -336,10 +336,13 @@ class Strategy(Strategy):
|
|
|
info = self.context.get_account_info()
|
|
|
except Exception as exc:
|
|
|
self._log(f"account info failed: {exc}")
|
|
|
+ # A failed balance read makes this tick unsuitable for shape decisions.
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
return 0.0
|
|
|
|
|
|
balances = info.get("balances") if isinstance(info, dict) else []
|
|
|
if not isinstance(balances, list):
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
return 0.0
|
|
|
wanted = str(asset_code or "").upper()
|
|
|
for balance in balances:
|
|
|
@@ -350,7 +353,9 @@ class Strategy(Strategy):
|
|
|
try:
|
|
|
return float(balance.get("available") if balance.get("available") is not None else balance.get("total") or 0.0)
|
|
|
except Exception:
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
return 0.0
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
return 0.0
|
|
|
|
|
|
def _refresh_balance_snapshot(self) -> bool:
|
|
|
@@ -358,10 +363,12 @@ class Strategy(Strategy):
|
|
|
info = self.context.get_account_info()
|
|
|
except Exception as exc:
|
|
|
self._log(f"balance refresh failed: {exc}")
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
return False
|
|
|
|
|
|
balances = info.get("balances") if isinstance(info, dict) else []
|
|
|
if not isinstance(balances, list):
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
return False
|
|
|
|
|
|
base = self._base_symbol()
|
|
|
@@ -373,6 +380,7 @@ class Strategy(Strategy):
|
|
|
try:
|
|
|
available = float(balance.get("available") if balance.get("available") is not None else balance.get("total") or 0.0)
|
|
|
except Exception:
|
|
|
+ self.state["balance_shape_inconclusive"] = True
|
|
|
continue
|
|
|
if asset == base:
|
|
|
self.state["base_available"] = available
|
|
|
@@ -773,6 +781,7 @@ class Strategy(Strategy):
|
|
|
previous_orders = list(self.state.get("orders") or [])
|
|
|
tracked_ids_before_sync = list(self.state.get("order_ids") or [])
|
|
|
rebuild_done = False
|
|
|
+ self.state["balance_shape_inconclusive"] = False
|
|
|
balance_refresh_ok = self._refresh_balance_snapshot()
|
|
|
price = self._price()
|
|
|
self.state["last_price"] = price
|
|
|
@@ -892,6 +901,7 @@ class Strategy(Strategy):
|
|
|
target_buy = self._target_levels_for_side("buy", price, live_orders, total_quote, expected_levels, float(self.context.minimum_order_value or 0.0))
|
|
|
target_sell = self._target_levels_for_side("sell", price, live_orders, total_base, expected_levels, float(self.context.minimum_order_value or 0.0))
|
|
|
target_total = target_buy + target_sell
|
|
|
+ balance_shape_inconclusive = bool(self.state.get("balance_shape_inconclusive"))
|
|
|
grid_not_as_expected = (
|
|
|
bool(live_orders)
|
|
|
and (
|
|
|
@@ -902,7 +912,9 @@ class Strategy(Strategy):
|
|
|
|
|
|
can_make_better = target_total > 0 and (current_buy != target_buy or current_sell != target_sell)
|
|
|
|
|
|
- if grid_not_as_expected and can_make_better and not self._grid_refresh_paused():
|
|
|
+ if balance_shape_inconclusive:
|
|
|
+ self._log("balance info not conclusive, skipping grid shape rebuild checks this tick")
|
|
|
+ elif grid_not_as_expected and can_make_better and not self._grid_refresh_paused():
|
|
|
if rebuild_done:
|
|
|
return {"action": "hold", "price": price}
|
|
|
self._log(
|
|
|
@@ -916,12 +928,12 @@ class Strategy(Strategy):
|
|
|
self.state["last_action"] = "reseeded" if mode == "active" else f"{mode} monitor"
|
|
|
return {"action": "reseed" if mode == "active" else "plan", "price": price}
|
|
|
|
|
|
- if grid_not_as_expected and not can_make_better:
|
|
|
+ if not balance_shape_inconclusive and grid_not_as_expected and not can_make_better:
|
|
|
self._log(
|
|
|
f"grid shape left unchanged, balance cannot improve it: live_buy={current_buy} live_sell={current_sell} target_buy={target_buy} target_sell={target_sell}"
|
|
|
)
|
|
|
|
|
|
- if self._order_count_mismatch(tracked_ids_before_sync, live_orders):
|
|
|
+ if not balance_shape_inconclusive and self._order_count_mismatch(tracked_ids_before_sync, live_orders):
|
|
|
if rebuild_done:
|
|
|
return {"action": "hold", "price": price}
|
|
|
self._log(f"grid mismatch detected, rebuilding full grid: tracked={len(tracked_ids_before_sync)} live={len(live_orders)}")
|
|
|
@@ -942,7 +954,7 @@ class Strategy(Strategy):
|
|
|
self._log(f"{'seeded' if mode == 'active' else 'planned'} grid at {price}")
|
|
|
return {"action": "seed" if mode == "active" else "plan", "price": price}
|
|
|
|
|
|
- if ((open_order_count == 0) or missing_tracked) and not self._grid_refresh_paused():
|
|
|
+ if not balance_shape_inconclusive and ((open_order_count == 0) or missing_tracked) and not self._grid_refresh_paused():
|
|
|
if rebuild_done:
|
|
|
return {"action": "hold", "price": price}
|
|
|
self._log("missing tracked order(s), rebuilding full grid")
|