| Overall Statistics |
|
Total Trades 6139 Average Win 0.13% Average Loss -0.06% Compounding Annual Return 9.831% Drawdown 54.000% Expectancy 0.285 Net Profit 30.461% Sharpe Ratio 0.367 Probabilistic Sharpe Ratio 4.551% Loss Rate 58% Win Rate 42% Profit-Loss Ratio 2.07 Alpha 0.283 Beta -0.012 Annual Standard Deviation 0.768 Annual Variance 0.59 Information Ratio 0.245 Tracking Error 0.777 Treynor Ratio -23.386 Total Fees $6378.08 |
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
class NetNet(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1) # Set Start Date
self.SetEndDate(2017, 10, 31)
self.SetCash(100000) # Set Strategy Cash
self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction, None, None))
self.UniverseSettings.Resolution = Resolution.Daily
self.SetAlpha(ConstantAlphaModelSellEOY(InsightType.Price, InsightDirection.Up, timedelta(days=365)))
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(lambda time: None))
self.SetExecution(ImmediateExecutionModel())
self.SetBenchmark("SPY")
self.year = -1
def OnData(self, data):
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
Arguments:
data: Slice object keyed by symbol containing the stock data
'''
# if not self.Portfolio.Invested:
# self.SetHoldings("SPY", 1)
# on 15 Jan, filter for securities with fundamental data
def CoarseSelectionFunction(self, coarse):
if self.Time.year == self.year:
return Universe.Unchanged
self.year = self.Time.year
return [ x.Symbol for x in coarse if x.HasFundamentalData ][:1000]
# on 15 Jan, filter first for securities with shares and then filter a second time for net net stocks
def FineSelectionFunction(self, fine):
filtered = [ x for x in fine if ((x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths - x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths) > 0) and (x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths > 0) ]
# filtered = [ x for x in filtered if (x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths > 0) ]
# filtered = [ x for x in filtered if (x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths > 0) ]
filtered = [ x.Symbol for x in filtered if (x.Price / ((x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths - x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths - x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths) / x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths)) <= 0.66 ]
# for x in filtered:
# self.Log("Symbol: " + str(x.Symbol))
# self.Log("Name: " + x.CompanyReference.LegalName)
# self.Log(str(self.Time.month) + str(self.Time.day) + str(self.Time.year))
# self.Log("Price: " + str(x.Price))
# self.Log("Current Assets: " + str(x.FinancialStatements.BalanceSheet.CurrentAssets.ThreeMonths))
# self.Log("Total Liabilities: " + str(x.FinancialStatements.BalanceSheet.TotalLiabilitiesAsReported.ThreeMonths))
# self.Log("Preferred Stock: " + str(x.FinancialStatements.BalanceSheet.PreferredStock.ThreeMonths))
# self.Log("Shares: " + str(x.FinancialStatements.BalanceSheet.OrdinarySharesNumber.ThreeMonths))
# filtered = [ x.Symbol for x in filtered ]
return filtered[:100]
class ConstantAlphaModelSellEOY(AlphaModel):
''' Provides an implementation of IAlphaModel that always returns the same insight for each security'''
def __init__(self, type, direction, period, magnitude = None, confidence = None):
'''Initializes a new instance of the ConstantAlphaModel class
Args:
type: The type of insight
direction: The direction of the insight
period: The period over which the insight with come to fruition
magnitude: The predicted change in magnitude as a +- percentage
confidence: The confidence in the insight'''
self.type = type
self.direction = direction
self.period = period
self.magnitude = magnitude
self.confidence = confidence
self.securities = []
typeString = Extensions.GetEnumString(type, InsightType)
directionString = Extensions.GetEnumString(direction, InsightDirection)
self.Name = '{}({},{},{}'.format(self.__class__.__name__, typeString, directionString, strfdelta(period))
if magnitude is not None:
self.Name += ',{}'.format(magnitude)
if confidence is not None:
self.Name += ',{}'.format(confidence)
self.Name += ')'
def Update(self, algorithm, data):
''' Creates a constant insight for each security as specified via the constructor
Args:
algorithm: The algorithm instance
data: The new data available
Returns:
The new insights generated'''
insights = []
for security in self.securities:
# security price could be zero until we get the first data point. e.g. this could happen
# when adding both forex and equities, we will first get a forex data point
if security.Price != 0:
if algorithm.Time.month == 12 and algorithm.Time.day > 20:
insights.append(Insight(security.Symbol, self.period, self.type, InsightDirection.Flat, self.magnitude, self.confidence))
elif not algorithm.Securities[security.Symbol].Invested:
insights.append(Insight(security.Symbol, self.period, self.type, self.direction, self.magnitude, self.confidence))
return insights
def OnSecuritiesChanged(self, algorithm, changes):
''' Event fired each time the we add/remove securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm'''
for added in changes.AddedSecurities:
self.securities.append(added)
# this will allow the insight to be re-sent when the security re-joins the universe
for removed in changes.RemovedSecurities:
if removed in self.securities:
self.securities.remove(removed)
def strfdelta(tdelta):
d = tdelta.days
h, rem = divmod(tdelta.seconds, 3600)
m, s = divmod(rem, 60)
return "{}.{:02d}:{:02d}:{:02d}".format(d,h,m,s)