| Overall Statistics |
|
Total Trades 3385 Average Win 0.38% Average Loss -0.27% Compounding Annual Return 22.178% Drawdown 35.100% Expectancy 0.167 Net Profit 118.953% Sharpe Ratio 1.188 Probabilistic Sharpe Ratio 57.216% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.41 Alpha 0.209 Beta -0.1 Annual Standard Deviation 0.164 Annual Variance 0.027 Information Ratio 0.221 Tracking Error 0.257 Treynor Ratio -1.948 Total Fees $0.00 |
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
class VentralModulatedCompensator(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2016, 11, 2) # Set Start Date
self.SetCash(3500) # Set Strategy Cash
# self.AddEquity("SPY", Resolution.Minute)
self.SetExecution(ImmediateExecutionModel())
self.UniverseSettings.Leverage = 1
self.UniverseSettings.Resolution = Resolution.Daily
self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction, None, None))
self.SetSecurityInitializer(self.CustomSecurityInitializer)
#self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
#self.Securities[AddedSecurities].FeeModel = ConstantFeeModel(0)
#self.ActiveSecurities.FeeModel = ConstantFeeModel(0)
self.UniverseSettings.MinimumTimeInUniverse = 10
self.averages = { }
def CustomSecurityInitializer(self, security):
security.SetFeeModel(CustomFeeModel())
def CoarseSelectionFunction(self, coarse):
selected = []
universe = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)
universe = [c for c in universe if c.Price > 10]
for coarse in universe:
symbol = coarse.Symbol
if symbol not in self.averages:
# 1. Call history to get an array of 200 days of history data
history = self.History(symbol, 31, Resolution.Daily)
#2. Adjust SelectionData to pass in the history result
self.averages[symbol] = SelectionData(history)
self.averages[symbol].update(self.Time, coarse.AdjustedPrice)
if self.averages[symbol].is_ready() and self.averages[symbol].fast > self.averages[symbol].slow:
selected.append(symbol)
return selected
def FineSelectionFunction(self, fine):
#sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.NormalizedPEGatio, reverse=False)
#sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.EVToEBITDA.latest > 0, reverse=False)
#sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.PricetoEBITDA > 0, reverse=False)
#sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.PCFRatio,reverse=False)
#sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.CashReturn, reverse=True)
#sorted_by_roic = sorted(fine, key=lambda x: x.OperationRatios.ROIC.ThreeMonths, reverse=True)[:150]
sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.PricetoEBITDA, reverse=True)[:100]
sorted_by_roic = sorted(sorted_by_ev, key=lambda x: x.OperationRatios.ROIC.ThreeMonths, reverse=True)[:50]
sorted_by_bookvalue = sorted(sorted_by_roic, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse = False)[:15]
#CashReturn = 8.98%, 4.2 alpha
#EVToRevenue3YrAvg = Not worth pursuing?
#EVToFCF3YrAvg
#PBRatio3YrAvg = 9.15%, 4.8 alpha
#EarningYield
#NormalizedPEGatio
#PB/Cash Return = 1.235, alpha = .177
#ROIC/PBRATIO3YRAVG = 88.78%, alpha = .194
return [x.Symbol for x in sorted_by_bookvalue]
#sorted_by_cash = sorted(sorted_by_roic, key=lambda x: x.ValuationRatios.CashReturn, reverse=True)
#return [x.Symbol for x in sorted_by_cash[:60]]
#sorted_by_pb = sorted(sorted_by_roic, key=lambda x: x.ValuationRatios.PBRatio3YrAvg, reverse=False)
#return [x.Symbol for x in sorted_by_pb[:15]]
def OnSecuritiesChanged(self, changes):
self.changes = changes
self.Log(f"OnSecuritiesChanged({self.Time}):: {changes}")
for security in self.changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
for security in self.changes.AddedSecurities:
self.SetHoldings(security.Symbol, 0.04)
class CustomFeeModel:
def GetOrderFee(self, parameters):
return OrderFee(CashAmount(0, 'USD'))
class SelectionData():
#3. Update the constructor to accept a history array
def __init__(self, history):
self.slow = SimpleMovingAverage(30)
self.fast = SimpleMovingAverage(1)
#4. Loop over the history data and update the indicators
for bar in history.itertuples():
self.fast.Update(bar.Index[1], bar.close)
self.slow.Update(bar.Index[1], bar.close)
def is_ready(self):
return self.slow.IsReady and self.fast.IsReady
def update(self, time, price):
self.fast.Update(time, price)
self.slow.Update(time, price)