65ed73977e
Per Podman deploybarer Bot, der Käufe und Verkäufe simuliert ausführt und sich aus den Ergebnissen weiter antrainiert. Aufbau - Einheitliche Bar-Verarbeitung für paper, backtest und live; ausgetauscht werden nur Datenquelle und Broker. - Börsenanbindung über ccxt: rund 100 Börsen allein über exchange.id erreichbar. Zugangsdaten kommen über ENV-Platzhalter, der Live-Modus ist doppelt abgesichert. - Paper-Broker mit Gebühren, Slippage, Börsenpräzision und Volumengrenzen. - Online trainierte logistische Regression bewertet jedes Einstiegssignal. Sie lernt aus realen Trade-Ergebnissen, aus Shadow-Labels aller Kandidaten – auch der abgelehnten – und aus Hintergrund-Stichproben; beim Kaltstart wird sie aus der Kurshistorie vorgelernt. - Risikomanagement: Positions- und Exposure-Grenzen, ATR-Stops, Cooldown sowie Tagesverlust- und Drawdown-Notbremsen. - SQLite-Persistenz, HTTP-Status mit Prometheus-Metriken und Dashboard, Webhooks. Deployment - Containerfile (zweistufig, non-root UID 10001), podman-compose, systemd-Quadlet. - Modell und Datenbank liegen im Volume /data und überleben Neustarts. 128 Tests, ruff sauber. Verifiziert gegen echte Marktdaten sowie im gebauten Container inklusive Healthcheck und Zustandswiederherstellung.
85 lines
2.4 KiB
Python
85 lines
2.4 KiB
Python
"""Gemeinsame Fixtures: synthetische Kursverläufe ohne Netzwerkzugriff."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from trademind.config import Config, PaperConfig, RiskConfig, RuleConfig
|
|
from trademind.models import Candles
|
|
|
|
BAR_MS = 300_000 # 5 Minuten
|
|
|
|
|
|
def make_candles(
|
|
symbol: str = "BTC/USDT",
|
|
n: int = 600,
|
|
start_price: float = 30_000.0,
|
|
trend: float = 0.0002,
|
|
noise: float = 0.002,
|
|
cycle: float = 0.0,
|
|
cycle_period: int = 80,
|
|
seed: int = 7,
|
|
timeframe: str = "5m",
|
|
start_ts: int = 1_700_000_000_000,
|
|
) -> Candles:
|
|
"""Erzeugt eine plausible OHLCV-Serie (geometrischer Random Walk plus optionale Welle)."""
|
|
rng = np.random.default_rng(seed)
|
|
steps = rng.normal(loc=trend, scale=noise, size=n)
|
|
if cycle:
|
|
steps += cycle * np.sin(2 * np.pi * np.arange(n) / cycle_period)
|
|
close = start_price * np.exp(np.cumsum(steps))
|
|
|
|
open_ = np.concatenate([[start_price], close[:-1]])
|
|
spread = np.abs(rng.normal(0.0, noise * 0.8, size=n)) * close
|
|
high = np.maximum(open_, close) + spread
|
|
low = np.minimum(open_, close) - spread
|
|
low = np.maximum(low, close * 0.5)
|
|
volume = rng.lognormal(mean=3.0, sigma=0.4, size=n)
|
|
timestamp = start_ts + np.arange(n, dtype=np.int64) * BAR_MS
|
|
|
|
return Candles(
|
|
symbol=symbol,
|
|
timeframe=timeframe,
|
|
timestamp=timestamp,
|
|
open=open_,
|
|
high=high,
|
|
low=low,
|
|
close=close,
|
|
volume=volume,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def candles() -> Candles:
|
|
return make_candles()
|
|
|
|
|
|
@pytest.fixture
|
|
def rules() -> RuleConfig:
|
|
return RuleConfig()
|
|
|
|
|
|
@pytest.fixture
|
|
def paper_config() -> PaperConfig:
|
|
return PaperConfig(starting_balance=10_000.0, fee_rate=0.001, slippage_bps=5.0)
|
|
|
|
|
|
@pytest.fixture
|
|
def risk_config() -> RiskConfig:
|
|
return RiskConfig(max_open_positions=2, max_position_pct=0.25, cooldown_bars_after_exit=0)
|
|
|
|
|
|
@pytest.fixture
|
|
def base_config(tmp_path) -> Config:
|
|
return Config.model_validate(
|
|
{
|
|
"mode": "backtest",
|
|
"market": {"symbols": ["BTC/USDT"], "timeframe": "5m", "history_bars": 300},
|
|
"paper": {"starting_balance": 10_000.0},
|
|
"storage": {"database_path": str(tmp_path / "test.sqlite3")},
|
|
"server": {"enabled": False},
|
|
"strategy": {"learner": {"model_path": str(tmp_path / "model.npz"), "warmup_samples": 20}},
|
|
}
|
|
)
|