| Overall Statistics |
|
Total Trades 194 Average Win 4.80% Average Loss -1.95% Compounding Annual Return 48.521% Drawdown 17.300% Expectancy 1.219 Net Profit 777.418% Sharpe Ratio 1.921 Probabilistic Sharpe Ratio 95.192% Loss Rate 36% Win Rate 64% Profit-Loss Ratio 2.46 Alpha 0.427 Beta -0.084 Annual Standard Deviation 0.217 Annual Variance 0.047 Information Ratio 1.03 Tracking Error 0.285 Treynor Ratio -4.975 Total Fees $2176.48 |
from QuantConnect.Data.UniverseSelection import *
import math
import numpy as np
import pandas as pd
import scipy as sp
import statistics
class MomentumEffectAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 7, 1) # Set Start Date
self.SetEndDate(2020, 12, 25) # Set Start Date
self.SetCash(100000) # Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Daily
self.mom = {} # Dict of Momentum indicator keyed by Symbol
self.lookback = 252 # Momentum indicator lookback period
self.num_coarse = 100 # Number of symbols selected at Coarse Selection
self.num_fine = 50 # Number of symbols selected at Fine Selection
self.num_long = 5 # Number of symbols with open positions
self.month = -1
self.rebalance = False
self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
def CoarseSelectionFunction(self, coarse):
'''Drop securities which have no fundamental data or have too low prices.
Select those with highest by dollar volume'''
if self.month == self.Time.month:
return Universe.Unchanged
self.rebalance = True
self.month = self.Time.month
selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5],
key=lambda x: x.DollarVolume, reverse=True)
return [x.Symbol for x in selected[:self.num_coarse]]
def FineSelectionFunction(self, fine):
filtered_fine = [x for x in fine if x.OperationRatios.OperationMargin.Value
and x.ValuationRatios.PriceChange1M
and x.ValuationRatios.BookValuePerShare]
# rank stocks by three factor.
sortedByfactor1 = sorted(filtered_fine, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=True)
sortedByfactor2 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=True)
sortedByfactor3 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True)
stock_dict = {}
# assign a score to each stock, you can also change the rule of scoring here.
for i,ele in enumerate(sortedByfactor1):
rank1 = i
rank2 = sortedByfactor2.index(ele)
rank3 = sortedByfactor3.index(ele)
score = sum([rank1*0.1,rank2*0.8,rank3*0.1])
stock_dict[ele] = score
# sort the stocks by their scores
self.sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1],reverse=True)
sorted_symbol = [x[0] for x in self.sorted_stock][:20]
'''Select security with highest market cap'''
fine = [f for f in fine if f.ValuationRatios.PERatio > 0
and f.EarningReports.BasicEPS.TwelveMonths > 0
and f.EarningReports.BasicAverageShares.ThreeMonths > 0]
lowest_market_cap= sorted(fine,
key=lambda f: f.ValuationRatios.PERatio *
f.EarningReports.BasicEPS.TwelveMonths *
f.EarningReports.BasicAverageShares.ThreeMonths,
reverse=True)
turnoverss=[]
for i in lowest_market_cap[:]:
hist = self.History([i.Symbol], timedelta(days=10))
if not hist.empty:
i.Turnover=(hist["close"].iloc[0])
i.compare= (((statistics.mean(hist["close"]))-(statistics.mean(hist["open"])))/(statistics.stdev(hist["close"]-(statistics.mean(hist["open"])))))
turnoverss.append(i.compare)
else:
lowest_market_cap.remove(i)
lowest_market_cappp=sorted(lowest_market_cap,key=lambda f:f.compare,reverse=False)
lowest_market_capp=lowest_market_cappp[:5]
turnovers=turnoverss[:3]
som=sum(turnovers)
self.long = [x.Symbol for x in set(lowest_market_capp).intersection(sorted_symbol)]
self.weights = {}
for i in lowest_market_capp:
self.weights[str(i.Symbol)] = i.compare
return self.long
def OnData(self, data):
if not self.rebalance: return
if self.long:
stocks_invested = [x.Key for x in self.Portfolio if x.Value.Invested]
# liquidate stocks not in the trading list
for i in stocks_invested:
if i not in self.long:
self.Liquidate(i)
# goes long on stocks with the lowest turnover
for i in self.long:
self.SetHoldings(i,-self.weights[str(i)])
# short on stocks with the highest turnover
self.rebalance = False