|
|
@@ -456,7 +456,7 @@ def test_grid_seed_keeps_other_side_when_one_side_fails(monkeypatch):
|
|
|
assert any("partial success" in line for line in (strategy.state.get("debug_log") or [])) or strategy.state.get("last_error") == "insufficient USD"
|
|
|
|
|
|
|
|
|
-def test_grid_plan_extends_sell_ladder_past_skipped_inner_level(monkeypatch):
|
|
|
+def test_grid_plan_keeps_inner_levels_first(monkeypatch):
|
|
|
class FakeContext:
|
|
|
base_currency = "XRP"
|
|
|
counter_currency = "USD"
|
|
|
@@ -468,7 +468,7 @@ def test_grid_plan_extends_sell_ladder_past_skipped_inner_level(monkeypatch):
|
|
|
return {"maker": 0.0, "taker": 0.0}
|
|
|
|
|
|
def suggest_order_amount(self, **kwargs):
|
|
|
- return 7.29474
|
|
|
+ return 8.0 if kwargs["side"] == "buy" else 7.31
|
|
|
|
|
|
strategy = GridStrategy(FakeContext(), {"grid_levels": 5, "order_call_delay_ms": 0})
|
|
|
monkeypatch.setattr(
|
|
|
@@ -477,12 +477,74 @@ def test_grid_plan_extends_sell_ladder_past_skipped_inner_level(monkeypatch):
|
|
|
lambda center, **kwargs: {"base": 0.00718865, "buy": 0.00718865, "sell": 0.006407884093550591},
|
|
|
)
|
|
|
|
|
|
- plan = strategy._plan_grid(1.3615, base_total=49.035, quote_total=19.77)
|
|
|
+ plan = strategy._plan_grid(1.3615, base_total=100.0, quote_total=100.0)
|
|
|
|
|
|
- assert plan["counts"]["buy"] == 0
|
|
|
+ assert plan["counts"]["buy"] == 5
|
|
|
assert plan["counts"]["sell"] == 5
|
|
|
- assert [order["level"] for order in plan["sell_orders"]] == [2, 3, 4, 5, 6]
|
|
|
- assert (plan["sell_skipped"] or [])[0]["level"] == 1
|
|
|
+ assert [order["level"] for order in plan["buy_orders"]] == [1, 2, 3, 4, 5]
|
|
|
+ assert [order["level"] for order in plan["sell_orders"]] == [1, 2, 3, 4, 5]
|
|
|
+ assert plan["buy_skipped"] == []
|
|
|
+ assert plan["sell_skipped"] == []
|
|
|
+
|
|
|
+
|
|
|
+def test_grid_plan_truncates_outer_levels_without_skipping_inner_levels(monkeypatch):
|
|
|
+ class FakeContext:
|
|
|
+ base_currency = "XRP"
|
|
|
+ counter_currency = "USD"
|
|
|
+ market_symbol = "xrpusd"
|
|
|
+ minimum_order_value = 10.0
|
|
|
+ mode = "active"
|
|
|
+
|
|
|
+ def get_fee_rates(self, market):
|
|
|
+ return {"maker": 0.0, "taker": 0.0}
|
|
|
+
|
|
|
+ def get_account_info(self):
|
|
|
+ return {
|
|
|
+ "balances": [
|
|
|
+ {"asset_code": "USD", "available": 19.77},
|
|
|
+ {"asset_code": "XRP", "available": 49.035},
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ def get_price(self, symbol):
|
|
|
+ return {"price": 1.3615}
|
|
|
+
|
|
|
+ def get_regime(self, symbol, timeframe="1h"):
|
|
|
+ return {"volatility": {"atr_percent": 0.0}, "trend": {"state": "flat"}}
|
|
|
+
|
|
|
+ def suggest_order_amount(self, **kwargs):
|
|
|
+ return 8.0 if kwargs["side"] == "buy" else 7.31
|
|
|
+
|
|
|
+ def get_open_orders(self):
|
|
|
+ return []
|
|
|
+
|
|
|
+ ctx = FakeContext()
|
|
|
+ strategy = GridStrategy(ctx, {"grid_levels": 5, "order_call_delay_ms": 0})
|
|
|
+ strategy.state["center_price"] = 1.3615
|
|
|
+ strategy.state["seeded"] = True
|
|
|
+ strategy.state["orders"] = []
|
|
|
+ strategy.state["order_ids"] = []
|
|
|
+
|
|
|
+ monkeypatch.setattr(strategy, "_refresh_balance_snapshot", lambda: True)
|
|
|
+ monkeypatch.setattr(strategy, "_price", lambda: 1.3615)
|
|
|
+ monkeypatch.setattr(strategy, "_refresh_regimes", lambda: None)
|
|
|
+ monkeypatch.setattr(strategy, "_sync_open_orders_state", lambda: [])
|
|
|
+ monkeypatch.setattr(strategy, "_grid_refresh_paused", lambda: False)
|
|
|
+ monkeypatch.setattr(strategy, "_recenter_threshold_pct", lambda: 0.5)
|
|
|
+ monkeypatch.setattr(
|
|
|
+ strategy,
|
|
|
+ "_effective_grid_steps",
|
|
|
+ lambda center, **kwargs: {"base": 0.00718865, "buy": 0.00718865, "sell": 0.006407884093550591},
|
|
|
+ )
|
|
|
+
|
|
|
+ plan = strategy._plan_grid(1.3615, base_total=100.0, quote_total=35.0)
|
|
|
+
|
|
|
+ assert plan["counts"]["buy"] == 3
|
|
|
+ assert plan["counts"]["sell"] == 5
|
|
|
+ assert [order["level"] for order in plan["buy_orders"]] == [1, 2, 3]
|
|
|
+ assert [order["level"] for order in plan["sell_orders"]] == [1, 2, 3, 4, 5]
|
|
|
+ assert plan["buy_skipped"] == []
|
|
|
+ assert plan["sell_skipped"] == []
|
|
|
|
|
|
|
|
|
def test_grid_shape_check_reuses_canonical_plan_without_rebuild(monkeypatch):
|
|
|
@@ -502,8 +564,8 @@ def test_grid_shape_check_reuses_canonical_plan_without_rebuild(monkeypatch):
|
|
|
def get_account_info(self):
|
|
|
return {
|
|
|
"balances": [
|
|
|
- {"asset_code": "USD", "available": 19.77},
|
|
|
- {"asset_code": "XRP", "available": 12.5613},
|
|
|
+ {"asset_code": "USD", "available": 40.0},
|
|
|
+ {"asset_code": "XRP", "available": 40.0},
|
|
|
]
|
|
|
}
|
|
|
|
|
|
@@ -514,15 +576,20 @@ def test_grid_shape_check_reuses_canonical_plan_without_rebuild(monkeypatch):
|
|
|
return {"volatility": {"atr_percent": 0.0}, "trend": {"state": "flat"}}
|
|
|
|
|
|
def suggest_order_amount(self, **kwargs):
|
|
|
- return 7.29474
|
|
|
+ return 7.7
|
|
|
|
|
|
def get_open_orders(self):
|
|
|
return [
|
|
|
- {"side": "sell", "price": 1.37894867, "amount": 7.29474, "id": "s2"},
|
|
|
- {"side": "sell", "price": 1.387673, "amount": 7.29474, "id": "s3"},
|
|
|
- {"side": "sell", "price": 1.39639734, "amount": 7.29474, "id": "s4"},
|
|
|
- {"side": "sell", "price": 1.40512167, "amount": 7.29474, "id": "s5"},
|
|
|
- {"side": "sell", "price": 1.41384601, "amount": 7.29474, "id": "s6"},
|
|
|
+ {"side": "buy", "price": 1.35171265, "amount": 7.7, "id": "b1"},
|
|
|
+ {"side": "buy", "price": 1.34192531, "amount": 7.7, "id": "b2"},
|
|
|
+ {"side": "buy", "price": 1.33213796, "amount": 7.7, "id": "b3"},
|
|
|
+ {"side": "buy", "price": 1.32235061, "amount": 7.7, "id": "b4"},
|
|
|
+ {"side": "buy", "price": 1.31256327, "amount": 7.7, "id": "b5"},
|
|
|
+ {"side": "sell", "price": 1.37022433, "amount": 7.7, "id": "s1"},
|
|
|
+ {"side": "sell", "price": 1.37894867, "amount": 7.7, "id": "s2"},
|
|
|
+ {"side": "sell", "price": 1.387673, "amount": 7.7, "id": "s3"},
|
|
|
+ {"side": "sell", "price": 1.39639734, "amount": 7.7, "id": "s4"},
|
|
|
+ {"side": "sell", "price": 1.40512167, "amount": 7.7, "id": "s5"},
|
|
|
]
|
|
|
|
|
|
def cancel_all_orders(self):
|
|
|
@@ -534,7 +601,7 @@ def test_grid_shape_check_reuses_canonical_plan_without_rebuild(monkeypatch):
|
|
|
strategy.state["center_price"] = 1.3615
|
|
|
strategy.state["seeded"] = True
|
|
|
strategy.state["orders"] = ctx.get_open_orders()
|
|
|
- strategy.state["order_ids"] = ["s2", "s3", "s4", "s5", "s6"]
|
|
|
+ strategy.state["order_ids"] = ["b1", "b2", "b3", "b4", "b5", "s1", "s2", "s3", "s4", "s5"]
|
|
|
|
|
|
monkeypatch.setattr(
|
|
|
strategy,
|