Fájl részletek

Ezen az oldalon egy konkrét fájl aktuális állapotát tudod megnézni.

Vissza a fájltérképhez Csak változott Stratégia-labor Monitor főoldal Következő módosult →
Fájl útvonala
/opt/bots/saturnus/app/app.py
Létezik most?
IGEN
Aktuális státusz
MODIFIED
Méret
49431
Módosítás ideje
1779876687.1075823
Korábbi baseline időpont
1775632282.5379288
SHA256 rövid

Előnézet (első 120 sor)

import json
import os
import freqtrade_ui
import tempfile
import hashlib
import subprocess
import csv
from datetime import datetime, timezone

from flask import Flask, jsonify, render_template, Response, redirect, request

from rules_loader import (
    load_rules_doc,
    save_rules_doc,
    load_lab_override_doc,
    save_lab_override_doc,
    list_lab_profiles,
)
from rules_merge import build_effective_rules


APP_DIR = os.path.dirname(os.path.abspath(__file__))
ROOT_DIR = os.path.abspath(os.path.join(APP_DIR, ".."))

STATE_PATH = os.path.join(ROOT_DIR, "state.json")
FREQTRADE_CONFIG_PATH = os.path.join(ROOT_DIR, "freqtrade", "user_data", "config.json")
SETTINGS_ENV_FILE = "/etc/systemd/system/saturnus-runner.service.d/30-canonical-env.conf"

POLL_INTERVAL_MS = 15000

MONITOR_DATA_DIR = "/opt/bots/saturnus_monitor/data"
MONITOR_PRICES_CSV = os.path.join(MONITOR_DATA_DIR, "prices.csv")


def _parse_iso_ts(value: str):
    if not value:
        return None
    raw = str(value).strip()
    try:
        if raw.endswith("Z"):
            return datetime.fromisoformat(raw.replace("Z", "+00:00"))
        return datetime.fromisoformat(raw)
    except Exception:
        return None


def load_chart_history_from_prices_csv(days: int = 10, short_days: int = 1, long_days: int = 10):
    rows = []
    try:
        with open(MONITOR_PRICES_CSV, "r", encoding="utf-8") as f:
            reader = csv.DictReader(f)
            for row in reader:
                ts = _parse_iso_ts(row.get("timestamp_utc"))
                if ts is None:
                    continue
                try:
                    last = float(row.get("last"))
                except Exception:
                    continue
                rows.append((ts, last))
    except Exception as e:
        return {
            "ok": False,
            "error": "prices_csv_unavailable",
            "detail": str(e),
            "labels": [],
            "price": [],
            "ma_short": [],
            "ma_long": [],
        }

    if not rows:
        return {
            "ok": False,
            "error": "prices_csv_empty",
            "labels": [],
            "price": [],
            "ma_short": [],
            "ma_long": [],
        }

    rows.sort(key=lambda x: x[0])

    # perces gyertyásítás: adott perc utolsó ára maradjon meg
    minute_map = {}
    minute_order = []
    for ts, last in rows:
        minute_ts = ts.replace(second=0, microsecond=0)
        key = minute_ts.isoformat()
        if key not in minute_map:
            minute_order.append(key)
        minute_map[key] = (minute_ts, last)

    minute_rows = [minute_map[k] for k in minute_order]
    if not minute_rows:
        return {
            "ok": False,
            "error": "minute_rows_empty",
            "labels": [],
            "price": [],
            "ma_short": [],
            "ma_long": [],
        }

    max_ts = minute_rows[-1][0]
    cutoff = max_ts.timestamp() - (int(days) * 86400)

    filtered = [(ts, last) for ts, last in minute_rows if ts.timestamp() >= cutoff]
    if not filtered:
        filtered = minute_rows[-min(len(minute_rows), int(days) * 1440):]

    short_window = max(1, int(short_days) * 1440)
    long_window = max(1, int(long_days) * 1440)

    labels = []
    price = []
    ma_short = []
    ma_long = []

    short_buf = []

Csak változott diff sorok

