Initial commit: TradeMind – Krypto-Trading-Bot mit Lernmodus
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.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import numpy as np
|
||||
|
||||
from trademind.features import (
|
||||
FEATURE_NAMES,
|
||||
N_FEATURES,
|
||||
build_feature_matrix,
|
||||
compute_features,
|
||||
required_bars,
|
||||
)
|
||||
|
||||
from .conftest import make_candles
|
||||
|
||||
|
||||
def test_matrix_has_expected_shape(candles, rules):
|
||||
matrix = build_feature_matrix(candles, rules)
|
||||
assert matrix is not None
|
||||
assert matrix.values.shape == (len(candles), N_FEATURES)
|
||||
assert matrix.first_valid == required_bars(rules) - 1
|
||||
|
||||
|
||||
def test_all_feature_values_are_finite_and_bounded(candles, rules):
|
||||
matrix = build_feature_matrix(candles, rules)
|
||||
valid = matrix.values[matrix.first_valid :]
|
||||
assert np.isfinite(valid).all()
|
||||
assert np.abs(valid).max() <= 8.0
|
||||
|
||||
|
||||
def test_too_short_history_returns_none(rules):
|
||||
short = make_candles(n=50)
|
||||
assert build_feature_matrix(short, rules) is None
|
||||
assert compute_features(short, rules) is None
|
||||
|
||||
|
||||
def test_snapshot_exposes_raw_indicators(candles, rules):
|
||||
snapshot = compute_features(candles, rules)
|
||||
assert snapshot is not None
|
||||
assert snapshot.price == float(candles.close[-1])
|
||||
assert snapshot.atr > 0
|
||||
assert 0.0 <= snapshot.rsi <= 100.0
|
||||
assert len(snapshot.values) == len(FEATURE_NAMES)
|
||||
assert set(snapshot.as_dict()) == set(FEATURE_NAMES)
|
||||
|
||||
|
||||
def test_snapshot_before_warmup_is_none(candles, rules):
|
||||
matrix = build_feature_matrix(candles, rules)
|
||||
assert matrix.snapshot(matrix.first_valid - 1) is None
|
||||
assert matrix.snapshot(matrix.first_valid) is not None
|
||||
|
||||
|
||||
def test_uptrend_produces_positive_trend_distance(rules):
|
||||
up = make_candles(n=500, trend=0.001, noise=0.0005, seed=11)
|
||||
snapshot = compute_features(up, rules)
|
||||
features = snapshot.as_dict()
|
||||
assert features["trend_dist"] > 0
|
||||
assert features["ema_spread"] > 0
|
||||
|
||||
|
||||
def test_downtrend_produces_negative_trend_distance(rules):
|
||||
down = make_candles(n=500, trend=-0.001, noise=0.0005, seed=12)
|
||||
features = compute_features(down, rules).as_dict()
|
||||
assert features["trend_dist"] < 0
|
||||
assert features["ema_spread"] < 0
|
||||
|
||||
|
||||
def test_features_are_scale_invariant(rules):
|
||||
"""Ein zehnfach höherer Kurs darf die normierten Merkmale kaum verändern."""
|
||||
cheap = make_candles(n=400, start_price=100.0, seed=5)
|
||||
expensive = make_candles(n=400, start_price=1_000.0, seed=5)
|
||||
a = compute_features(cheap, rules).values
|
||||
b = compute_features(expensive, rules).values
|
||||
assert np.allclose(a, b, atol=1e-8)
|
||||
|
||||
|
||||
def test_time_features_are_on_the_unit_circle(candles, rules):
|
||||
snapshot = compute_features(candles, rules).as_dict()
|
||||
radius = snapshot["time_sin"] ** 2 + snapshot["time_cos"] ** 2
|
||||
assert radius == 1.0 or abs(radius - 1.0) < 1e-9
|
||||
|
||||
|
||||
def test_matrix_rows_match_pointwise_snapshots(candles, rules):
|
||||
matrix = build_feature_matrix(candles, rules)
|
||||
index = matrix.first_valid + 25
|
||||
assert np.allclose(matrix.snapshot(index).values, matrix.values[index])
|
||||
Reference in New Issue
Block a user