| Overall Statistics |
|
Total Orders 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000.00 End Equity 100000 Net Profit 0% Sharpe Ratio 0 Sortino Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -1.842 Tracking Error 0.106 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset Portfolio Turnover 0% Drawdown Recovery 0 |
from AlgorithmImports import *
import numpy as np
class ZenithDataMinerV3_DualVol(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 1, 1)
self.SetEndDate(2024, 12, 1)
self.SetCash(100000)
self.symbol = self.AddCrypto("BTCUSDT", Resolution.Minute).Symbol
# 1. ATR (Benchmark Clássico)
self.atr = self.ATR(self.symbol, 14, MovingAverageType.Simple, Resolution.Hour)
# 2. Janela para Parkinson (Benchmark Cirúrgico)
self.vol_window = 240 # 4 horas
self.history_window = RollingWindow[TradeBar](self.vol_window)
# Janelas de Preço para Breakout (4h)
self.high_window = RollingWindow[float](240)
self.low_window = RollingWindow[float](240)
# Controle de High/Low do Dia Anterior (PDH/PDL)
self.daily_high = 0
self.daily_low = 0
self.pdh = 0
self.pdl = 0
self.virtual_trades = []
self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.At(0, 0), self.ResetDailyStats)
# HEADER CSV ATUALIZADO: Inclui ATR e PARKINSON
self.Log("TYPE,DATE,HOUR,ATR_PCT,PARKINSON_PCT,SETUP,DIRECTION,STOP_SIZE_PCT,RESULT_R,OUTCOME")
self.SetWarmUp(240)
def OnData(self, data):
if self.IsWarmingUp: return
if not data.Bars.ContainsKey(self.symbol): return
bar = data.Bars[self.symbol]
# Atualiza Janelas
self.history_window.Add(bar)
self.high_window.Add(bar.High)
self.low_window.Add(bar.Low)
# Atualiza PDH/PDL Tracker
if self.daily_high == 0 or bar.High > self.daily_high: self.daily_high = bar.High
if self.daily_low == 0 or bar.Low < self.daily_low: self.daily_low = bar.Low
if not self.high_window.IsReady or not self.history_window.IsReady: return
# --- CÁLCULO DE VOLATILIDADE DUPLA ---
# 1. ATR % (Normalizado pelo preço)
current_atr_pct = (self.atr.Current.Value / bar.Close) * 100
# 2. Parkinson % (Corrigido e Normalizado)
# Parkinson Volatility = sqrt( 1/(4ln2) * mean( ln(H/L)^2 ) )
# Isso dá a volatilidade do período da janela.
current_parkinson_pct = self.CalculateParkinsonVol() * 100
# --- LÓGICA DE TRADES VIRTUAIS ---
for trade in self.virtual_trades[:]:
self.CheckVirtualTrade(trade, bar)
hour = self.Time.hour
# SETUP A: SWEEP (Reversão) - Londres & NY Open
if (7 <= hour < 10) or (13 <= hour < 16):
if self.pdh > 0 and self.pdl > 0:
# Short Sweep
if bar.High > self.pdh and bar.Close < self.pdh:
sl_price = bar.High
if abs(sl_price - bar.Close)/bar.Close < 0.002: sl_price = bar.Close * 1.002
self.CreateVirtualTrade("SWEEP", "SHORT", bar.Close, sl_price, current_atr_pct, current_parkinson_pct, bar.Time)
# Long Sweep
elif bar.Low < self.pdl and bar.Close > self.pdl:
sl_price = bar.Low
if abs(bar.Close - sl_price)/bar.Close < 0.002: sl_price = bar.Close * 0.998
self.CreateVirtualTrade("SWEEP", "LONG", bar.Close, sl_price, current_atr_pct, current_parkinson_pct, bar.Time)
# SETUP B: BREAKOUT (Tendência) - Late NY
if 16 <= hour < 19:
range_high = max(list(self.high_window)[1:])
range_low = min(list(self.low_window)[1:])
# Breakout Long
if bar.Close > range_high:
sl = bar.Close * 0.994 # Stop fixo 0.6%
self.CreateVirtualTrade("BREAKOUT", "LONG", bar.Close, sl, current_atr_pct, current_parkinson_pct, bar.Time, tp_r=2.0)
# Breakout Short
elif bar.Close < range_low:
sl = bar.Close * 1.006 # Stop fixo 0.6%
self.CreateVirtualTrade("BREAKOUT", "SHORT", bar.Close, sl, current_atr_pct, current_parkinson_pct, bar.Time, tp_r=2.0)
def CalculateParkinsonVol(self):
sum_sq_log_hl = 0
count = 0
for bar in self.history_window:
if bar.Low > 0:
# Logaritmo da razão High/Low
log_hl = np.log(bar.High / bar.Low)
sum_sq_log_hl += log_hl ** 2
count += 1
if count == 0: return 0
# Variância de Parkinson
variance = (1.0 / (4.0 * np.log(2.0))) * (sum_sq_log_hl / count)
# Volatilidade (Desvio Padrão)
vol = np.sqrt(variance)
# Escalar para o período da janela (opcional, mas aqui deixamos cru para ser "volatilidade por candle médio na janela")
return vol
def CreateVirtualTrade(self, setup_type, direction, entry, sl_price, atr_pct, parkinson_pct, time, tp_r=2.0):
for t in self.virtual_trades:
if t['entry_time'] == time and t['setup'] == setup_type: return
risk_dist = abs(entry - sl_price)
if risk_dist == 0: return
stop_size_pct = (risk_dist / entry) * 100
if direction == "LONG":
tp_price = entry + (risk_dist * tp_r)
else:
tp_price = entry - (risk_dist * tp_r)
trade = {
'setup': setup_type,
'direction': direction,
'entry': entry,
'sl': sl_price,
'tp': tp_price,
'atr_pct': atr_pct,
'parkinson_pct': parkinson_pct,
'entry_time': time,
'stop_size_pct': stop_size_pct,
'active': True
}
self.virtual_trades.append(trade)
def CheckVirtualTrade(self, trade, bar):
outcome = None
pnl_r = 0
if trade['direction'] == "LONG":
if bar.Low <= trade['sl']:
outcome = "LOSS"
pnl_r = -1.0
elif bar.High >= trade['tp']:
outcome = "WIN"
pnl_r = 2.0
else:
if bar.High >= trade['sl']:
outcome = "LOSS"
pnl_r = -1.0
elif bar.Low <= trade['tp']:
outcome = "WIN"
pnl_r = 2.0
if outcome:
# Log: DATA, HORA, ATR%, PARKINSON%, SETUP, DIR, STOP%, RESULT(R), OUTCOME
self.Log(f"DATA,{trade['entry_time']},{trade['entry_time'].hour},{trade['atr_pct']:.4f},{trade['parkinson_pct']:.6f},{trade['setup']},{trade['direction']},{trade['stop_size_pct']:.4f},{pnl_r},{outcome}")
self.virtual_trades.remove(trade)
elif (bar.Time - trade['entry_time']).total_seconds() > 3600 * 8:
self.Log(f"DATA,{trade['entry_time']},{trade['entry_time'].hour},{trade['atr_pct']:.4f},{trade['parkinson_pct']:.6f},{trade['setup']},{trade['direction']},{trade['stop_size_pct']:.4f},-0.1,TIMEOUT")
self.virtual_trades.remove(trade)
def ResetDailyStats(self):
self.pdh = self.daily_high
self.pdl = self.daily_low
self.daily_high = 0
self.daily_low = 0