MidasBot: bot trading crypto IA + stratégies Ichimoku validées

- Infra: Freqtrade (futures dry-run) + Redis + dashboard + Docker Compose
- Couche IA: ai_analyzer (Claude via abonnement, MCP TradingView, backfill biais)
- Stratégies: SampleStrategy, AiBiasStrategy, IchimokuLS (long/short, validée
  train/test + données vierges + walk-forward), MTFIchimoku, variantes hyperopt
- Arbitrage CEX (dry-run), backtesting, walk-forward, volatility targeting
- IchimokuLS en dry-run live (config_live.json)

Claude-Session: https://claude.ai/code/session_01VHETcFacdnDhQzthLpdYFR
This commit is contained in:
jerem
2026-06-23 19:25:49 +02:00
commit 633b033f4d
59 changed files with 3868 additions and 0 deletions

111
ai_analyzer/backfill.py Normal file
View File

@@ -0,0 +1,111 @@
"""Backfill — génère un historique de biais IA pour backtester la stratégie.
Parcourt l'historique des bougies à une cadence donnée et, à chaque pas de temps,
demande à Claude un biais en n'utilisant QUE les données disponibles jusqu'à ce
moment-là (pas de fuite du futur). Écrit chaque biais dans l'historique CSV avec
l'horodatage de la bougie correspondante.
⚠️ COÛTEUX EN QUOTA D'ABONNEMENT : 1 appel Claude par pas de temps.
6 mois × cadence 24 h ≈ 180 appels. Choisis une cadence raisonnable.
Usage :
python backfill.py --start 20260101 --step-hours 24
python backfill.py --start 20260101 --end 20260301 --step-hours 12 --pairs BTC/USDT,ETH/USDT
"""
from __future__ import annotations
import argparse
import os
from datetime import datetime, timedelta, timezone
import ccxt
from claude_client import ClaudeClient
from market_data import snapshot_from_candles
from signal_store import append_history
EXCHANGE = os.environ.get("ANALYZER_EXCHANGE", "binance")
MODEL = os.environ.get("ANALYZER_MODEL", "claude-sonnet-4-6")
HISTORY_DIR = os.environ.get(
"ANALYZER_HISTORY_DIR",
os.path.join(os.path.dirname(os.path.dirname(__file__)),
"freqtrade", "user_data", "ai_bias_history"),
)
WINDOW = 100 # nb de bougies fournies à Claude à chaque pas
def _parse_day(s: str) -> datetime:
return datetime.strptime(s, "%Y%m%d").replace(tzinfo=timezone.utc)
def _fetch_full(exchange, pair: str, timeframe: str, since_ms: int) -> list:
"""Récupère tout l'OHLCV depuis `since_ms` (pagination ccxt)."""
out: list = []
cursor = since_ms
while True:
batch = exchange.fetch_ohlcv(pair, timeframe=timeframe, since=cursor, limit=1000)
if not batch:
break
out += batch
cursor = batch[-1][0] + 1
if len(batch) < 1000:
break
return out
def main() -> None:
p = argparse.ArgumentParser(description="MidasBot — backfill historique des biais IA")
p.add_argument("--pairs", default="BTC/USDT,ETH/USDT,SOL/USDT,BNB/USDT")
p.add_argument("--timeframe", default="1h")
p.add_argument("--start", required=True, help="AAAAMMJJ")
p.add_argument("--end", default=None, help="AAAAMMJJ (défaut: maintenant)")
p.add_argument("--step-hours", type=int, default=24, help="cadence d'analyse")
p.add_argument("--yes", action="store_true", help="ne pas demander confirmation")
args = p.parse_args()
pairs = [x.strip() for x in args.pairs.split(",") if x.strip()]
tf = args.timeframe
start = _parse_day(args.start)
end = _parse_day(args.end) if args.end else datetime.now(timezone.utc)
step = timedelta(hours=args.step_hours)
n_steps = int((end - start) / step)
print(f"Backfill {pairs} {tf} | {start.date()}{end.date()} | "
f"pas {args.step_hours} h | ~{n_steps} appels Claude ({MODEL})")
if not args.yes:
if input("Continuer ? Ça consomme ton quota d'abonnement [y/N] ").strip().lower() != "y":
print("Annulé.")
return
exchange = getattr(ccxt, EXCHANGE)({"enableRateLimit": True})
since_ms = int(start.timestamp() * 1000)
# Récupère tout l'historique une fois par paire (puis on tranche par pas de temps).
full = {pair: _fetch_full(exchange, pair, tf, since_ms) for pair in pairs}
client = ClaudeClient(model=MODEL)
t = start
done = 0
while t <= end:
t_ms = int(t.timestamp() * 1000)
snaps = []
for pair in pairs:
candles = [c for c in full[pair] if c[0] <= t_ms][-WINDOW:]
snap = snapshot_from_candles(pair, tf, candles)
if snap:
snaps.append(snap)
if snaps:
try:
batch = client.get_biases(snaps)
for bias in batch.biases:
append_history(bias, HISTORY_DIR, ts=t)
done += 1
print(f" {t.isoformat()}{len(batch.biases)} biais")
except Exception as exc: # noqa: BLE001
print(f" {t.isoformat()} → erreur: {exc}")
t += step
print(f"Terminé : {done} pas écrits dans {HISTORY_DIR}")
if __name__ == "__main__":
main()