--- baseline +++ current @@ -4,6 +4,7 @@ +import csv @@ -27,6 +28,142 @@ +MONITOR_DATA_DIR = "/opt/bots/saturnus_monitor/data" +MONITOR_PRICES_CSV = os.path.join(MONITOR_DATA_DIR, "prices.csv") + + +def _parse_iso_ts(value: str): + if not value: + return None + raw = str(value).strip() + try: + if raw.endswith("Z"): + return datetime.fromisoformat(raw.replace("Z", "+00:00")) + return datetime.fromisoformat(raw) + except Exception: + return None + + +def load_chart_history_from_prices_csv(days: int = 10, short_days: int = 1, long_days: int = 10): + rows = [] + try: + with open(MONITOR_PRICES_CSV, "r", encoding="utf-8") as f: + reader = csv.DictReader(f) + for row in reader: + ts = _parse_iso_ts(row.get("timestamp_utc")) + if ts is None: + continue + try: + last = float(row.get("last")) + except Exception: + continue + rows.append((ts, last)) + except Exception as e: + return { + "ok": False, + "error": "prices_csv_unavailable", + "detail": str(e), + "labels": [], + "price": [], + "ma_short": [], + "ma_long": [], + } + + if not rows: + return { + "ok": False, + "error": "prices_csv_empty", + "labels": [], + "price": [], + "ma_short": [], + "ma_long": [], + } + + rows.sort(key=lambda x: x[0]) + + # perces gyertyásítás: adott perc utolsó ára maradjon meg + minute_map = {} + minute_order = [] + for ts, last in rows: + minute_ts = ts.replace(second=0, microsecond=0) + key = minute_ts.isoformat() + if key not in minute_map: + minute_order.append(key) + minute_map[key] = (minute_ts, last) + + minute_rows = [minute_map[k] for k in minute_order] + if not minute_rows: + return { + "ok": False, + "error": "minute_rows_empty", + "labels": [], + "price": [], + "ma_short": [], + "ma_long": [], + } + + max_ts = minute_rows[-1][0] + cutoff = max_ts.timestamp() - (int(days) * 86400) + + filtered = [(ts, last) for ts, last in minute_rows if ts.timestamp() >= cutoff] + if not filtered: + filtered = minute_rows[-min(len(minute_rows), int(days) * 1440):] + + short_window = max(1, int(short_days) * 1440) + long_window = max(1, int(long_days) * 1440) + + labels = [] + price = [] + ma_short = [] + ma_long = [] + + short_buf = [] + long_buf = [] + + for ts, last in filtered: + labels.append(ts.isoformat().replace("+00:00", "Z")) + price.append(last) + + short_buf.append(last) + if len(short_buf) > short_window: + short_buf.pop(0) + + long_buf.append(last) + if len(long_buf) > long_window: + long_buf.pop(0) + + ma_short.append(sum(short_buf) / len(short_buf) if short_buf else None) + ma_long.append(sum(long_buf) / len(long_buf) if long_buf else None) + + return { + "ok": True, + "source": MONITOR_PRICES_CSV, + "days": int(days), + "short_days": int(short_days), + "long_days": int(long_days), + "points": len(labels), + "labels": labels, + "price": price, + "ma_short": ma_short, + "ma_long": ma_long, + } + +import os + +def get_env(name, default=None): + return os.environ.get(name, default) + +def env_float(name, default=0.0): + try: + return float(get_env(name, default)) + except: + return float(default) + +def env_int(name, default=0): + try: + return int(float(get_env(name, default))) + except: + return int(default) @@ -762,59 +899,124 @@ + + "std_sell_enabled": True, + "recovery_sell_retrace_pct": 0.3, + "recovery_profit_target_pct": 0.1, + "panic_sell_enabled": True, + "sell_reversal_min_pct": 0.0, + + "std_buy_enabled": True, + "recovery_buy_rebound_pct": 0.3, + "panic_buy_enabled": True, + "panic_buy_confirm_ticks": 2, - "recovery_buy_rebound_pct": 0.3, - "recovery_sell_retrace_pct": 0.3, + + "ma_filter_enabled": True, + "ma_period": 20, + "ma_sideways_band_pct": 0.05, + def apply_env(k, v): + try: + if k == "TICK_SECONDS": + cfg["tick_seconds"] = float(v) + elif k == "PAIR": + cfg["pair"] = v + elif k == "TIMEFRAME": + cfg["timeframe"] = v + elif k == "LIMIT": + cfg["limit"] = int(float(v)) + elif k == "EXECUTION_ENABLED": + cfg["execution_enabled"] = env_to_bool(v) + elif k == "EXECUTION_LOG_ONLY": + cfg["execution_log_only"] = env_to_bool(v) + elif k == "BUY_LOCK_TTL_SEC": + cfg["buy_lock_ttl_sec"] = int(float(v)) + elif k == "STD_SELL_ENABLED": + cfg["std_sell_enabled"] = env_to_bool(v)

Teljes diff

