| Overall Statistics |
|
Total Trades 630 Average Win 1.10% Average Loss -1.00% Compounding Annual Return 56.958% Drawdown 21.600% Expectancy 0.154 Net Profit 56.958% Sharpe Ratio 1.63 Probabilistic Sharpe Ratio 64.138% Loss Rate 45% Win Rate 55% Profit-Loss Ratio 1.10 Alpha 0.514 Beta 0.011 Annual Standard Deviation 0.318 Annual Variance 0.101 Information Ratio 0.562 Tracking Error 0.345 Treynor Ratio 48.708 Total Fees $5850.82 Estimated Strategy Capacity $780000000.00 Lowest Capacity Asset SPCE X91R7VLCNM91 |
from AlgorithmImports import *
from QuantConnect.DataSource import *
import numpy as np
class BrainMLRankingDataAlgorithm(QCAlgorithm):
percentile = 2.5
symbol_data_by_symbol = {}
def Initialize(self):
self.SetStartDate(2020, 7, 1)
self.SetEndDate(2021, 6, 30)
self.SetCash(1000000)
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.Universe.DollarVolume.Top(5))
self.knownSymbols = set()
def OnData(self, data):
for symbol, symbol_data in self.symbol_data_by_symbol.items():
if data.ContainsKey(symbol_data.ranking_symbol) and data[symbol_data.ranking_symbol] is not None:
rank = data[symbol_data.ranking_symbol].Rank
symbol_data.update(rank)
ranks = [symbol_data.brain_rank for _, symbol_data in self.symbol_data_by_symbol.items() if symbol_data.IsReady]
if len(ranks) == 0:
return
short_threshold = np.percentile(ranks, self.percentile)
long_threshold = np.percentile(ranks, 100 - self.percentile)
weight = self.percentile / len(self.symbol_data_by_symbol)
for symbol, symbol_data in self.symbol_data_by_symbol.items():
# Ensure we have data to place orders
if not (data.ContainsKey(symbol) and data[symbol] is not None):
continue
if not symbol_data.IsReady:
continue
if symbol_data.brain_rank <= short_threshold:
self.SetHoldings(symbol, -weight)
elif symbol_data.brain_rank >= long_threshold:
self.SetHoldings(symbol, weight)
else:
self.Liquidate(symbol)
def OnSecuritiesChanged(self, changes):
for security in changes.AddedSecurities:
symbol = security.Symbol
self.symbol_data_by_symbol[symbol] = SymbolData(self, symbol)
self.knownSymbols.add(symbol)
for security in changes.RemovedSecurities:
symbol = security.Symbol
self.Liquidate(symbol)
symbol_data = self.symbol_data_by_symbol.pop(symbol, None)
if symbol_data:
symbol_data.dispose()
def OnEndOfAlgorithm(self):
self.Debug(f"Seen {len(self.knownSymbols)} symbols")
class SymbolData:
brain_rank = None
def __init__(self, algorithm, symbol):
self.algorithm = algorithm
self.ranking_symbol = algorithm.AddData(BrainStockRanking2Day, symbol).Symbol
# Warm up Brain rank
history = algorithm.History(self.ranking_symbol, 3, Resolution.Daily)
if history.empty or 'rank' not in history.columns:
return
for time, row in history.loc[self.ranking_symbol].iterrows():
self.brain_rank = row['rank']
def update(self, rank):
self.brain_rank = rank
@property
def IsReady(self):
return self.brain_rank is not None
def dispose(self):
self.algorithm.RemoveSecurity(self.ranking_symbol)