| Overall Statistics |
|
Total Trades 1531 Average Win 0.34% Average Loss -0.37% Compounding Annual Return 13.685% Drawdown 27.200% Expectancy 0.183 Net Profit 90.031% Sharpe Ratio 0.707 Probabilistic Sharpe Ratio 20.784% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 0.90 Alpha 0.157 Beta -0.173 Annual Standard Deviation 0.185 Annual Variance 0.034 Information Ratio -0.079 Tracking Error 0.27 Treynor Ratio -0.755 Total Fees $1574.58 Estimated Strategy Capacity $490000.00 |
from QuantConnect.Data.UniverseSelection import *
import operator
from math import ceil,floor
import pandas as pd
class CoarseFineFundamentalComboAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2016, 3, 1) #Set Start Date
self.SetEndDate(2021, 3, 1) #Set End Date
self.SetCash(100000) #Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
self.__numberOfSymbols = 100
self.__numberOfSymbolsFine = 50
self.num_portfolios = 5
self.symbols = []
self.month = 0
def CoarseSelectionFunction(self, coarse):
if self.Time.month == self.month:
return Universe.Unchanged
self.month = self.Time.month
sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData], key=lambda x: x.DollarVolume, reverse=True)
top = sortedByDollarVolume[:self.__numberOfSymbols]
return [x.Symbol for x in top]
def FineSelectionFunction(self, fine):
selected = [x for x in fine if x.OperationRatios.OperationMargin.Value
and x.ValuationRatios.PriceChange1M
and x.ValuationRatios.BookValuePerShare]
# Gather factor values
factors = pd.DataFrame()
symbol_by_str_symbol = {}
for f in selected:
str_symbol = str(f.Symbol)
symbol_by_str_symbol[str_symbol] = f.Symbol
factors.loc[str_symbol, 'OperationMargin'] = f.OperationRatios.OperationMargin.Value
factors.loc[str_symbol, 'PriceChange1M'] = f.ValuationRatios.PriceChange1M
factors.loc[str_symbol, 'BookValuePerShare'] = f.ValuationRatios.BookValuePerShare
# Rank symbols by their factor values
factors_rank = factors.rank()
# Calculate score of each symbol
factors['score'] = factors_rank['OperationMargin'] * 0.2 + factors_rank['PriceChange1M'] * 0.4 + factors_rank['BookValuePerShare'] * 0.4
# Sort symbols by their score
sorted_by_score = factors['score'].sort_values(ascending=False).index
num_stocks = floor(len(selected)/self.num_portfolios)
self.symbols = [symbol_by_str_symbol[symbol] for symbol in sorted_by_score[:num_stocks]]
return self.symbols
def OnData(self, data):
if not self.symbols:
return
weight = 1 / len(self.symbols)
for symbol in self.symbols:
self.SetHoldings(symbol, weight)
self.symbols.clear()
def OnSecuritiesChanged(self, changes):
for security in changes.RemovedSecurities:
self.Liquidate(security.Symbol)