Ezen az oldalon egy konkrét fájl aktuális állapotát tudod megnézni.
Előnézet (első 120 sor)
#!/usr/bin/env python3
import csv
import io
import json
import math
import hashlib
import subprocess
from datetime import datetime, timezone
from pathlib import Path
from types import SimpleNamespace
from zoneinfo import ZoneInfo
from flask import Flask, render_template, send_file, jsonify, request, redirect, url_for
import sys
sys.path.insert(0, "/opt/bots/saturnus/app")
from rules_loader import load_rules_doc, load_lab_override_doc
from rules_merge import build_effective_rules
from rule_engine import decide as shared_rule_decide, normalize_state as shared_normalize_state, compute_levels as shared_compute_levels
APP_DIR = Path("/opt/bots/saturnus_monitor/app")
DATA_DIR = Path("/opt/bots/saturnus_monitor/data")
STATE_PATH = Path("/opt/bots/saturnus/state.json")
CONFIG_PATH = Path("/opt/bots/saturnus/freqtrade/user_data/config.json")
PRICES_CSV = DATA_DIR / "prices.csv"
EVENTS_CSV = DATA_DIR / "events.csv"
TRADE_EVENTS_CSV = DATA_DIR / "trade_events.csv"
ANALYSIS_JSON = DATA_DIR / "analysis_summary.json"
ANALYSIS_SCRIPT = Path("/opt/bots/saturnus_monitor/scripts/analyze_monitor_data.py")
FILE_AUDIT_JSON = DATA_DIR / "files_audit_snapshot.json"
app = Flask(__name__, template_folder=str(APP_DIR / "templates"))
# ------------------------------------------------
# alap segédfüggvények
# ------------------------------------------------
def read_json(path: Path):
if not path.exists():
return {}
try:
return json.loads(path.read_text(encoding="utf-8"))
except Exception:
return {}
def read_csv_rows(path: Path):
if not path.exists():
return []
try:
with path.open("r", encoding="utf-8", newline="") as f:
return list(csv.DictReader(f))
except Exception:
return []
def to_float(value, default=None):
try:
if value is None or value == "":
return default
return float(value)
except Exception:
return default
def fmt_num(value, digits=4):
if value is None:
return "—"
try:
return f"{float(value):,.{digits}f}".replace(",", " ")
except Exception:
return "—"
def fmt_pct(value, digits=2):
if value is None:
return "—"
try:
return f"{float(value):.{digits}f}%"
except Exception:
return "—"
def safe_tail(rows, n):
if not rows:
return []
return rows[-n:]
def first_nonempty(row, keys, default=""):
for key in keys:
val = row.get(key)
if val not in (None, ""):
return val
return default
def parse_bool_arg(name: str, default: bool) -> bool:
values = request.args.getlist(name)
if not values:
return default
last = values[-1]
return str(last).strip().lower() in ("1", "true", "yes", "on")
def parse_float_arg(name: str, default: float, min_value=None, max_value=None) -> float:
value = to_float(request.args.get(name), default)
if value is None:
value = default
if min_value is not None and value < min_value:
value = min_value
if max_value is not None and value > max_value:
value = max_value
return value
Csak változott diff sorok
--- baseline
+++ current
@@ -118,16 +118,21 @@
-
- raw = (request.args.get("rows_limit", "500") or "500").strip().lower()
- allowed = {"100", "250", "500", "1000", "2000", "all"}
- if raw not in allowed:
- raw = "500"
- if raw == "all":
+ raw = (request.args.get("rows_limit", "all") or "all").strip().lower()
+
+ if raw in ("", "all", "max", "full", "10d", "10days"):
- return int(raw), raw
-
+
+ try:
+ value = int(raw)
+ except Exception:
+ return None, "all"
+
+ if value <= 0:
+ return None, "all"
+
+ return value, str(value)
@@ -197,11 +202,9 @@
- "BUY_PROFITLESS": "BUY_RECOVERY",
- "BUY_PANIC": "BUY_PANIC",
+ "BUY_PANIC": "BUY_PANIC",
- "SELL_PROFITLESS": "SELL_RECOVERY",
- "SELL_PANIC": "SELL_PANIC",
+ "SELL_PANIC": "SELL_PANIC",
@@ -402,9 +405,7 @@
- "SELL_PROFITLESS": "SELL_RECOVERY",
- "PROFITLESS_SELL": "SELL_RECOVERY",
- "RECOVERY_SELL": "SELL_RECOVERY",
+ "RECOVERY_SELL": "SELL_RECOVERY",
@@ -414,9 +415,7 @@
- "BUY_PROFITLESS": "BUY_RECOVERY",
- "PROFITLESS_BUY": "BUY_RECOVERY",
- "RECOVERY_BUY": "BUY_RECOVERY",
+ "RECOVERY_BUY": "BUY_RECOVERY",
@@ -442,16 +441,14 @@
- "PROFITLESS SELL": "recovery_sell",
- "SELL_STANDARD": "std_sell",
+ "SELL_STANDARD": "std_sell",
- "PROFITLESS BUY": "recovery_buy",
- "BUY_STANDARD": "std_buy",
+ "BUY_STANDARD": "std_buy",
@@ -541,8 +538,6 @@
-
- note = str(note).replace("PROFITLESS", "RECOVERY").replace("profitless", "recovery")
@@ -944,7 +939,7 @@
- value = float(default_internal)
+ value = float(default_internal or 0.0)
@@ -991,24 +986,26 @@
- "std_sell_pct": parse_percent_arg("std_sell_pct", 0.03, 0.0),
+ "std_sell_pct": parse_percent_arg("std_sell_pct", None, 0.0),
- "panic_sell_pct": parse_percent_arg("panic_sell_pct", 0.01, 0.0),
+ "panic_sell_pct": parse_percent_arg("panic_sell_pct", None, 0.0),
- "std_buy_pct": parse_percent_arg("std_buy_pct", 0.03, 0.0),
+ "std_buy_pct": parse_percent_arg("std_buy_pct", None, 0.0),
- "panic_buy_pct": parse_percent_arg("panic_buy_pct", 0.01, 0.0),
+ "panic_buy_pct": parse_percent_arg("panic_buy_pct", None, 0.0),
+ "sell_reversal_min_pct": parse_percent_arg("sell_reversal_min_pct", 0.0, 0.0),
+ "ma_sideways_band_pct": parse_percent_arg("ma_sideways_band_pct", 0.0005, 0.0),
@@ -1030,6 +1027,7 @@
+ params_ns.sell_reversal_min_pct_ui = format_percent_ui_from_internal(params_ns.sell_reversal_min_pct, 3)
@@ -1055,18 +1053,17 @@
- if str(getattr(params, "rules_source", "lab") or "lab").strip().lower() == "lab":
- params.rules_profile_type = "lab-effective"
- params.rules_profile_name = "lab"
- params.rules_source_label = "LAB nézet / kézi űrlap paraméterek"
- return params
-
- # FONTOS:
- # LAB módban a felhasználó által beírt űrlapérték az elsődleges.
- # CANONICAL módban az effective threshold sorokból töltjük vissza
- # a tényleges paramétereket.
+ """
+ A stratégia-labor mindig a közös shared engine-t használja.
+ A rules_source itt már nem külön stratégiai logikát választ,
+ csak nézeti / kiinduló forrás címke marad.
+
+ A tényleges szimulációs engine input elsődlegesen a form/UI értékekből épül fel.
+ """
+ if rules_source not in ("lab", "canonical"):
+ rules_source = "lab"
@@ -1084,84 +1081,13 @@
- threshold_mode = "bot"
- params.rules_profile_type = "canonical-effective"
- params.rules_profile_name = threshold_mode
- params.rules_source_label = "Canonical nézet / effective bot paraméterek"
+ params.rules_profile_type = "shared-engine"
+ params.rules_profile_name = "canonical-base + ui-overrides"
+ params.rules_source_label = "Közös engine / canonical alap + kézi labor paraméterek"
- threshold_mode = "lab"
- params.rules_profile_type = "lab-effective"
- params.rules_profile_name = threshold_mode
- params.rules_source_label = "LAB nézet / kézi űrlap paraméterek"
-
- # LAB módban NEM írjuk felül az űrlapból jött értékeket.
- return params
-
- try:
- threshold_rows = build_effective_threshold_rows(threshold_mode)
- except Exception:
- threshold_rows = []
-
- threshold_map = {}
- label_alias = {
- "SELL_STANDARD": "SELL_STANDARD",
Teljes diff
--- baseline
+++ current
@@ -118,16 +118,21 @@
value = max_value
return value
-
def parse_rows_limit_arg():
- raw = (request.args.get("rows_limit", "500") or "500").strip().lower()
- allowed = {"100", "250", "500", "1000", "2000", "all"}
- if raw not in allowed:
- raw = "500"
- if raw == "all":
+ raw = (request.args.get("rows_limit", "all") or "all").strip().lower()
+
+ if raw in ("", "all", "max", "full", "10d", "10days"):
return None, "all"
- return int(raw), raw
-
+
+ try:
+ value = int(raw)
+ except Exception:
+ return None, "all"
+
+ if value <= 0:
+ return None, "all"
+
+ return value, str(value)
# ------------------------------------------------
# rules source helper
@@ -197,11 +202,9 @@
rule_map = {
"BUY_STD": "BUY_STANDARD",
- "BUY_PROFITLESS": "BUY_RECOVERY",
- "BUY_PANIC": "BUY_PANIC",
+ "BUY_PANIC": "BUY_PANIC",
"SELL_STD": "SELL_STANDARD",
- "SELL_PROFITLESS": "SELL_RECOVERY",
- "SELL_PANIC": "SELL_PANIC",
+ "SELL_PANIC": "SELL_PANIC",
}
out = []
@@ -402,9 +405,7 @@
"STANDARD_SELL": "SELL_STANDARD",
"SELL_STANDARD": "SELL_STANDARD",
- "SELL_PROFITLESS": "SELL_RECOVERY",
- "PROFITLESS_SELL": "SELL_RECOVERY",
- "RECOVERY_SELL": "SELL_RECOVERY",
+ "RECOVERY_SELL": "SELL_RECOVERY",
"SELL_RECOVERY": "SELL_RECOVERY",
"SELL_PANIC": "SELL_PANIC",
@@ -414,9 +415,7 @@
"STANDARD_BUY": "BUY_STANDARD",
"BUY_STANDARD": "BUY_STANDARD",
- "BUY_PROFITLESS": "BUY_RECOVERY",
- "PROFITLESS_BUY": "BUY_RECOVERY",
- "RECOVERY_BUY": "BUY_RECOVERY",
+ "RECOVERY_BUY": "BUY_RECOVERY",
"BUY_RECOVERY": "BUY_RECOVERY",
"BUY_PANIC": "BUY_PANIC",
@@ -442,16 +441,14 @@
threshold_key_from_label = {
"STD SELL": "std_sell",
"PANIC SELL": "panic_sell",
- "PROFITLESS SELL": "recovery_sell",
- "SELL_STANDARD": "std_sell",
+ "SELL_STANDARD": "std_sell",
"SELL_PANIC": "panic_sell",
"SELL_RECOVERY": "recovery_sell",
"SELL_CATASTROPHE": "catastrophe_sell",
"STD BUY": "std_buy",
"PANIC BUY": "panic_buy",
- "PROFITLESS BUY": "recovery_buy",
- "BUY_STANDARD": "std_buy",
+ "BUY_STANDARD": "std_buy",
"BUY_PANIC": "panic_buy",
"BUY_RECOVERY": "recovery_buy",
"BUY_CATASTROPHE": "catastrophe_buy",
@@ -541,8 +538,6 @@
"A RECOVERY szabályok kontextusfüggők; kevés minta esetén az értékek csak előzetes "
"irányadónak tekintendők."
)
-
- note = str(note).replace("PROFITLESS", "RECOVERY").replace("profitless", "recovery")
if note == "Ha a RECOVERY események dominálnak, akkor a recovery szabály túl gyakran aktiválódhat. A becsült értékek kevés adat mellett még csak előzetes javaslatok.":
note = (
@@ -944,7 +939,7 @@
def parse_percent_arg(name, default_internal, min_value=None, max_value=None):
raw = request.args.get(name)
if raw is None:
- value = float(default_internal)
+ value = float(default_internal or 0.0)
else:
normalized = _normalize_localized_number(raw)
if normalized is None:
@@ -991,24 +986,26 @@
"rows_limit_label": rows_limit_label,
"std_sell_enabled": parse_bool_arg("std_sell_enabled", True),
- "std_sell_pct": parse_percent_arg("std_sell_pct", 0.03, 0.0),
+ "std_sell_pct": parse_percent_arg("std_sell_pct", None, 0.0),
"panic_sell_enabled": parse_bool_arg("panic_sell_enabled", True),
- "panic_sell_pct": parse_percent_arg("panic_sell_pct", 0.01, 0.0),
+ "panic_sell_pct": parse_percent_arg("panic_sell_pct", None, 0.0),
"catastrophe_sell_pct": parse_percent_arg("catastrophe_sell_pct", 0.10, 0.0),
"std_buy_enabled": parse_bool_arg("std_buy_enabled", True),
- "std_buy_pct": parse_percent_arg("std_buy_pct", 0.03, 0.0),
+ "std_buy_pct": parse_percent_arg("std_buy_pct", None, 0.0),
"panic_buy_enabled": parse_bool_arg("panic_buy_enabled", True),
- "panic_buy_pct": parse_percent_arg("panic_buy_pct", 0.01, 0.0),
+ "panic_buy_pct": parse_percent_arg("panic_buy_pct", None, 0.0),
"catastrophe_buy_pct": parse_percent_arg("catastrophe_buy_pct", 0.10, 0.0),
"panic_buy_confirm_ticks": int(parse_float_arg("panic_buy_confirm_ticks", 2, 1, 10)),
"recovery_buy_rebound_pct": parse_percent_arg("recovery_buy_rebound_pct", 0.003, 0.0),
"recovery_sell_retrace_pct": parse_percent_arg("recovery_sell_retrace_pct", 0.003, 0.0),
"recovery_profit_target_pct": parse_percent_arg("recovery_profit_target_pct", 0.001, 0.0),
+ "sell_reversal_min_pct": parse_percent_arg("sell_reversal_min_pct", 0.0, 0.0),
"ma_filter_enabled": parse_bool_arg("ma_filter_enabled", True),
"ma_period": int(parse_float_arg("ma_period", 20, 5, 100000)),
+ "ma_sideways_band_pct": parse_percent_arg("ma_sideways_band_pct", 0.0005, 0.0),
"rules_source": get_rules_source(),
}
@@ -1030,6 +1027,7 @@
params_ns.recovery_buy_rebound_pct_ui = format_percent_ui_from_internal(params_ns.recovery_buy_rebound_pct, 3)
params_ns.recovery_sell_retrace_pct_ui = format_percent_ui_from_internal(params_ns.recovery_sell_retrace_pct, 3)
params_ns.recovery_profit_target_pct_ui = format_percent_ui_from_internal(params_ns.recovery_profit_target_pct, 3)
+ params_ns.sell_reversal_min_pct_ui = format_percent_ui_from_internal(params_ns.sell_reversal_min_pct, 3)
params_ns.catastrophe_buy_pct_ui = format_percent_ui_from_internal(params_ns.catastrophe_buy_pct, 3)
return params_ns
@@ -1055,18 +1053,17 @@
def apply_effective_rules_to_params(params):
- if str(getattr(params, "rules_source", "lab") or "lab").strip().lower() == "lab":
- params.rules_profile_type = "lab-effective"
- params.rules_profile_name = "lab"
- params.rules_source_label = "LAB nézet / kézi űrlap paraméterek"
- return params
-
- # FONTOS:
- # LAB módban a felhasználó által beírt űrlapérték az elsődleges.
- # CANONICAL módban az effective threshold sorokból töltjük vissza
- # a tényleges paramétereket.
+ """
+ A stratégia-labor mindig a közös shared engine-t használja.
+ A rules_source itt már nem külön stratégiai logikát választ,
+ csak nézeti / kiinduló forrás címke marad.
+
+ A tényleges szimulációs engine input elsődlegesen a form/UI értékekből épül fel.
+ """
try:
rules_source = str(getattr(params, "rules_source", "lab") or "lab").strip().lower()
+ if rules_source not in ("lab", "canonical"):
+ rules_source = "lab"
params.std_sell_enabled = bool(getattr(params, "std_sell_enabled", True))
params.panic_sell_enabled = bool(getattr(params, "panic_sell_enabled", True))
@@ -1084,84 +1081,13 @@
params.recovery_profit_target_pct = max(0.0, float(getattr(params, "recovery_profit_target_pct", 0.001)))
if rules_source == "canonical":
- threshold_mode = "bot"
- params.rules_profile_type = "canonical-effective"
- params.rules_profile_name = threshold_mode
- params.rules_source_label = "Canonical nézet / effective bot paraméterek"
+ params.rules_profile_type = "shared-engine"
+ params.rules_profile_name = "canonical-base + ui-overrides"
+ params.rules_source_label = "Közös engine / canonical alap + kézi labor paraméterek"
else:
- threshold_mode = "lab"
- params.rules_profile_type = "lab-effective"
- params.rules_profile_name = threshold_mode
- params.rules_source_label = "LAB nézet / kézi űrlap paraméterek"
-
- # LAB módban NEM írjuk felül az űrlapból jött értékeket.
- return params
-
- try:
- threshold_rows = build_effective_threshold_rows(threshold_mode)
- except Exception:
- threshold_rows = []
-
- threshold_map = {}
- label_alias = {
- "SELL_STANDARD": "SELL_STANDARD",
... [DIFF LEVÁGVA] további sorok: 768