Overall Statistics
Total Trades
189
Average Win
1.47%
Average Loss
-1.39%
Compounding Annual Return
0.138%
Drawdown
38.200%
Expectancy
0.036
Net Profit
0.523%
Sharpe Ratio
0.097
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.05
Alpha
0.034
Beta
-0.959
Annual Standard Deviation
0.181
Annual Variance
0.033
Information Ratio
0.004
Tracking Error
0.181
Treynor Ratio
-0.018
Total Fees
$1543.04
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from System.Collections.Generic import List
import decimal as d
import numpy as np
import time
from datetime import datetime
import numpy as np
from scipy import stats
import pandas as pd


class AFCMOM(QCAlgorithm):
    '''Basic template algorithm simply initializes the date range and cash'''

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
        self.first = -1
        self.bi_weekly = 0
        self.SetStartDate(2006,1,1)  
        self.SetEndDate(2009,1,1)    
        self.SetCash(100000)           
        
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        
        self.AddUniverse(self.CoarseSelectionFunction,self.FineSelectionFunction)
        self.UniverseSettings.Resolution = Resolution.Daily
        
        self.spy_200_sma = self.SMA("SPY",200,Resolution.Daily)
        
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Wednesday,DayOfWeek.Wednesday), \
                 self.TimeRules.At(12, 0), \
                 Action(self.rebalnce))
                 
        self.stocks_to_trade = []

        self.SetWarmUp(201)
        
        self.splotName = 'Strategy Info'
        sPlot = Chart(self.splotName)
        sPlot.AddSeries(Series('Leverage',  SeriesType.Line, 0))
        self.AddChart(sPlot)
        
    def OnData(self, data):
        #if self.IsWarmingUp: return
    
        # assuming daily mode,dont chart in a smaller res and kill quota
        self.account_leverage = self.Portfolio.TotalAbsoluteHoldingsCost / self.Portfolio.TotalPortfolioValue
        self.Plot(self.splotName,'Leverage', float(self.account_leverage))
        
    def CoarseSelectionFunction(self, coarse):
        filtered_stocks = filter(lambda x: x.DollarVolume >250000,coarse)
        filtered_stocks = filter(lambda x: x.HasFundamentalData,filtered_stocks)
        filtered_stocks = filter(lambda x: x.Price >=20,filtered_stocks)
        filtered_stocks = filtered_stocks[:100]
        return [stock.Symbol for stock in filtered_stocks]
        
    def FineSelectionFunction(self, fine):
        filtered_stocks = filter(lambda x: x.SecurityReference.IsPrimaryShare,fine)
        return [stock.Symbol for stock in filtered_stocks]
        
    def OnSecuritiesChanged(self, changes):
        dt = datetime(self.Time.year,self.Time.month,self.Time.day)
        if dt.weekday() != 3 or self.Securities[self.spy].Price < self.spy_200_sma.Current.Value:
            return 
        
        # manage stocks_to_trade(add and remove items)
        for security in changes.RemovedSecurities:
            # update stocks_to_trade list
            if security.Symbol in self.stocks_to_trade:
                self.stocks_to_trade.remove(security.Symbol)
            if security.Invested:
                self.Liquidate(security.Symbol)
        for security in changes.AddedSecurities:
            #security.MarginModel = PatternDayTradingMarginModel()
            if security.Symbol not in self.stocks_to_trade:
                self.stocks_to_trade.append(security.Symbol)
        
        if self.stocks_to_trade:
            for stock in self.stocks_to_trade:
                ATR = self.my_ATR(stock,14)
                self.stocks_to_trade.sort(key = lambda x: self.get_slope(stock,90),reverse= True)
                maximum_range = int(round(len(self.stocks_to_trade) * 0.10))
                self.stocks_to_trade[:maximum_range]
                cash = float(self.Portfolio.Cash)
                oo = len(self.Transactions.GetOpenOrders(stock))
                if self.Securities[stock].Price >self.moving_average(stock,100) and not self.gapper(stock,90) and cash >0 and not oo:
                    self.SetHoldings(stock,self.weight(stock,ATR))
    
        
    def rebalnce(self):
        self.bi_weekly +=1
        if self.bi_weekly%2 == 0:
            for stock in self.Portfolio.Values:
                if stock.Invested:
                    symbol = stock.Symbol
                    shares_held = float(self.Portfolio[symbol].Quantity)
                    if (self.Securities[symbol].Price < self.moving_average(symbol,100) and shares_held >0) or (self.gapper(symbol,90) and shares_held>0):
                        self.Liquidate(symbol)
                    else:
                        if shares_held >0: 
                            ATR = self.my_ATR(symbol,20)
                            cost_basis = float(self.Portfolio[symbol].AveragePrice)
                            shares_held = float(self.Portfolio[symbol].Quantity)
                            percent_of_p = ((cost_basis * shares_held )/ float(self.Portfolio.TotalPortfolioValue))
                            weight= self.weight(symbol,ATR)
                            diff_in_desired_weight = weight -percent_of_p
                            if diff_in_desired_weight < 0:
                                order_amount = shares_held * diff_in_desired_weight
                                self.MarketOrder(symbol,order_amount)
                
    
    def GetHistory(self, security, period):
        security_data = self.History([security],period,Resolution.Daily)
        
        # check if candle has close component(assume others?)
        if 'close' not in security_data.columns:
            self.Log("History had no Close for %s"%security)
            return None
        
        # we need enough for the np.diff which removes 1 from length
        if len(security_data.close.index) < period: 
            self.Log("Close test had too few or no values for %s with period %d"%(security, period))
            return None
            
        return security_data
        
    def gapper(self,security,period):
        if not self.Securities.ContainsKey(security):
            return 0
            
        security_data = self.GetHistory(security, period)
        if security_data is None: return 0
    
        close_data = security_data.close.values
        
        return np.max(np.abs(np.diff(close_data))/close_data[:-1])>=0.15
        
    def get_slope(self,security,period):
        if not self.Securities.ContainsKey(security):
            return 0
            
        security_data = self.GetHistory(security, period)
        if security_data is None: return 0
    
        y = np.log(security_data.close.values)
        x = np.arange(len(y))
        slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
        
        return ((np.exp(slope)**252)-1)*(r_value**2)
        
    def my_ATR(self,security,period):
        if not self.Securities.ContainsKey(security):
            return 0
        self.first+=1
        
        security_data = self.GetHistory(security, period)
        if security_data is None: return 0
        
        c_data = security_data.close.values
        l_data= security_data.low.values
        h_data = security_data.high.values
        
        true_range = h_data-l_data
        average_true_range = np.mean(true_range)
        average_true_range_smooted = ((average_true_range*13)+true_range[-1])/14
        
        return average_true_range_smooted if not self.first else average_true_range
    
    def weight(self,security,atr):
        risk = float(self.Portfolio.TotalPortfolioValue)*0.0001
        
        # prevent div by zero
        if atr == 0.:
            return 0.
            
        return (((risk/atr) * float(self.Securities[security].Price))/float(self.Portfolio.TotalPortfolioValue)*100)
        
    def moving_average(self,security,period):
        if not self.Securities.ContainsKey(security):
            return 0
        
        security_data = self.GetHistory(security, period)
        if security_data is None: return 0
        
        return security_data.close.mean()