Fájl részletek
Ezen az oldalon egy konkrét fájl aktuális állapotát tudod megnézni.
Fájl útvonala
/opt/bots/saturnus/app/rule_engine.py
Aktuális státusz
MODIFIED
Módosítás ideje
1776173717.205729
Korábbi baseline időpont
1776107087.9101276
Előnézet (első 120 sor)
# rule_engine.py
# Saturnus – kanonikus rule engine
# Fő elvek:
# - normál trade-pár ne legyen veszteséges, ha ezt a piac megengedi
# - panic trade után recovery mód kötelező
# - recovery BUY csak akkor engedhető, ha a következő SELL oldalon
# a korábbi veszteség visszahozható
# - recovery SELL csak akkor engedhető, ha ténylegesen visszahozza
# a korábbi veszteséget
# - panic szabályokra kötelező az MA + iránykényszer
# - catastrophe szabályok külön végső védelmi ágként maradnak
from __future__ import annotations
from typing import Any, Dict, List, Optional, Tuple
DEFAULT_CFG = {
"STD_SELL_RETRACE_PCT": 0.006,
"STD_BUY_REBOUND_PCT": 0.006,
"RECOVERY_SELL_RETRACE_PCT": 0.004,
"RECOVERY_BUY_REBOUND_PCT": 0.012,
"PANIC_OFFSET_PCT": 0.03,
"CATASTROPHE_OFFSET_PCT": 0.10,
"MIN_REBOUND_CONFIRM_PCT": 0.003,
"RECOVERY_ARM_TIMEOUT_BARS": 240,
"SPREAD_MAX_PCT": 0.05,
"STALENESS_MAX_MS": 999999999,
"PROFIT_BUFFER_PCT": 0.001,
"COST_GUARD_ENABLED": True,
"PANIC_RECOVERY_EXTRA_GAP_PCT": 0.002,
"SIDEWAYS_MA_GAP_PCT": 0.0015,
"RECOVERY_PROFIT_TARGET_PCT": 0.003,
"RECOVERY_BUY_MAX_ABOVE_BASE_PCT": 0.0,
}
def _f(value: Any) -> Optional[float]:
try:
if value is None:
return None
return float(value)
except Exception:
return None
def _b(value: Any, default: bool = False) -> bool:
if isinstance(value, bool):
return value
if value is None:
return default
if isinstance(value, str):
v = value.strip().lower()
if v in ("1", "true", "yes", "on"):
return True
if v in ("0", "false", "no", "off"):
return False
return bool(value)
def _cfg_value(params: Dict[str, Any], *keys: str, default: Any, cast=float):
for key in keys:
if key in params and params.get(key) is not None:
try:
return cast(params.get(key))
except Exception:
pass
return default
def _hold(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
payload = {
"action": "HOLD",
"rule": "HOLD",
"reason": reason,
"trigger_level": None,
"gate_reason": "",
"data": data or {},
"decision_action": "HOLD",
"decision_rule_id": "HOLD",
"decision_level": None,
"decision_reason": reason,
}
return payload
def _no_trade(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
return _hold(reason, data or {})
def _sell(rule_id: str, level: float, reason: str, data: Dict[str, Any]) -> Dict[str, Any]:
payload = {
"action": "SELL",
"rule": rule_id,
"reason": reason,
"trigger_level": level,
"gate_reason": "",
"data": data,
"decision_action": "SELL",
"decision_rule_id": rule_id,
"decision_level": level,
"decision_reason": reason,
}
return payload
def _buy(rule_id: str, level: float, reason: str, data: Dict[str, Any]) -> Dict[str, Any]:
payload = {
"action": "BUY",
"rule": rule_id,
"reason": reason,
"trigger_level": level,
"gate_reason": "",
"data": data,
"decision_action": "BUY",
"decision_rule_id": rule_id,
"decision_level": level,
"decision_reason": reason,
}
return payload
Csak változott diff sorok
--- baseline
+++ current
@@ -1,52 +1,39 @@
-# Saturnus v2 – recovery-alapú, 6 szabályos döntési motor
-# Kompatibilis wrapperrel a meglévő egyparaméteres decide(raw_state) hívásokhoz
+# Saturnus – kanonikus rule engine
+# Fő elvek:
+# - normál trade-pár ne legyen veszteséges, ha ezt a piac megengedi
+# - panic trade után recovery mód kötelező
+# - recovery BUY csak akkor engedhető, ha a következő SELL oldalon
+# a korábbi veszteség visszahozható
+# - recovery SELL csak akkor engedhető, ha ténylegesen visszahozza
+# a korábbi veszteséget
+# - panic szabályokra kötelező az MA + iránykényszer
+# - catastrophe szabályok külön végső védelmi ágként maradnak
-from typing import Any, Dict, Optional, Tuple
-
-
-# =========================================================
-# Default config
-# =========================================================
-
-DEFAULT_CFG: Dict[str, Any] = {
- "STD_SELL_RETRACE_PCT": 0.010,
- "STD_BUY_REBOUND_PCT": 0.010,
- "RECOVERY_SELL_RETRACE_PCT": 0.006,
- "RECOVERY_BUY_REBOUND_PCT": 0.006,
- "PANIC_OFFSET_PCT": 0.020,
- "CATASTROPHE_OFFSET_PCT": 0.120,
- "MIN_REBOUND_CONFIRM_PCT": 0.002,
+from typing import Any, Dict, List, Optional, Tuple
+
+
+DEFAULT_CFG = {
+ "STD_SELL_RETRACE_PCT": 0.006,
+ "STD_BUY_REBOUND_PCT": 0.006,
+ "RECOVERY_SELL_RETRACE_PCT": 0.004,
+ "RECOVERY_BUY_REBOUND_PCT": 0.012,
+ "PANIC_OFFSET_PCT": 0.03,
+ "CATASTROPHE_OFFSET_PCT": 0.10,
+ "MIN_REBOUND_CONFIRM_PCT": 0.003,
- "SPREAD_MAX_PCT": 0.0020,
- "STALENESS_MAX_MS": 500,
- "PROFIT_BUFFER_PCT": 0.0010,
+ "SPREAD_MAX_PCT": 0.05,
+ "STALENESS_MAX_MS": 999999999,
+ "PROFIT_BUFFER_PCT": 0.001,
- "RECOVERY_PROFIT_TARGET_PCT": 0.001,
- "RECOVERY_BUY_MAX_ABOVE_BASE_PCT": 0.006,
+ "RECOVERY_PROFIT_TARGET_PCT": 0.003,
+ "RECOVERY_BUY_MAX_ABOVE_BASE_PCT": 0.0,
-
-SELL_PRIORITY = {
- "SELL_PANIC": 1,
- "SELL_STANDARD": 2,
- "SELL_RECOVERY": 3,
-}
-
-BUY_PRIORITY = {
- "BUY_PANIC": 1,
- "BUY_STANDARD": 2,
- "BUY_RECOVERY": 3,
-}
-
-
-# =========================================================
-# Helpers
-# =========================================================
@@ -71,86 +58,123 @@
-def _decision(
- *,
- action: str,
- rule: str,
- reason: str,
- trigger_level: Optional[float] = None,
- gate_reason: str = "",
- data: Optional[Dict[str, Any]] = None,
-) -> Dict[str, Any]:
- return {
- "action": action,
- "rule": rule,
+def _cfg_value(params: Dict[str, Any], *keys: str, default: Any, cast=float):
+ for key in keys:
+ if key in params and params.get(key) is not None:
+ try:
+ return cast(params.get(key))
+ except Exception:
+ pass
+ return default
+
+
+def _hold(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
+ payload = {
+ "action": "HOLD",
+ "rule": "HOLD",
- "trigger_level": trigger_level,
- "gate_reason": gate_reason,
+ "trigger_level": None,
+ "gate_reason": "",
- # új mezők a monitor/export felé
- "decision_action": action,
- "decision_rule_id": rule,
- "decision_level": trigger_level,
+ "decision_action": "HOLD",
+ "decision_rule_id": "HOLD",
+ "decision_level": None,
-
-
-def _hold(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(action="HOLD", rule="HOLD", reason=reason, trigger_level=None, data=data)
-
-
-def _no_trade(gate_reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(
- action="NO_TRADE",
- rule="NO_TRADE",
- reason=gate_reason,
- trigger_level=None,
- gate_reason=gate_reason,
- data=data,
- )
-
-
-def _buy(rule: str, level: float, reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(action="BUY", rule=rule, reason=reason, trigger_level=level, data=data)
-
-
-def _sell(rule: str, level: float, reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(action="SELL", rule=rule, reason=reason, trigger_level=level, data=data)
-
-
-def _choose_sell(active_rules: list[Tuple[str, float]]) -> Tuple[str, float]:
- # SELL: legmagasabb árszint nyer
- # tie-break: RECOVERY > STANDARD > PANIC
- return sorted(active_rules, key=lambda x: (x[1], SELL_PRIORITY.get(x[0], 0)), reverse=True)[0]
-
-
-def _choose_buy(active_rules: list[Tuple[str, float]]) -> Tuple[str, float]:
- # BUY: legalacsonyabb árszint nyer
- # tie-break: RECOVERY > STANDARD > PANIC
- return sorted(active_rules, key=lambda x: (x[1], -BUY_PRIORITY.get(x[0], 0)))[0]
-
-
-# =========================================================
-# Normalization / compatibility layer
-# =========================================================
+ return payload
+
+
+def _no_trade(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
+ return _hold(reason, data or {})
+
+
+def _sell(rule_id: str, level: float, reason: str, data: Dict[str, Any]) -> Dict[str, Any]:
+ payload = {
+ "action": "SELL",
+ "rule": rule_id,
+ "reason": reason,
+ "trigger_level": level,
+ "gate_reason": "",
+ "data": data,
+ "decision_action": "SELL",
+ "decision_rule_id": rule_id,
+ "decision_level": level,
+ "decision_reason": reason,
+ }
+ return payload
+
+
+def _buy(rule_id: str, level: float, reason: str, data: Dict[str, Any]) -> Dict[str, Any]:
+ payload = {
+ "action": "BUY",
+ "rule": rule_id,
+ "reason": reason,
+ "trigger_level": level,
+ "gate_reason": "",
+ "data": data,
+ "decision_action": "BUY",
+ "decision_rule_id": rule_id,
+ "decision_level": level,
Teljes diff
--- baseline
+++ current
@@ -1,52 +1,39 @@
# rule_engine.py
-# Saturnus v2 – recovery-alapú, 6 szabályos döntési motor
-# Kompatibilis wrapperrel a meglévő egyparaméteres decide(raw_state) hívásokhoz
+# Saturnus – kanonikus rule engine
+# Fő elvek:
+# - normál trade-pár ne legyen veszteséges, ha ezt a piac megengedi
+# - panic trade után recovery mód kötelező
+# - recovery BUY csak akkor engedhető, ha a következő SELL oldalon
+# a korábbi veszteség visszahozható
+# - recovery SELL csak akkor engedhető, ha ténylegesen visszahozza
+# a korábbi veszteséget
+# - panic szabályokra kötelező az MA + iránykényszer
+# - catastrophe szabályok külön végső védelmi ágként maradnak
from __future__ import annotations
-from typing import Any, Dict, Optional, Tuple
-
-
-# =========================================================
-# Default config
-# =========================================================
-
-DEFAULT_CFG: Dict[str, Any] = {
- "STD_SELL_RETRACE_PCT": 0.010,
- "STD_BUY_REBOUND_PCT": 0.010,
- "RECOVERY_SELL_RETRACE_PCT": 0.006,
- "RECOVERY_BUY_REBOUND_PCT": 0.006,
- "PANIC_OFFSET_PCT": 0.020,
- "CATASTROPHE_OFFSET_PCT": 0.120,
- "MIN_REBOUND_CONFIRM_PCT": 0.002,
+from typing import Any, Dict, List, Optional, Tuple
+
+
+DEFAULT_CFG = {
+ "STD_SELL_RETRACE_PCT": 0.006,
+ "STD_BUY_REBOUND_PCT": 0.006,
+ "RECOVERY_SELL_RETRACE_PCT": 0.004,
+ "RECOVERY_BUY_REBOUND_PCT": 0.012,
+ "PANIC_OFFSET_PCT": 0.03,
+ "CATASTROPHE_OFFSET_PCT": 0.10,
+ "MIN_REBOUND_CONFIRM_PCT": 0.003,
"RECOVERY_ARM_TIMEOUT_BARS": 240,
- "SPREAD_MAX_PCT": 0.0020,
- "STALENESS_MAX_MS": 500,
- "PROFIT_BUFFER_PCT": 0.0010,
+ "SPREAD_MAX_PCT": 0.05,
+ "STALENESS_MAX_MS": 999999999,
+ "PROFIT_BUFFER_PCT": 0.001,
"COST_GUARD_ENABLED": True,
"PANIC_RECOVERY_EXTRA_GAP_PCT": 0.002,
"SIDEWAYS_MA_GAP_PCT": 0.0015,
- "RECOVERY_PROFIT_TARGET_PCT": 0.001,
- "RECOVERY_BUY_MAX_ABOVE_BASE_PCT": 0.006,
+ "RECOVERY_PROFIT_TARGET_PCT": 0.003,
+ "RECOVERY_BUY_MAX_ABOVE_BASE_PCT": 0.0,
}
-
-SELL_PRIORITY = {
- "SELL_PANIC": 1,
- "SELL_STANDARD": 2,
- "SELL_RECOVERY": 3,
-}
-
-BUY_PRIORITY = {
- "BUY_PANIC": 1,
- "BUY_STANDARD": 2,
- "BUY_RECOVERY": 3,
-}
-
-
-# =========================================================
-# Helpers
-# =========================================================
def _f(value: Any) -> Optional[float]:
try:
@@ -71,86 +58,123 @@
return bool(value)
-def _decision(
- *,
- action: str,
- rule: str,
- reason: str,
- trigger_level: Optional[float] = None,
- gate_reason: str = "",
- data: Optional[Dict[str, Any]] = None,
-) -> Dict[str, Any]:
- return {
- "action": action,
- "rule": rule,
+def _cfg_value(params: Dict[str, Any], *keys: str, default: Any, cast=float):
+ for key in keys:
+ if key in params and params.get(key) is not None:
+ try:
+ return cast(params.get(key))
+ except Exception:
+ pass
+ return default
+
+
+def _hold(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
+ payload = {
+ "action": "HOLD",
+ "rule": "HOLD",
"reason": reason,
- "trigger_level": trigger_level,
- "gate_reason": gate_reason,
+ "trigger_level": None,
+ "gate_reason": "",
"data": data or {},
- # új mezők a monitor/export felé
- "decision_action": action,
- "decision_rule_id": rule,
- "decision_level": trigger_level,
+ "decision_action": "HOLD",
+ "decision_rule_id": "HOLD",
+ "decision_level": None,
"decision_reason": reason,
}
-
-
-def _hold(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(action="HOLD", rule="HOLD", reason=reason, trigger_level=None, data=data)
-
-
-def _no_trade(gate_reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(
- action="NO_TRADE",
- rule="NO_TRADE",
- reason=gate_reason,
- trigger_level=None,
- gate_reason=gate_reason,
- data=data,
- )
-
-
-def _buy(rule: str, level: float, reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(action="BUY", rule=rule, reason=reason, trigger_level=level, data=data)
-
-
-def _sell(rule: str, level: float, reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
- return _decision(action="SELL", rule=rule, reason=reason, trigger_level=level, data=data)
-
-
-def _choose_sell(active_rules: list[Tuple[str, float]]) -> Tuple[str, float]:
- # SELL: legmagasabb árszint nyer
- # tie-break: RECOVERY > STANDARD > PANIC
- return sorted(active_rules, key=lambda x: (x[1], SELL_PRIORITY.get(x[0], 0)), reverse=True)[0]
-
-
-def _choose_buy(active_rules: list[Tuple[str, float]]) -> Tuple[str, float]:
- # BUY: legalacsonyabb árszint nyer
- # tie-break: RECOVERY > STANDARD > PANIC
- return sorted(active_rules, key=lambda x: (x[1], -BUY_PRIORITY.get(x[0], 0)))[0]
-
-
-# =========================================================
-# Normalization / compatibility layer
-# =========================================================
+ return payload
+
+
+def _no_trade(reason: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
+ return _hold(reason, data or {})
+
+
+def _sell(rule_id: str, level: float, reason: str, data: Dict[str, Any]) -> Dict[str, Any]:
+ payload = {
+ "action": "SELL",
+ "rule": rule_id,
+ "reason": reason,
+ "trigger_level": level,
+ "gate_reason": "",
+ "data": data,
+ "decision_action": "SELL",
+ "decision_rule_id": rule_id,
+ "decision_level": level,
+ "decision_reason": reason,
+ }
+ return payload
+
+
+def _buy(rule_id: str, level: float, reason: str, data: Dict[str, Any]) -> Dict[str, Any]:
+ payload = {
+ "action": "BUY",
+ "rule": rule_id,
+ "reason": reason,
+ "trigger_level": level,
+ "gate_reason": "",
+ "data": data,
+ "decision_action": "BUY",
+ "decision_rule_id": rule_id,
+ "decision_level": level,
... [DIFF LEVÁGVA] további sorok: 840