| 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] <= -1import 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)