
Transclude of Minervini_Toolkit.py
Descrizione e Logica
Questa strategia non esegue operazioni di compravendita automatica, ma agisce come una Dashboard Visiva Avanzata sovrapposta al grafico dei prezzi. È progettata per permettere al trader di valutare a colpo d’occhio la “salute” tecnica di un titolo secondo i rigidi criteri dei Campioni di Borsa (SEPA method).
Il toolkit combina tre strumenti analitici distinti in un unico overlay:
- Trend Template (Il Semaforo Verde): Verifica se il titolo si trova in una “Fase 2” (Trend Primario Rialzista). Le condizioni verificate includono l’allineamento delle medie mobili (Prezzo > 50 > 150 > 200), la pendenza della media a 200 giorni, la posizione rispetto ai massimi/minimi a 52 settimane e la forza relativa (proxy RSI).
- Extended Detector (Il Semaforo Rosso): Identifica quando il prezzo si è allontanato troppo velocemente dalla sua media mobile a breve termine (EMA 10), una condizione statistica che spesso precede un ritracciamento o una pausa (“Mean Reversion”).
- Pivot Points (Struttura del Prezzo): Identifica e scrive sul grafico i prezzi esatti dei massimi e minimi di swing rilevanti, aiutando a tracciare basi, resistenze e supporti.
Come si Utilizza
Questo strumento è fondamentale nella fase di Selezione e Screening (Watchlist Review) o nell’Analisi Discrezionale (Arena/Backtest Lab).
- Analisi del Trend: Se lo sfondo del grafico non è colorato di Verde, il titolo non soddisfa i requisiti minimi per un investimento trend-following di qualità istituzionale.
- Timing di Ingresso: Evitare di acquistare quando lo sfondo è Rosso (Extended). Anche se il trend è forte, il rischio di acquistare un massimo locale è elevato. Attendere che il prezzo rientri verso la EMA 10 o la SMA 50.
- Analisi Strutturale: Utilizzare i numeri dei Pivot Points per identificare pattern grafici come Cup with Handle, VCP o doppi minimi.
Parametri di Input
Configurazione Medie Mobili
- Show Moving Averages: Attiva/Disattiva le linee delle medie.
- SMA 50 / 150 / 200 Length: Periodi per le medie a medio e lungo termine.
Trend Template (Sfondo Verde)
- Show Trend Template: Attiva la colorazione verde dello sfondo se le condizioni sono soddisfatte.
- Min RSI (RS Proxy): Valore minimo di RSI (default 70) usato come proxy per la Forza Relativa (RS Ranking) per confermare la leadership del titolo.
Extended Detector (Sfondo Rosso)
- Show Extended: Attiva la colorazione rossa dello sfondo per eccessi di prezzo.
- Extended ATR Mult: La distanza dalla EMA 10, misurata in multipli dell’ATR, necessaria per considerare il prezzo “esteso” (default: 2.1).
Pivot Points
- Show Pivot Points: Attiva la visualizzazione numerica dei massimi/minimi.
- Pivot Lookback: Numero di barre a sinistra e destra per confermare un pivot (default: 9).
Interpretazione Grafica (Overlay)
Il grafico si arricchisce di diversi layer informativi:
-
Colorazione Sfondo (Zone):
- 🟩 Sfondo Verde Trasparente: Il titolo è in Stage 2 confermato. È un candidato all’acquisto (“Green Light”).
- 🟥 Sfondo Rosso Trasparente: Il titolo è Esteso (Overextended). Pericolo di pullback (“Red Light”).
- ⬜ Sfondo Neutro: Il titolo è in fase di consolidamento, accumulazione o trend ribassista (Stage 1, 3 o 4).
-
Linee Indicatori:
- 🟠 Linea Arancione: EMA 10 (Trend brevissimo termine / Power Trend).
- 🔵 Linea Blu: SMA 50 (Supporto Istituzionale).
- 🟢 Linea Lime: SMA 150.
- 🔴 Linea Rossa: SMA 200 (Trend Primario).
-
Etichette Testuali:
- Numeri bianchi sopra/sotto le candele indicano i prezzi esatti dei Pivot Highs e Pivot Lows confermati.
# VERSION: v1.0 - fire/strategies/standard_library/Minervini_Toolkit.py
# RESP: Implementazione Visuale del "Mark Minervini Toolkit" (Trend Template, Pivots, MAs).
# LOGICA: Identifica Stage 2 (Trend) e Punti di Svolta (Pivots).
# DEPS: pandas, numpy, fire.strategies.base_strategy
# ARCH_ROLE: feature (strategy/indicator)
# LAYER: fire-app
import pandas as pd
import numpy as np
from fire.strategies.base_strategy import BaseStrategy
class Minervini_Toolkit(BaseStrategy):
name = "Toolkit: Mark Minervini (Visual)"
description = (
"Suite completa di analisi visiva basata su Mark Minervini. "
"Include: Medie Mobili Chiave, Trend Template (Sfondo Verde), "
"Extended Detector (Sfondo Rosso) e Pivot Points (High/Low)."
)
def __init__(self):
super().__init__()
# --- INPUT MEDIE MOBILI ---
self.add_parameter("show_ma", True, bool, label="Show Moving Averages")
self.add_parameter("ma50_len", 50, int, label="SMA 50 Length")
self.add_parameter("ma150_len", 150, int, label="SMA 150 Length")
self.add_parameter("ma200_len", 200, int, label="SMA 200 Length")
# --- INPUT TREND TEMPLATE ---
self.add_parameter("show_template", True, bool, label="Show Trend Template (Bg)")
self.add_parameter("rs_proxy_rsi", 70, int, label="Min RSI (RS Proxy)")
# --- INPUT EXTENDED DETECTOR ---
self.add_parameter("show_extended", True, bool, label="Show Extended (Bg)")
self.add_parameter("ext_threshold", 2.1, float, label="Extended ATR Mult")
# --- INPUT PIVOTS ---
self.add_parameter("show_pivots", True, bool, label="Show Pivot Points")
self.add_parameter("pivot_len", 9, int, label="Pivot Lookback")
def init(self):
"""Calcolo Vettoriale Completo."""
close = self.data['Close']
high = self.data['High']
low = self.data['Low']
# 1. Medie Mobili
self.ema10 = close.ewm(span=10, adjust=False).mean()
self.sma50 = close.rolling(window=self.params['ma50_len']).mean()
self.sma150 = close.rolling(window=self.params['ma150_len']).mean()
self.sma200 = close.rolling(window=self.params['ma200_len']).mean()
# 2. Plotting Medie
if self.params['show_ma']:
self.plot(self.ema10, "EMA 10", color="orange", width=1)
self.plot(self.sma50, "SMA 50", color="#2C8AD8", width=2) # Blue
self.plot(self.sma150, "SMA 150", color="#00E676", width=2) # Lime
self.plot(self.sma200, "SMA 200", color="#FF5252", width=2) # Red
# 3. Calcolo Trend Template (Minervini Criteria)
# Condition 1: Price > 150 > 200
c1 = (close > self.sma150) & (self.sma150 > self.sma200)
# Condition 2: 200 SMA Rising (almeno da 20 barre)
sma200_prev = self.sma200.shift(20)
c2 = self.sma200 > sma200_prev
# Condition 3: Price > 50 SMA
c3 = close > self.sma50
# Condition 4: 52-Week High/Low
high_52 = high.rolling(window=250).max()
low_52 = low.rolling(window=250).min()
# Price > 30% above 52w low
c4 = close >= (low_52 * 1.30)
# Price within 25% of 52w high (Price > 75% of High)
c5 = close >= (high_52 * 0.75)
# RS Proxy (RSI)
delta = close.diff()
gain = (delta.where(delta > 0, 0)).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
c6 = rsi >= self.params['rs_proxy_rsi']
self.trend_template_active = c1 & c2 & c3 & c4 & c5 & c6
# 4. Calcolo Extended Detector
# Distanza High da EMA10 > N * ATR
prev_c = close.shift(1)
tr = pd.concat([high - low, (high - prev_c).abs(), (low - prev_c).abs()], axis=1).max(axis=1)
atr = tr.rolling(14).mean()
dist = high - self.ema10
self.is_extended = dist > (atr * self.params['ext_threshold'])
# 5. Calcolo Pivot Points (Swing Highs / Lows)
# Un massimo è un pivot se è il più alto di N barre prima e N barre dopo.
# In backtest (vettoriale) possiamo guardare "avanti" usando shift negativo.
p_len = self.params['pivot_len']
# Rotoliamo su una finestra di (p_len * 2 + 1) centrata
# Nota: rolling(center=True) guarda avanti.
window = p_len * 2 + 1
local_max = high.rolling(window=window, center=True).max()
local_min = low.rolling(window=window, center=True).min()
# Identifichiamo dove High == LocalMax (Pivot High)
self.pivot_highs = (high == local_max) & (high > 0)
# Identifichiamo dove Low == LocalMin (Pivot Low)
self.pivot_lows = (low == local_min) & (low > 0)
def next(self):
# 1. Gestione Background (Trend Template & Extended)
if self.params['show_extended'] and self.is_extended.iloc[self.i]:
# Rosso Trasparente (Extended - Pericolo)
self.bgcolor("rgba(255, 82, 82, 0.2)")
elif self.params['show_template'] and self.trend_template_active.iloc[self.i]:
# Verde Trasparente (Trend Template - Safe Zone)
self.bgcolor("rgba(0, 230, 118, 0.15)")
# 2. Gestione Pivot Points (Visualizzazione Ritardata)
# Poiché un pivot è confermato solo dopo N barre, dobbiamo "disegnare nel passato"?
# In LWC non possiamo aggiungere shape nel passato facilmente barra per barra in real-time mode,
# ma in Backtest Mode (dove abbiamo tutto il dataframe) FIRE gestisce i plot_shape.
# Tuttavia, per simulare il comportamento "live", disegniamo il pivot
# quando viene "confermato" (cioè N barre dopo il picco).
if self.params['show_pivots']:
p_len = self.params['pivot_len']
# Guardiamo indietro di p_len barre per vedere se LÌ c'era un pivot
# Se siamo alla barra 100, e p_len è 9, controlliamo la barra 91.
check_idx = self.i - p_len
if check_idx >= 0:
# Controlliamo se nel dataframe, all'indice check_idx, c'è un pivot
if self.pivot_highs.iloc[check_idx]:
price = self.data['High'].iloc[check_idx]
# Disegniamo sulla barra corrente (o proviamo a posizionarlo nel passato se il motore lo supporta?)
# BaseStrategy.plot_shape usa self.i (corrente).
# Trick: Inseriamo il testo con il prezzo.
self.plot_shape(
style="none", # Solo testo, niente forma geometrica (o arrow_down piccola)
color="white", # O textcolor specifico
location="above",
text=f"{price:.2f}",
size="small"
# Nota: Questo apparirà alla barra corrente (N giorni dopo il pivot).
# È il comportamento corretto per un indicatore non-repainting in real-time.
# Se volessimo l'effetto "repainting" (disegno sul picco esatto),
# servirebbe un supporto engine avanzato.
)
if self.pivot_lows.iloc[check_idx]:
price = self.data['Low'].iloc[check_idx]
self.plot_shape(
style="none",
color="white",
location="below",
text=f"{price:.2f}",
size="small"
)