Overall Statistics
Total Trades
727
Average Win
2.58%
Average Loss
-1.33%
Compounding Annual Return
19.466%
Drawdown
20.900%
Expectancy
0.390
Net Profit
314.902%
Sharpe Ratio
0.988
Loss Rate
53%
Win Rate
47%
Profit-Loss Ratio
1.93
Alpha
0.11
Beta
0.484
Annual Standard Deviation
0.2
Annual Variance
0.04
Information Ratio
0.079
Tracking Error
0.205
Treynor Ratio
0.409
Total Fees
$1997.60
import talib
import pandas as pd
import numpy as np
from scipy.ndimage.interpolation import shift
from scipy.signal import butter, lfilter, freqz
from datetime import datetime
from collections import deque

def butter_lowpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = butter(order, normal_cutoff, btype='low', analog=False)
    return b, a

def butter_lowpass_filter(data, cutoff, fs, order=5):
    b, a = butter_lowpass(cutoff, fs, order=order)
    y = lfilter(b, a, data)
    return y

class MMI:
    def __init__(self, name, period):
        self.Name = name
        self.Time = datetime.min
        self.Value = 0
        self.LowPass = 0
        self.IsReady = False
        self.queue = deque(maxlen=period)
        self.Hist = []
        self.LowPassHist = []
        self.IsFalling = False

    # Update method is mandatory
    def Update(self, input):
        self.queue.appendleft(input.Close)
        self.Time = input.EndTime
        self.IsReady = len(self.queue) == self.queue.maxlen
        
        if not self.IsReady:return
        
        m = np.median(self.queue)
        nh, nl = 0, 0
        for i in range(1, len(self.queue)):
            if self.queue[i] > m and self.queue[i] > self.queue[i-1]:
                nl += 1
            elif self.queue[i] < m and self.queue[i] < self.queue[i-1]:
                nh += 1
                
        self.Value = 100 * (nl + nh) / (len(self.queue) - 1)
        self.Hist.append(self.Value)
        self.LowPass = butter_lowpass_filter(np.array(self.Hist), 3.667, len(self.queue))[-1]
        self.LowPassHist.append(self.LowPass)
        self.IsFalling = len(self.LowPassHist) >= 2 and self.LowPassHist[-1] - self.LowPassHist[-2] <= -1
import numpy as np
from datetime import timedelta
from decimal import Decimal

from func import MMI


### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
### framework you can use for designing an algorithm.
### </summary>
class BasicTemplateAlgorithm(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.symbol = 'GOOGL'
        self.SetStartDate(2010,1, 1)  #Set Start Date
        self.SetEndDate(2018,1,1)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash
        # Find more symbols here: http://quantconnect.com/data
        #self.AddCfd(self.symbol, Resolution.Daily, Market.Oanda)
        self.AddEquity(self.symbol, Resolution.Daily)
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
        
        self.SetBenchmark(self.symbol)
        
        self.ha = self.HeikinAshi(self.symbol, Resolution.Daily)
        
        self.SetTimeZone("EST")
        
        self.PIP = self.Securities[self.symbol].SymbolProperties.MinimumPriceVariation
        
        self.barWindowDaily = RollingWindow[TradeBar](10)
        
        #consolidator = QuoteBarConsolidator(timedelta(minutes=60))
        #consolidator.DataConsolidated += self.ConsolidatorHandler
        #self.SubscriptionManager.AddConsolidator(self.symbol, consolidator)
        
        #self.atr = self.ATR(self.symbol, 2, MovingAverageType.Simple, Resolution.Hour)
        
        self.MMI = MMI("mmi", 200)
        self.RegisterIndicator(self.symbol, self.MMI, Resolution.Daily)
        
        self.alma = self.ALMA(self.symbol, 26, 6, 0.85, Resolution.Daily)
        
        
        stockPlot = Chart("Trade Plot")
        # On the Trade Plotter Chart we want 3 series: trades and price:
        stockPlot.AddSeries(Series("Buy", SeriesType.Scatter, 0))
        stockPlot.AddSeries(Series("Sell", SeriesType.Scatter, 0))
        stockPlot.AddSeries(Series("Price", SeriesType.Line, 0))
        stockPlot.AddSeries(Series("SenkouA", SeriesType.Line, 0))
        stockPlot.AddSeries(Series("SenkouB", SeriesType.Line, 0))
        #stockPlot.AddSeries(Series("RSI", SeriesType.Line, 1))
        #stockPlot.AddSeries(Series("RSI-SMA", SeriesType.Line, 1))
        
        self.AddChart(stockPlot)
        
        self.buy_ticket = None
        self.sl_price = None
        
        self.num_bars_in_position = 0
        self.sl_triggered = False
        
        self.buy_time = None
        self.max_close = 0
        self.min_close = 100000000
        
        self.buy_price = None

        #avgCross = Chart("Channels")
       
        #self.AddChart(avgCross)
        
    def ConsolidatorHandler(self, sender, bar):
        if self.IsWarmingUp: return
    
        #self.Debug(f"consolidated bar: {bar.Time}, ma val: {self.ma_high_1.Current.Value}")
        self.Plot("Trade Plot", "Price", bar.Close)
 
        
        #if not (self.Time.hour >= 3 and self.Time.hour <= 15): 
        #    return 
        
        position = self.Portfolio[self.symbol].Quantity
        
        
        # long entry
        #if position <= 0 and bar.Close > self.ma_high_1.Current.Value and self.rsi.Current.Value > 55 \
        #and self.rsi.Current.Value > self.rsi_sma.Current.Value:
        #    if self.buy_ticket is None or self.buy_ticket.Status in (OrderStatus.Canceled, OrderStatus.Filled):
        #        self.buy_ticket = self.StopMarketOrder(self.symbol, 50000, bar.High + 6 * self.PIP)
            
        # cancel stop buy order if a false brakeout occured
        #if self.buy_ticket is not None and not self.buy_ticket.Status == OrderStatus.Filled \
        #and bar.Close < self.ma_high_1.Current.Value:
        #    self.buy_ticket.Cancel()
            
        #if not position == 0:
        #    self.num_bars_in_position += 1
            
    
    def OnOrderEvent(self, OrderEvent):
        if OrderEvent.FillQuantity == 0:
            return
        
        fetched = self.Transactions.GetOrderById(OrderEvent.OrderId)
        
        #self.Debug("{} was filled. Symbol: {}. Quantity: {}. Direction: {}"
        #           .format(str(fetched.Type), 
        #                   str(OrderEvent.Symbol), 
        #                   str(OrderEvent.FillQuantity), 
        #                   str(OrderEvent.Direction)))
                           
        if OrderEvent.Direction == 0:
            self.buy_price = OrderEvent.FillPrice
            self.Plot("Trade Plot", "Buy", OrderEvent.FillPrice)
         
        if OrderEvent.Direction == 1:
            self.Plot("Trade Plot", "Sell", OrderEvent.FillPrice)
    

    def OnData(self, data):
        barr = data[self.symbol] 
        
        if barr is None: return
        
        if not self.barWindowDaily.IsReady or not self.alma.IsReady: 
            self.Debug("warming up")
            self.barWindowDaily.Add(self.ha.CurrentBar)
            return
      
        position = self.Portfolio[self.symbol].Quantity
    
        
        bar = self.ha.CurrentBar
        
        if position == 0 and bar.Close > bar.Open and (abs(bar.Close - bar.Open) / abs(self.barWindowDaily[0].Close - self.barWindowDaily[0].Open) - 1 >= 0.004 
        or self.barWindowDaily[0].Close > self.barWindowDaily[0].Open) and self.MMI.Value < 55: 
            self.SetHoldings(self.symbol, 1)
            
        if position == 0 and bar.Close > bar.Open and self.MMI.Value < 55 and barr.Close > self.alma.Current.Value: 
            self.SetHoldings(self.symbol, 1)
            
        #if position == 0 and bar.Close < bar.Open and barr.Close < self.alma.Current.Value: 
        #    self.SetHoldings(self.symbol, -1)
            
        #if position == 0 and bar.Close < bar.Open and bar.Close < self.barWindowDaily[0].Close and self.MMI.LowPass < 53: 
        #    self.SetHoldings(self.symbol, -1)
            
        #if position > 0 and (self.MMI.Value > 55 or barr.Close / self.buy_price - 1 > 0.01): 
        #    self.Liquidate(self.symbol)
        
        if position > 0 and (self.buy_price / barr.Close - 1 >= 0.0 or self.MMI.Value > 55):
            self.Liquidate(self.symbol)
            
        if position < 0 and bar.Close > bar.Open:
            self.Liquidate(self.symbol)
            
        if position > 0:
            self.buy_price = barr.Close
            
            
        #if position < 0 and bar.Low < self.barWindowDaily[0].Low:
        #    self.Liquidate(self.symbol)
            
        self.barWindowDaily.Add(self.ha.CurrentBar)
        
    
        
        self.Plot("Trade Plot", "Price", bar.Close)

        
        #self.Debug(f"MMI: {self.MMI.LowPass}")
            
        
        #if position > 0 and bar.Close <= self.sl_price:
            #self.sl_triggered = True
        #    self.Liquidate(self.symbol)