diff --git a/assets/detection.gif b/assets/detection.gif new file mode 100644 index 0000000..e032ac5 --- /dev/null +++ b/assets/detection.gif @@ -0,0 +1,75 @@ +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.animation import FuncAnimation, PillowWriter + +# --- 1. SETUP & SIMULATION DATA --- +np.random.seed(42) +n_frames = 150 +x = np.arange(n_frames) + +# Regime Transitions: 0-60 (Stable), 60-100 (Latent), 100-150 (Stress) +regime_bg = np.zeros(n_frames) +regime_bg[60:100] = 1 # Latent Build-up +regime_bg[100:] = 2 # Stress + +# Simulated Signals +volatility = np.random.normal(0.5, 0.1, n_frames) +volatility[100:] += np.cumsum(np.random.normal(0.2, 0.05, 50)) # Reactive Spike + +entropy = np.random.normal(0.2, 0.05, n_frames) +entropy[65:] += np.linspace(0, 0.6, 85) # Early build-up in latent phase + +trigger_signal = (entropy > 0.45).astype(float) # The "Early" Detection + +# --- 2. PLOTTING ENGINE --- +fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True) +plt.subplots_adjust(hspace=0.3) + +# Style Customization +for ax in [ax1, ax2]: + ax.set_facecolor('#0f0f0f') + ax.grid(color='white', alpha=0.1) + ax.tick_params(colors='white') + +fig.patch.set_facecolor('#0f0f0f') + +# Lines +line_vol, = ax1.plot([], [], color='#00ffcc', lw=2, label='Market Volatility (Reactive)') +line_ent, = ax2.plot([], [], color='#ff00ff', lw=2, label='HMM Entropy (Latent Signal)') +line_trig, = ax2.plot([], [], color='#ffcc00', lw=3, label='EARLY TRIGGER') + +ax1.set_ylim(0, 3) +ax2.set_ylim(0, 1.2) +ax1.legend(loc='upper left') +ax2.legend(loc='upper left') + +# --- 3. ANIMATION LOGIC --- +def init(): + line_vol.set_data([], []) + line_ent.set_data([], []) + line_trig.set_data([], []) + return line_vol, line_ent, line_trig + +def update(frame): + # Update line data + line_vol.set_data(x[:frame], volatility[:frame]) + line_ent.set_data(x[:frame], entropy[:frame]) + + if frame > 75: # Trigger activation point + line_trig.set_data(x[75:frame], trigger_signal[75:frame]) + + # Dynamic Shading for Regimes + if frame == 60: + ax1.axvspan(60, 100, color='yellow', alpha=0.1, label='Latent Phase') + if frame == 100: + ax1.axvspan(100, 150, color='red', alpha=0.2, label='Stress Phase') + + return line_vol, line_ent, line_trig + +# --- 4. EXPORT --- +ani = FuncAnimation(fig, update, frames=n_frames, init_func=init, blit=True) + +print("Encoding GIF... this may take a moment.") +writer = PillowWriter(fps=20) +ani.save("assets/detection.gif", writer=writer) +print("GIF saved to assets/detection.gif")