Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
0
Tracking Error
0
Treynor Ratio
0
Total Fees
$0.00
import numpy as np
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
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Orders import OrderStatus
from QuantConnect.Orders.Fills import ImmediateFillModel
from operator import itemgetter, attrgetter
import decimal as d


class ParameterizedAlgorithm(QCAlgorithm):

    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.'''

        #Risk Management Parameters
        TradingEquity = self.GetParameter("Starting Trading Equity") #Starting equity used for Trading
        FixedFractionalRisk = self.GetParameter("Fixed Fractional Risk") #Fraction of the equity risked in trading 
        
        #Screening Parameters
        MinVolumeDays = self.GetParameter("Days for Minimum Volume") #Number of days needed to compute the minimum number of volume (in shares and in dollar value) (default = 50 days)
        MinStockPrice = self.GetParameter("Minimum Stock Price") #Minimum price of stock 
        MaxPosition = self.GetParameter("Maximum Number of Positions") #Number of positions allowed 

        #Entry Parameters
        MADays = self.GetParameter("Moving Average Period") #Number of days needed to compute the simple moving average of stock price 
        ADXDays = self.GetParameter("ADX Period") #Number of days needed to compute the ADX 
        ADXThreshold = self.GetParameter("ADX Threshold") #Threshold used to determine if ADX produces buy signal. If ADX reaches above threshold, then the indicator produces a buy signal. (default = 45)
        ATRDays = self.GetParameter("ATR Period") #Number of days needed to compute the ATR
        RSIDays = self.GetParameter("RSI Period") #Number of days needed to compute the RSI
        RSIThreshold = self.GetParameter("RSI Threshold") #Threshold used to determine if RSI produces buy signal. If RSI reaches below threshold, then the indicator produces a buy signal. (default = 30)
        LimitOrderThreshold = self.GetParameter("Limit Order Threshold") #Threshold used to compute the limit price when the system signals to buy the stocks. The limit price = previous close - Limit Order Threshold*previous close  (default = 4%)

        #Exit Parameters
        ATRMultiplier = self.GetParameter("ATR Stop Loss Factor") #Multiplier of ATR used for computing the stop-loss
        ProfitTarget = self.GetParameter("Profit Target: ") #Threshold for Profit Target
        ExitDays = self.GetParameter("Exit Signal Period") #Number of days to wait for an exit signal (either a stop-loss or hitting the profit target). If there is neither a stop-loss or profit target, the system will exit after the exit signal period.

        #Set Default Parameter Values
        self.value_TradingEquity = 100,000 if TradingEquity is None else float(TradingEquity)
        self.value_FixedFractionalRisk = 0.02 if FixedFractionalRisk is None else float(FixedFractionalRisk)
        self.value_MinVolumeDays = 50 if MinVolumeDays is None else int(MinVolumeDays)
        self.value_MinStockPrice = 1 if MinStockPrice is None else float(MinStockPrice)
        self.value_MaxPosition = 10 if MaxPosition is None else int(MaxPosition) 
        self.value_MADays = 150 if MADays is None else int(MADays) 
        self.value_ADXDays = 7 if ADXDays is None else int(ADXDays)  
        self.value_ADXThreshold = 45 if ADXThreshold is None else int(ADXThreshold)
        self.value_ATRDays = 10 if ATRDays is None else int(ATRDays)
        self.value_RSIDays = 3 if RSIDays is None else int(RSIDays)
        self.value_RSIThreshold = 30 if RSIThreshold is None else int(RSIThreshold)
        self.value_LimitOrderThreshold = 0.04 if LimitOrderThreshold is None else float(LimitOrderThreshold)
        self.value_ATRMultiplier = 2.5 if ATRMultiplier is None else float(ATRMultiplier)
        self.value_ProfitTarget = 0.03 if ProfitTarget is None else float(ProfitTarget)
        self.value_ExitDays = 4 if ExitDays is None else int(ExitDays)
        
        self.SetStartDate(2017, 1, 1)                    #Set Start Date
        self.SetEndDate(2017, 6, 30)                     #Set End Date
        self.SetCash(100000)                             #Set Strategy Cash

        # use daily historical data and construct the universe according to the screennng criteria
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction)
        #self.AddSecurity(SecurityType.Equity, "AAPL", Resolution.Daily)

        #To determine the number of positions and update existing positions, make a storage for the list of entry orders and exit orders
        self.__openLimitOrders = []          #Stores Entry Limit Orders not yet filled
        self.__filledLimitOrders = []        #Stores Entry Limit Orders that are filled (i.e., existing positions)
        self.__openStopMarketOrder = []      #Stores Exit Stop Market Orders not yet filled
        self.__filledStopMarketOrder = []    #Stores Exit Stop Market Orders that are filled
        
        #We also create a list of dates when there is no exit signal
        self.__daysWithoutExitSignal = []
        self.Initial_Universe = {}

    def CoarseSelectionFunction(self, coarse):

        Filtered_Universe_1 = [x for x in coarse if x.Volume > 500000 and x.DollarVolume > 2500000 and x.Value > 1]
               
        return [i.Symbol for i in Filtered_Universe_1]

    def OnSecuritiesChanged(self, changes):
        self._changes = changes                        

    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.'''
         
        #Check for trade signals AS LONG AS the number of existing positions do not reach the maximum number of positions
        while len(self.__filledLimitOrders) < self.value_MaxPosition:
                
            for stock in self._changes.AddedSecurities:
                # User defined universe has symbols from AddSecurity/AddEquity calls
                if stock is UserDefinedUniverse:
                    continue

                #Get the ticker symbol
                stock_symbol = stock.Symbol

                #Compute the indicators needed for trade signal
                self.SimpleMA = self.SMA(stock_symbol,  150, Resolution.Daily)
                self.RSIndex = self.RSI(stock_symbol, 3, Resolution.Daily)
                self.AverageTrueRange = self.ATR(stock_symbol, 10,  MovingAverageType.Exponential, Resolution.Daily)   

                #Wait for our indicators to ready    
                if not self.SimpleMA.IsReady or not self.RSIndex.IsReady or not self.AverageTrueRange.IsReady or not self.AverageDirectionalIndex.IsReady:
                    return

                # Obtain the current value of the indicators
                CurrentSimpleMA = float(self.SimpleMA.Current.Value)
                CurrentRSIndex = float(self.RSIndex.Current.Value)
                CurrentAverageTrueRange = float(self.AverageTrueRange.Current.Value)
                CurrentAverageDirectionalIndex = float(self.AverageDirectionalIndex.Current.Value)

                #Get the EOD close price 
                close = self.Securities[stock_symbol].Close
        
                #Check if the following entry trade signal criteria are satisfied:
                    #Close of stock previous trading day must be above 150 day MA.
                    #7 day average directional index (ADX) must be above 45.
                    #Average true range (ATR) of last 10 days must be above 4% of last closing price.
                    #3 day RSI must be below 30.
            
                if close > CurrentSimpleMA and CurrentAverageDirectionalIndex > self.value_ADXThreshold and CurrentAverageTrueRange > d.Decimal(0.04)*close and CurrentRSIndex < self.value_RSIThreshold:

                    #If the entry trade signal criteria are satisfied...
            
                    #Then, compute the Position Size,
                    PositionSize = (100000*self.value_FixedFractionalRisk)/(self.value_ATRMultiplier*CurrentAverageTrueRange)
  
                    #And, place a buy limit order at 4% below the previous close (4% can be tweaked using the parameter Limit Order Threshold = self.value_LimitOrderThreshold)
                    LimitPrice = close - self.value_LimitOrderThreshold*close
                    entryOrder = self.LimitOrder(stock_symbol, PositionSize, LimitPrice)
                    self.__openLimitOrders.append(entryOrder)
                
                    #And then, place a stop-loss for this trade
                    StopPrice = LimitPrice - self.value_ATRMultiplier*CurrentAverageTrueRange                                                   #Stop price calculation
                    StopLoss = self.StopMarketOrder(stock_symbol, -PositionSize, StopPrice)                                                     #Place stop loss
                    self.__openStopMarketOrder.append(StopLoss)


        #Check FIRST if there are existing limit orders BEFORE exiting the trade
        if len(self.__openLimitOrders) != 0 or len(self.__filledLimitOrders) != 0:

            self.__LimitOrders = [x for x in self.__filledLimitOrders]
            

            #Collect all limit (entry) orders whether it's filled or not 
            for orders in self.__openLimitOrders:
                self.__LimitOrders.append(orders)
               
            
            #Then, for every limit order... 
            for traded_stocks in self.__LimitOrders:
            
                #check if the entry order is filled
                if self.CheckPairOrdersForFills(traded_stocks):
                   
                #If entry order is filled...

                    #First, store it to the list of existing positions if it's not yet stored
                    if traded_stocks not in self.__filledLimitOrders:
                        self.__filledLimitOrders.append(traded_stocks)

                    #Then, retrieve the ticker symbol of the traded stock and the number of shares traded
                    traded_symbol = orderTicket.Get(OrderField.symbol)           #ticker symbol                   
                    PositionSize = orderTicket.Get(OrderField.quantity)          #number of shares traded
                    EntryTime = orderTicket.Get(OrderField.time) 
                    EntryDate = EntryTime.date
                    CurrentDate = self.datetime.now()
                    
                    #Also, Compute the desired price in order to reach the profit target
                    price_ProfitTarget = orderTicket.Get(OrderField.LimitPrice)*(1 + self.value_ProfitTarget)

                    #If the price reaches profit target, exit the trade
                    if self.closeWindow[0] > price_ProfitTarget:
                        self.Liquidate(traded_symbol)
                        self.__filledLimitOrders.remove(traded_stocks)        #remove the trade from the list of existing positions
                    elif timedelta(CurrentDate - EntryDate) > self.value_ExitDays:
                        self.Liquidate(traded_symbol)
                        self.__filledLimitOrders.remove(traded_stocks)        #remove the trade from the list of existing positions

                    #Finally remove the list from the list of entry orders that not yet filled
                    self.__openLimitOrders.remove(traded_stocks)


        #Then, for every existing trades 
        for trades in self.__openStopMarketOrder:

            #If the stop-loss is filled, then remove the trade from the list of existing positions
            if self.CheckPairOrdersForFills(trades):
                self.__filledLimitOrders.remove(trades)
                

class SymbolData(object):
    def __init__(self, symbol):
        self.Symbol = Symbol
        self.RS_Index = RelativeStrengthIndex(3)

    def update(self, time, value):
        if self.RS_Index.Update(time, value):
            self.RS_Index = self.RS_Index.Current.Value