| Overall Statistics |
|
Total Trades 390 Average Win 0.24% Average Loss -0.21% Compounding Annual Return 18.615% Drawdown 5.300% Expectancy 0.416 Net Profit 21.933% Sharpe Ratio 1.835 Loss Rate 33% Win Rate 67% Profit-Loss Ratio 1.12 Alpha 0.129 Beta 1.967 Annual Standard Deviation 0.09 Annual Variance 0.008 Information Ratio 1.625 Tracking Error 0.09 Treynor Ratio 0.084 Total Fees $487.23 |
# Strategy : Rebalancing Portfolio on Day 1 of the Every Month based on the fundamental data and liquidity of stock
# Backtest Result : Creative Red Butterfly
import numpy as np
import datetime as dt
from datetime import datetime,timedelta
class QCAlgorithm(QCAlgorithm):
def Initialize(self):
# Backtest Parameters
self.SetStartDate(2013,12,31)
self.SetEndDate(2015,3,1)
self.SetCash(100000.00)
# Setting Daily Resolution for all Securities in Universe.
self.UniverseSettings.Resolution = Resolution.Daily
#Adding Universe based on the defined filters
self.AddUniverse(self.MyCoarseUniverse,self.MyFineUniverse)
# Brokerage Model
self.SetBrokerageModel(BrokerageName.Default, AccountType.Cash)
self.SetTimeZone(TimeZones.NewYork)
#reference check for the developing new portfolio while rebalancing whether securities has changed
self._changes = None
# Schedule Rebalancing of Portfolio on Starting of the Month at 10 AM.
self.Schedule.On(self.DateRules.MonthStart(), self.TimeRules.At(10, 00), Action(self.Rebalance))
# define first filter which will filter the stocks based on the Dollervolume and if it has fundamental deta available
# so that we can run those stocks through second filter.
def MyCoarseUniverse(self,coarse):
for x in coarse:
# sorting of stocks based on dollervolume in descending order
sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True)
# filter if the stock has dollarvolume of more than 1000000 and has fundamentaldata
filtered = [ x.Symbol for x in sortedByDollarVolume if x.DollarVolume > 1000000 and x.HasFundamentalData == True]
#return first 1000 such stocks that we will feed in our second "fine" filter
return filtered[:1000]
# The output of the coarse function will in this function for second filter
def MyFineUniverse(self,fine):
#sort stocks based on the EV / EBITDA in descending order
sortedByEVRatio = sorted(fine, key=lambda x: x.ValuationRatios.EVToEBITDA, reverse=True)
# filter the stock list which has BookValue per share less than 1. ( These stocks's potential has not been realised due to market sentiments)
filtered = [x.Symbol for x in sortedByEVRatio if x.ValuationRatios.BookValuePerShare <= 1]
return filtered[:50]
def OnSecuritiesChanged(self, changes):
# changes in our security Universe will change the value of our reference variable defined in Initialize.
# It will have the information of addedsecurity and removed security which we will use to buy and liquidate security from our existing portfolio to rebalance.
self._changes = changes
# ScheduledOn will triger this function on month start as defined in Initialize
def Rebalance(self):
# check the day of datetime object if it is first day of the month if "No", do nothing and return
if self.Time.day != 1 :
return self.Debug(" No Need to Rebalance, Month :" + str(self.Time.month))
else :
self.Debug(" Portfolio will be Rebalanced , Month :" + str(self.Time.month) + "Day :" + str(self.Time.day))
# OnSecurityChanged will change the value of reference variable through which we get to know if there is change in our Universe
#If the variable is "None" that means no security has been added and removed from our universe. It will keep our portfolio as it is.
if not self._changes is None :
# give equal weight to security that has been added in the universe
if len(self._changes.AddedSecurities) == 0:
weightage = 0.5
else :
weightage = 1/len(self._changes.AddedSecurities)
# liquidate portfolio
if self.Portfolio.Invested:
self.Liquidate()
# invest in added securities based on predetermined weightage
for x in self._changes.AddedSecurities:
self.Debug("Security Weightage : " + str(weightage))
self.SetHoldings(x.Symbol,weightage)
self.Debug("Portfolio Rebalanced")
# change the value of variable again to None.
self._changes = None