| Overall Statistics |
|
Total Trades 130 Average Win 0.22% Average Loss -0.48% Compounding Annual Return 11.400% Drawdown 8.600% Expectancy -0.016 Net Profit 11.400% Sharpe Ratio 0.71 Probabilistic Sharpe Ratio 35.182% Loss Rate 32% Win Rate 68% Profit-Loss Ratio 0.45 Alpha -0.093 Beta 0.901 Annual Standard Deviation 0.12 Annual Variance 0.014 Information Ratio -1.577 Tracking Error 0.071 Treynor Ratio 0.095 Total Fees $130.71 Estimated Strategy Capacity $16000000.00 Lowest Capacity Asset ROL R735QTJ8XC9X |
from AlgorithmImports import *
from datetime import timedelta, datetime
from QuantConnect.Data.UniverseSelection import *
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
class Third_Attempt(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2021, 1, 1) # Set Start Date
self.SetEndDate(2022, 1, 1) # Set Start Date
self.SetCash(100000) # Set Strategy Cash
self.AddUniverseSelection(Highperformance())
self.UniverseSettings.Resolution = Resolution.Daily
self.AddAlpha(BuyPerformance())
self.SetPortfolioConstruction(PortfolioBuilder())
self.AddRiskManagement(Trailing_SL_TP())
self.SetExecution(ImmediateExecutionModel())
class Highperformance (FundamentalUniverseSelectionModel):
def __init__(self):
super().__init__( True, None)
self.lastMonth = -1
#self.spy = Symbol.Create('SPY', SecurityType.Equity, Market.USA)
def SelectCoarse(self, algorithm, coarse):
#run the algorithm once a month, return Universe.Unchanged in case we are looking at exactly the same month
if algorithm.Time.month == self.lastMonth:
return Universe.Unchanged
self.lastMonth = algorithm.Time.month
sortedByVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
filteredByFundamentals = [x.Symbol for x in sortedByVolume if x.HasFundamentalData]
return filteredByFundamentals
def SelectFine(self, algorithm, fine):
sorted_high = sorted([x for x in fine if x.MarketCap > 2e9
and 0.5 > x.OperationRatios.AVG5YrsROIC.FiveYears > 0.20
and 50 > x.ValuationRatios.PERatio > 20
and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.FinancialServices
and x.AssetClassification.MorningstarSectorCode != MorningstarSectorCode.Healthcare],
key = lambda x: x.ValuationRatios.PERatio, reverse=True)
#fundamental_universe = [x.Symbol for x in sorted_high[:5]] + [self.spy]
return [x.Symbol for x in sorted_high[:5]]
class BuyPerformance(AlphaModel):
def __init__(self):
self.lastMonth = -1
self.newAdds = []
def Update(self, algorithm, data):
if algorithm.Time.month == self.lastMonth:
return []
self.lastMonth = algorithm.Time.month
insights = []
#printing the time (for troubleshooting purposes)
algorithm.Debug(str(algorithm.Time))
# For the new securities, if they are NOT invested yet and have Data (tradeable)
#the algo will add them to the insights
# So if the security is already invested, it will still be in the portfolio
#no matter if it gets removed from the Universe.
for added in self.newAdds:
if not algorithm.Securities[added].Invested and algorithm.Securities[added].HasData:
algorithm.Debug('Positive Insight : ' + str(added))
insights.append(Insight(added, timedelta(30), InsightType.Price, InsightDirection.Up))
return insights
def OnSecuritiesChanged(self, algorithm, changes):
# When assets are added to the universe, they will trigger OnSecuritiesChanged() event.
#From there, you can initialize any state or history required for the Alpha Model
algorithm.Debug('\n -----ALPHA MODEL ----: ' + str(algorithm.Time))
# Securities added into the universe will be added to self.newAdds
for security in changes.AddedSecurities:
symbol = security.Symbol
if symbol not in self.newAdds:
algorithm.Debug('added symbol : ' + str(symbol))
self.newAdds.append(symbol)
# Securities removed from the universe will be removed from self.newAdds
for security in changes.RemovedSecurities:
symbol = security.Symbol
if symbol in self.newAdds:
algorithm.Debug('removed symbol symbol : ' + str(symbol))
self.newAdds.remove(symbol)
class PortfolioBuilder(PortfolioConstructionModel):
def __init__(self):
self.lastMonth = -1
def CreateTargets (self, algorithm, insights):
if not algorithm.Time.month == self.lastMonth:
total_equity = algorithm.Portfolio.TotalPortfolioValue
else:
return[]
self.lastMonth = algorithm.Time.month
algorithm.Debug('\n -----PORTFOLIO CONSTRUCTION ----: ' + str(algorithm.Time))
#Create a list of PortfolioTarget objects from Insights
uniques = []
for insight in insights:
if not algorithm.Securities[insight.Symbol].Invested:
uniques.append(insight.Symbol)
# Now i m going to count how many securities are new, and how many securities are already invested coz I have to see
#the length of my new portfolio after the new insights get executed
#this is considered just because my algo does not sell securities until they hit stop loss
invested = [x.Key for x in algorithm.Portfolio if x.Value.Invested]
Stocks_In_Portfolio = len(uniques) + len(invested)
if Stocks_In_Portfolio != 0:
allocation = 0.95 * total_equity / Stocks_In_Portfolio
algorithm.Debug('Total Porfolio Value : $' + str(total_equity))
algorithm.Debug('Number of Stocks : ' + str(Stocks_In_Portfolio))
algorithm.Debug('Number of Invested stocks : ' + str(len(invested)))
algorithm.Debug('Number of uniques : ' + str(len(uniques)))
algorithm.Debug('Individual Security Allocation : $' + str(allocation))
# Now i need to calculate the new amount of shares per security that my portfolio needs to have. For this one i need to breakdown if the security has been invested
# if the security is invested, check the existing security allocation and compare it with the allocation value above
# if the security has recently been added, then we just need to buy the amoun of shares equivalent to the allocation
# **************
target_array = []
for x in algorithm.ActiveSecurities:
holding = x.Value
symbol = holding.Symbol
if holding.Invested:
algorithm.Debug('Security (Invested) :' + str(symbol))
algorithm.Debug('Old Amount of Shares :' + str(algorithm.Portfolio[symbol].Quantity))
shares = allocation / algorithm.Securities[symbol].Close
algorithm.Debug('New Shares Target :' + str(shares))
target = PortfolioTarget(symbol, shares)
elif not holding.Invested:
if algorithm.Securities[symbol].Close != 0:
shares = allocation / algorithm.Securities[symbol].Close
target = PortfolioTarget(symbol, shares)
algorithm.Debug('Security (NON Invested) :' + str(symbol))
algorithm.Debug('New Shares Target :' + str(shares))
else:
algorithm.Debug('DIVISION BY ZERO!!')
target_array.append(target)
return target_array
class Trailing_SL_TP(RiskManagementModel):
def __init__(self):
#These two dictionaries will store the symbol and the respective TP and SL
self.Take_Profit = dict()
self.Stop_Loss = dict()
def ManageRisk(self, algorithm, targets):
#Risk_targets is the list to be returned at the end
#Green is the % value of the take profit (e.g 1.30 is 30% on the upside)
#Red is the % value of the Stop Loss (e.g 0.90 is 10% on the downside)
risk_targets = list()
Green = 1.15
Red = 0.95
for target in targets:
symbol = target.Symbol
#We just care if the security is invested coz it is the whole point of the risk management model
if algorithm.Securities[symbol].Invested:
if symbol not in self.Take_Profit:
self.Take_Profit[symbol] = Green * algorithm.Portfolio[symbol].AveragePrice
self.Stop_Loss[symbol] = Red * algorithm.Portfolio[symbol].AveragePrice
if (symbol in self.Take_Profit):
if algorithm.Securities[symbol].Price >= self.Take_Profit[symbol]:
algorithm.Debug('Security ' + str(symbol) + 'just hit the Take Profit at ' + str(algorithm.Portfolio[symbol].Price))
self.Take_Profit[symbol] = Green * algorithm.Portfolio[symbol].Price
self.Stop_Loss[symbol] = Red * algorithm.Portfolio[symbol].Price
algorithm.Debug('New TP : ' + str(self.Take_Profit[symbol]) + ' New SL :' + str(self.Stop_Loss[symbol]))
if algorithm.Securities[symbol].Price <= self.Stop_Loss[symbol]:
algorithm.Debug('Security Liquidated: ' + str(symbol))
risk_targets.append(PortfolioTarget(symbol, 0))
return risk_targets