Crash detection

This commit is contained in:
Jérôme Delacotte
2025-12-13 13:53:51 +01:00
parent 94b9845619
commit c039aa29bd
17 changed files with 17174 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
import json
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
DATA_FILE = "/home/jerome/Perso/freqtradeDocker/user_data/data/binance/BTC_USDC-4h.feather"
def compute_crash_risk_index(df):
# VOLATILITÉ
df['H-L'] = df['high'] - df['low']
df['ATR'] = df['H-L'].rolling(14).mean()
df['atr_norm'] = df['ATR'] / df['close']
# DRAWDOWN (critique)
df['rolling_max'] = df['close'].rolling(48).max()
df['drawdown'] = (df['close'] - df['rolling_max']) / df['rolling_max']
df['dd_score'] = np.clip(-df['drawdown'] / 0.10, 0, 1)
# TENDANCE (slope)
df['MA7'] = df['close'].rolling(7).mean()
df['MA14'] = df['close'].rolling(14).mean()
df['slope'] = df['MA7'] - df['MA14']
df['slope_score'] = np.clip(1 - (df['slope'] / df['close']), 0, 1)
# NÉGATIVE STREAK
df['neg_streak'] = df['close'].pct_change().apply(lambda x: min(x, 0)).rolling(24).sum()
df['neg_score'] = np.clip(-df['neg_streak'] / 0.05, 0, 1)
# COMPOSANTS COURT TERME
df['pct_change_3'] = df['close'].pct_change(3)
df['pct_change_3_smooth'] = df['pct_change_3'].rolling(6).mean()
df['crash_score'] = np.clip(1 + (df['pct_change_3_smooth'] / 0.05), 0, 1)
df['speed'] = df['close'].diff().rolling(6).mean()
df['accel'] = df['speed'].diff().rolling(6).mean()
df['STD20'] = df['close'].rolling(20).std()
df['accel_score'] = np.clip(1 + (df['accel'] / (df['STD20'] + 1e-9)), 0, 1)
# INDEX FINAL
df['crash_raw'] = (
0.35 * df['dd_score'] + # le plus important pour crash lent
0.25 * df['neg_score'] +
0.20 * df['slope_score'] +
0.10 * df['crash_score'] +
0.10 * df['accel_score']
)
# LISSAGE SIMPLE
df['crash_risk_index'] = df['crash_raw'].ewm(span=24).mean()
return df
#
# def compute_crash_risk_index(df):
# # -- volatilité
# df['H-L'] = df['high'] - df['low']
# df['ATR'] = df['H-L'].rolling(14).mean()
# df['atr_norm'] = df['ATR'] / df['close']
#
# # -- variations lissées pour éviter les spikes
# df['pct_change_3'] = df['close'].pct_change(3)
# df['pct_change_3_smooth'] = df['pct_change_3'].rolling(6).mean()
#
# # -- speed/accel : on SMOOTH sinon c'est incontrôlable
# df['speed'] = df['close'].diff().rolling(6).mean()
# df['accel'] = df['speed'].diff().rolling(6).mean()
#
# # -- Bollinger
# df['MA20'] = df['close'].rolling(20).mean()
# df['STD20'] = df['close'].rolling(20).std()
# df['BB_lower'] = df['MA20'] - 2 * df['STD20']
#
# # -------- Scores normalisés & STABLES --------
# df['crash_score'] = np.clip(1 + (df['pct_change_3_smooth'] / 0.05), 0, 1)
#
# df['accel_score'] = np.clip(
# 1 + (df['accel'] / (df['STD20'] + 1e-9)),
# 0, 1
# )
#
# df['boll_score'] = np.clip(
# (df['close'] - df['BB_lower']) / (3 * df['STD20']), # + 3σ au lieu de 2σ
# 0, 1
# )
#
# df['atr_score'] = np.clip(
# 1 - (df['atr_norm'] / 0.06), # tolérance plus large
# 0, 1
# )
#
# # -------- COMBINAISON + DOUBLE SMOOTHING --------
# df['crash_raw'] = (
# 0.30 * df['crash_score'] +
# 0.25 * df['accel_score'] +
# 0.20 * df['boll_score'] +
# 0.15 * df['atr_score']
# )
#
# # Lissage ultra important pour éviter le 0.3 → 0.9
# df['crash_risk_index'] = (
# df['crash_raw'].ewm(span=48).mean() # 2 jours en 1h
# ).rolling(24).mean() # filtre final
#
# return df
# def compute_crash_risk_index(df):
# df['H-L'] = df['high'] - df['low']
# df['ATR'] = df['H-L'].rolling(14).mean()
# df['atr_norm'] = df['ATR'] / df['close']
#
# df['pct_change_3'] = df['close'].pct_change(3)
#
# df['speed'] = df['close'].diff()
# df['accel'] = df['speed'].diff()
#
# df['MA20'] = df['close'].rolling(20).mean()
# df['STD20'] = df['close'].rolling(20).std()
# df['BB_lower'] = df['MA20'] - 2 * df['STD20']
#
# df['crash_score'] = np.clip(1 + (df['pct_change_3'] / 0.05), 0, 1)
# df['accel_score'] = np.clip(1 + (df['accel'] / df['STD20']), 0, 1)
# df['boll_score'] = np.clip((df['close'] - df['BB_lower']) / (2 * df['STD20']), 0, 1)
# df['atr_score'] = np.clip(1 - (df['atr_norm'] / 0.04), 0, 1)
#
# df['crash_raw'] = (
# 0.30 * df['crash_score'] +
# 0.25 * df['accel_score'] +
# 0.20 * df['boll_score'] +
# 0.15 * df['atr_score']
# )
#
# # Lissage ultra important pour éviter le 0.3 → 0.9
# df['crash_risk_index'] = (
# df['crash_raw'].ewm(span=4).mean() # 2 jours en 1h
# ).rolling(2).mean() # filtre final
#
# return df
# Load Freqtrade OHLCV JSON
# with open(DATA_FILE, "r") as f:
# raw = json.load(f)
df = pd.read_feather(DATA_FILE)
print(df.head())
# df = pd.DataFrame(raw)
df['date'] = pd.to_datetime(df['date'])
df = df.set_index('date')
# compute risk
df = compute_crash_risk_index(df)
# Only keep Nov. 2025
df = df["2025-10-01":"2025-12-10"]
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['crash_risk_index'])
plt.title("Crash Risk Index Novembre 2025")
plt.grid(True)
plt.show()