--- baseline +++ current @@ -4,6 +4,7 @@ import tempfile import hashlib import subprocess +import csv from datetime import datetime, timezone from flask import Flask, jsonify, render_template, Response, redirect, request @@ -27,6 +28,142 @@ POLL_INTERVAL_MS = 15000 +MONITOR_DATA_DIR = "/opt/bots/saturnus_monitor/data" +MONITOR_PRICES_CSV = os.path.join(MONITOR_DATA_DIR, "prices.csv") + + +def _parse_iso_ts(value: str): + if not value: + return None + raw = str(value).strip() + try: + if raw.endswith("Z"): + return datetime.fromisoformat(raw.replace("Z", "+00:00")) + return datetime.fromisoformat(raw) + except Exception: + return None + + +def load_chart_history_from_prices_csv(days: int = 10, short_days: int = 1, long_days: int = 10): + rows = [] + try: + with open(MONITOR_PRICES_CSV, "r", encoding="utf-8") as f: + reader = csv.DictReader(f) + for row in reader: + ts = _parse_iso_ts(row.get("timestamp_utc")) + if ts is None: + continue + try: + last = float(row.get("last")) + except Exception: + continue + rows.append((ts, last)) + except Exception as e: + return { + "ok": False, + "error": "prices_csv_unavailable", + "detail": str(e), + "labels": [], + "price": [], + "ma_short": [], + "ma_long": [], + } + + if not rows: + return { + "ok": False, + "error": "prices_csv_empty", + "labels": [], + "price": [], + "ma_short": [], + "ma_long": [], + } + + rows.sort(key=lambda x: x[0]) + + # perces gyertyásítás: adott perc utolsó ára maradjon meg + minute_map = {} + minute_order = [] + for ts, last in rows: + minute_ts = ts.replace(second=0, microsecond=0) + key = minute_ts.isoformat() + if key not in minute_map: + minute_order.append(key) + minute_map[key] = (minute_ts, last) + + minute_rows = [minute_map[k] for k in minute_order] + if not minute_rows: + return { + "ok": False, + "error": "minute_rows_empty", + "labels": [], + "price": [], + "ma_short": [], + "ma_long": [], + } + + max_ts = minute_rows[-1][0] + cutoff = max_ts.timestamp() - (int(days) * 86400) + + filtered = [(ts, last) for ts, last in minute_rows if ts.timestamp() >= cutoff] + if not filtered: + filtered = minute_rows[-min(len(minute_rows), int(days) * 1440):] + + short_window = max(1, int(short_days) * 1440) + long_window = max(1, int(long_days) * 1440) + + labels = [] + price = [] + ma_short = [] + ma_long = [] + + short_buf = [] + long_buf = [] + + for ts, last in filtered: + labels.append(ts.isoformat().replace("+00:00", "Z")) + price.append(last) + + short_buf.append(last) + if len(short_buf) > short_window: + short_buf.pop(0) + + long_buf.append(last) + if len(long_buf) > long_window: + long_buf.pop(0) + + ma_short.append(sum(short_buf) / len(short_buf) if short_buf else None) + ma_long.append(sum(long_buf) / len(long_buf) if long_buf else None) + + return { + "ok": True, + "source": MONITOR_PRICES_CSV, + "days": int(days), + "short_days": int(short_days), + "long_days": int(long_days), + "points": len(labels), + "labels": labels, + "price": price, + "ma_short": ma_short, + "ma_long": ma_long, + } + +import os + +def get_env(name, default=None): + return os.environ.get(name, default) + +def env_float(name, default=0.0): + try: + return float(get_env(name, default)) + except: + return float(default) + +def env_int(name, default=0): + try: + return int(float(get_env(name, default))) + except: + return int(default) app = Flask(__name__, template_folder=os.path.join(APP_DIR, "templates")) @@ -762,59 +899,124 @@ "max_trades_per_day": 20, "daily_loss_cap_pct": 5.0, "ft_url": "http://127.0.0.1:8089", + + "std_sell_enabled": True, "std_sell_pct": 1.0, + "recovery_sell_retrace_pct": 0.3, + "recovery_profit_target_pct": 0.1, + "panic_sell_enabled": True, "panic_sell_pct": 1.0, + "sell_reversal_min_pct": 0.0, "catastrophe_sell_pct": 10.0, + + "std_buy_enabled": True, "std_buy_pct": 1.0, + "recovery_buy_rebound_pct": 0.3, + "panic_buy_enabled": True, "panic_buy_pct": 1.0, + "panic_buy_confirm_ticks": 2, "catastrophe_buy_pct": 10.0, - "recovery_buy_rebound_pct": 0.3, - "recovery_sell_retrace_pct": 0.3, + + "ma_filter_enabled": True, + "ma_period": 20, + "ma_sideways_band_pct": 0.05, } + def apply_env(k, v): + try: + if k == "TICK_SECONDS": + cfg["tick_seconds"] = float(v) + elif k == "PAIR": + cfg["pair"] = v + elif k == "TIMEFRAME": + cfg["timeframe"] = v + elif k == "LIMIT": + cfg["limit"] = int(float(v)) + elif k == "EXECUTION_ENABLED": + cfg["execution_enabled"] = env_to_bool(v) + elif k == "EXECUTION_LOG_ONLY": + cfg["execution_log_only"] = env_to_bool(v) + elif k == "BUY_LOCK_TTL_SEC": + cfg["buy_lock_ttl_sec"] = int(float(v)) + elif k == "STD_SELL_ENABLED": + cfg["std_sell_enabled"] = env_to_bool(v) ... [DIFF LEVÁGVA] további sorok: 287