Ezen az oldalon egy konkrét fájl aktuális állapotát tudod megnézni.
/opt/bots/saturnus/app/tools/edge_case_transitions_test.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
edge_case_transitions_test.py (CANONICAL TEST TOOL)
Read-only edge-case tests for:
- BUY_LOCK transitions (SELL -> BUY -> LOCK)
- Dominance validation (PANIC vs others) + boundary (==) checks
- Extra transitions: SELL under lock, TTL expiry, clean lock lifecycle
NO state.json writes, NO Freqtrade calls.
"""
import sys
import time
APP_DIR = "/opt/bots/saturnus/app"
sys.path.insert(0, APP_DIR)
import tick_runner
import rule_engine
def ok(msg: str) -> None:
print(f"OK - {msg}")
def fail(msg: str) -> None:
print(f"ERR - {msg}")
raise SystemExit(2)
def assert_true(cond: bool, msg_ok: str, msg_fail: str) -> None:
if cond:
ok(msg_ok)
else:
fail(msg_fail)
def mk_levels(base: float, trough: float) -> dict:
# use fixed pct values aligned with default tick_runner env defaults:
# STD_SELL=3%, PROFITLESS_SELL=0.1%, PANIC_SELL=1%
# STD_BUY=3%, PROFITLESS_BUY=-0.1%, PANIC_BUY=1%
return {
"std_sell": base * (1.0 - 0.03),
"profitless_sell": base * (1.0 - 0.001),
"panic_sell": base * (1.0 - 0.01),
"std_buy": trough * (1.0 + 0.03),
"profitless_buy": trough * (1.0 - 0.001),
"panic_buy": trough * (1.0 + 0.01),
}
def decide_ctx(*, in_position: bool, last: float, prev_last: float, base: float, peak: float | None, trough: float | None) -> dict:
levels = mk_levels(base=base, trough=(trough if trough is not None else last))
ctx = {
"in_position": bool(in_position),
"last": float(last),
"prev_last": float(prev_last),
"base": float(base),
"peak": peak,
"trough": trough,
"levels": levels,
}
return rule_engine.decide(ctx)
def test_buy_lock_transitions() -> None:
print("\n=== BUY_LOCK átmenetek (SELL → BUY → LOCK) ===")
# Flat + BUY -> BUY allowed + lock created
state = {"in_position": False, "market": {"pair": "SOL/USDC", "timeframe": "1m", "last": 100.0, "prev_last": 99.0}}
d_buy = {"action": "BUY", "rule": "TEST_BUY", "reason": "BUY_SIGNAL", "level": "std_buy"}
out1 = tick_runner.apply_buy_lock(state, d_buy)
assert_true(out1.get("action") == "BUY",
"Flat BUY -> decision BUY",
"Flat BUY -> decision not BUY")
locks = state.get("locks") if isinstance(state.get("locks"), dict) else {}
assert_true(isinstance(locks.get("buy_pending"), dict),
"Flat BUY -> buy_pending lock létrejött",
"Flat BUY -> buy_pending lock missing")
# Flat + BUY again -> HOLD with BUY_LOCK
out2 = tick_runner.apply_buy_lock(state, d_buy)
assert_true(out2.get("action") == "HOLD",
"Lock mellett BUY -> HOLD",
"Lock mellett BUY -> not HOLD")
assert_true(out2.get("rule") == "BUY_LOCK",
"Lock mellett BUY -> rule=BUY_LOCK",
"Lock mellett BUY -> rule not BUY_LOCK")
# Any non-BUY decision clears lock
out3 = tick_runner.apply_buy_lock(state, {"action": "HOLD", "rule": "TEST_HOLD", "reason": "HOLD"})
locks2 = state.get("locks") if isinstance(state.get("locks"), dict) else {}
assert_true(locks2.get("buy_pending") is None,
"HOLD mellett lock törlődik",
"HOLD mellett lock nem törlődött")
# In_position clears lock and does not lock decisions
state2 = {"in_position": False, "market": {"pair": "SOL/USDC", "timeframe": "1m", "last": 100.0, "prev_last": 99.0}}
tick_runner.apply_buy_lock(state2, d_buy)
state2["in_position"] = True
out4 = tick_runner.apply_buy_lock(state2, d_buy)
locks3 = state2.get("locks") if isinstance(state2.get("locks"), dict) else {}
assert_true(locks3.get("buy_pending") is None,
"In_position -> lock törlődik",
"In_position -> lock nem törlődött")
assert_true(out4.get("action") == "BUY",
"In_position esetén döntés nem lockkolódik",
"In_position esetén döntés lockkolódott")
def test_dominance_validation() -> None:
print("\n=== Dominancia validáció (RuleEngine) ===")
base = 100.0
peak = 110.0