| Overall Statistics |
|
Total Trades 2594 Average Win 2.78% Average Loss -2.09% Compounding Annual Return 314.028% Drawdown 35.200% Expectancy 0.304 Net Profit 140772.120% Sharpe Ratio 2.341 Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.33 Alpha 0.351 Beta 59.578 Annual Standard Deviation 0.569 Annual Variance 0.323 Information Ratio 2.312 Tracking Error 0.569 Treynor Ratio 0.022 Total Fees $4074229.55 |
from QuantConnect.Data.UniverseSelection import *
import pandas as pd
import numpy as np
from scipy import stats
import decimal as d
import random
class BasicTemplateAlgorithm(QCAlgorithm):
def Initialize(self):
self.Debug("%%%%%%%%%%%%% Initialize")
self.my_universe = []
# Set the initial cash, start, and end dates for backtesting
self.mycash = 30000
self.mycash = 5000
self.cash_per_trade = 5000
self.SetCash(self.mycash)
self.SetStartDate(2013,1,1)
#self.SetEndDate(2018,11,11)
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) # Or override account type
# Subscribe to data for securities returned in my universe
self.UniverseSettings.Resolution = Resolution.Minute
self.UniverseSettings.MinimumTimeInUniverse = 0
self.AddUniverse(self.universe_filter_course, self.universe_filter_fine)
# Schedule the 'trade' method to run
# Add SPY to our data subscription so we can schedule from it (use defaults)
self.Schedule.On(self.DateRules.EveryDay(),
self.TimeRules.At(9, 31),
Action(self.my_trade))
self.Schedule.On(self.DateRules.EveryDay(),
self.TimeRules.At(15, 50),
Action(self.liquidate_portfolio))
self.Schedule.On(self.DateRules.EveryDay(),
self.TimeRules.At(11, 00),
Action(self.cancel_orders))
overlayPlot = Chart("Overlay Plot")
self.AddChart(overlayPlot)
#self.Schedule.On(
# self.DateRules.EveryDay('SPY'),
# self.TimeRules.AfterMarketOpen('SPY', 1),
# Action(self.my_trade))
def universe_filter_fine(self, fine_data):
columns = ['SecurityType', 'ShareClassStatus', 'ExchangeId', 'IsDepositaryReceipt', 'IsPrimaryShare']
data_df = pd.DataFrame.from_records(
[[getattr(s.SecurityReference, name) for name in columns] for s in fine_data],
#[[s.ValuationRatios.PERatio, s.ValuationRatios.SalesYield] for s in fine_data],
index=[s.Symbol for s in fine_data],
columns=columns,)
data_df = data_df.query("(SecurityType == 'ST00000001') and (IsDepositaryReceipt == False) and (IsPrimaryShare)")
#data_df = data_df.sort_values(data_df.columns[0], ascending = True)
data_df.sort_index(inplace=True)
#data_df = data_df[:10]
#self.Debug(data_df)
#self.Debug("data is: {}".format(data_df.EVToEBITDA.values))
self.universe = data_df.index.tolist()
self.my_universe = data_df.index.tolist()
#self.Debug("fine size is: {}".format(len(self.universe)))
return self.universe
def universe_filter_course(self, coarse_data):
# With data_df already initialized globally, this should now just
# populate securities for values matching query instead.
columns = ['Price', 'DollarVolume', 'HasFundamentalData', 'Volume']
data_df = pd.DataFrame.from_records(
[[getattr(s, name) for name in columns] for s in coarse_data],
index=[s.Symbol for s in coarse_data],
columns=columns,
coerce_float=True)
data_df = data_df.sort_values(by='Volume', ascending=False)
data_df = data_df.query("HasFundamentalData " \
"and (DollarVolume > @data_df.DollarVolume.quantile(.20))" \
"and (DollarVolume < @data_df.DollarVolume.quantile(.21))")
'''
data_df = data_df.query("HasFundamentalData ")
data_df = data_df.sort_values('DollarVolume')
data_df = data_df[data_df.DollarVolume > 1000000]
data_df = data_df[50:100]
'''
self.StocksDailyData =data_df
if len(data_df) == 0:
return
self.my_universe = data_df.index.tolist()
return data_df.index.tolist()
def my_trade(self):
# See how many securities we have in our Securities list
'''
for universe in self.UniverseManager.Keys:
self.Debug("universe name: {}".
format(universe))
'''
self.Debug("%%%%%%%%%%%%% my_trade")
for universe in self.UniverseManager.Values:
self.Debug("universe count: {}".
format(universe.Members.Count))
if len(self.my_universe) == 0:
self.Debug('Empty universe')
return
stock_list = []
for stock in self.my_universe:
stock_list.append(stock)
stockID = self.AddEquity(stock.Value)
h1 = self.History(stock_list, 31, Resolution.Daily)
h2 = self.History(stock_list, 1, Resolution.Minute)
#self.Debug("\nstock_list - 1: %s "%([s.Value for s in stock_list]))
#self.Debug("\nstock_list - 2: %s "%([s.Value for s in self.my_universe]))
#self.Debug("\nstock_list - 3: %s "%([s[0] for s in h2.index.tolist()]))
if not 'close' in h1.keys() or \
not 'open' in h2.keys():
self.Debug('Missing Data')
return
noMatch = returnNotMatches(h1.index.levels[0],h2.index.levels[0])
h1.drop(noMatch[0], level='symbol', inplace=True)
h2.drop(noMatch[1], level='symbol', inplace=True)
#h1.index.get_level_values(0).unique()
closes = h1['close'].unstack(level=0).reset_index(drop=True).values
opens = h1['open'].unstack(level=0).append(
h2['open'].unstack(level=0),
ignore_index=True)[1:].reset_index(drop=True).values
gap = opens - closes
gap_z = stats.zscore(gap, axis=0, ddof=1)
min_gap_z = np.nanmin(gap_z[-1,:])
max_gap_z = np.nanmax(gap_z[-1,:])
k_min = np.nanargmin(gap_z[-1,:])
k_max = np.nanargmax(gap_z[-1,:])
#self.Debug("Min (%s) %f Max (%s) %f"%(self.my_universe[k_min], min_gap_z, self.my_universe[k_max], max_gap_z))
i=0
buyList = []
sellList = []
self.Debug("NOMBER IN UNIVERSe {}".format(len(self.my_universe)))
#if len(stock_list) != len(h1.index.levels[0]) or \
# len(stock_list) != len(h2.index.levels[0]):
#if len(stock_list) != len(h1.index.get_level_values(0).unique()) or \
# len(stock_list) != len(h2.index.get_level_values(0).unique()):
#
# self.Debug('===================')
# self.Debug(len(stock_list))
# self.Debug("\nstock_list: %s "%([s.Value for s in stock_list]))
# self.Debug(len(h1.index.get_level_values(0).unique()))
# self.Debug("\nh1.index.levels[0]: %s "%([s for s in h1.index.get_level_values(0).unique()]))
# self.Debug(len(h2.index.get_level_values(0).unique()))
# self.Debug("\nh2.index.levels[0]: %s "%([s for s in h2.index.get_level_values(0).unique()]))
#for sid in h1.index.levels[0]:
'''
allStock = h1.index.get_level_values(0).unique()
maxZ = gap_z[-1,]/min_gap_z
maxZval = np.nanmax(maxZ)
if maxZval >= 0.75:
maxZpos = np.nanargmax(maxZ)
buyList.append(allStock[maxZpos])
'''
self.Debug(gap_z[-1])
for sid in h1.index.get_level_values(0).unique():
if gap_z[-1,i] / min_gap_z >= 0.999:
#if gap_z[-1,i] / min_gap_z >= 0.75:
self.Debug("->buy: %s :: %f"%(sid,gap_z[-1,i]))
buyList.append(sid)
elif gap_z[-1,i] / max_gap_z >= 0.95:
self.Debug("->sell: %s :: %f"%(sid,gap_z[-1,i]))
sellList.append(sid)
i += 1
#buyPower = (self.Portfolio.Cash * leverage) / (len(buyList) + len(sellList))
#self.Debug('VALUES !!!!!!!!!!!!!!!!!!!!!!!!!!')
#self.Debug(self.Portfolio.Cash)
#self.Debug(self.Portfolio.TotalPortfolioValue)
#self.Debug(self.mycash)
#buyPower = ((self.mycash * 1) / (len(buyList)))-100
percent = 1. / (len(buyList))
self.Plot("Overlay Plot", "Num Stocks Buy", len(buyList))
#buyPower = float(max(self.cash_per_trade,buyPower/5))
#buyPower = float(min(self.cash_per_trade,self.portfolio.cash/5))
self.Debug("\nBUYS: %s \nSELLS: %s"%([s for s in buyList],[s for s in sellList]))
self.Log('Cash: ' + str(self.Portfolio.Cash))
self.Log('TotalFees: ' + str(self.Portfolio.TotalFees))
self.Debug('Checking Holdings ...')
for kvp in self.Portfolio:
holdings = kvp.Value
symbol = holdings.Symbol
if (holdings.Invested):
self.Debug("Symbol: {} -> Price: {}".format(symbol, holdings.AveragePrice))
for sid in buyList:
#stock = self.AddEquity(sid.Value).Symbol
price = float(self.Securities[sid].Price)
if price == 0: continue
#self.Debug(self.StocksDailyData.head(50))
self.Debug(h1.loc[sid]['close'][0])
self.Debug(h1.loc[sid]['volume'][0])
self.Debug(h1.loc[sid]['close'][0]*h1.loc[sid]['volume'][0])
prevDayDV = h1.loc[sid]['close'][0]*h1.loc[sid]['volume'][0]
self.Debug('//////////////////////////////////////////////////////////')
self.Debug(prevDayDV)
#allocatedValue = float(min(prevDayDV/10, self.Portfolio.TotalPortfolioValue/(len(buyList))))
allocatedValue = float(self.Portfolio.TotalPortfolioValue/(len(buyList)))
shares = allocatedValue / price
#if allocatedValue < self.cash_per_trade:
# continue
#shares = (prevDayDV/10) / price
#shares = self.cash_per_trade/price
#self.Debug(self.Portfolio.GetBuyingPower(sid, OrderDirection.Buy))
#shares = self.CalculateOrderQuantity(sid, percent)
if shares > 0:
self.Buy(sid, shares)
#self.MarketOrder(sid, shares)
#self.LimitOrder(sid, shares, price)
#self.SetHoldings(sid.Value, shares)
self.Debug("Buy Stock : {} , # Shares {} , price {}".format(sid, shares, price))
#self.MarketOrder(sid, shares)
#self.SetHoldings(sid, percent)
#self.StopLimitOrder(sid, shares, decimal.Decimal(price * 0.90), decimal.Decimal(price * 0.90))
#self.StopMarketOrder(sid, shares, decimal.Decimal(price * 0.90));
#self.LimitOrder(sid.Value, shares)
#order(sid,shares,limit_price=price,stop_price=price)
'''
for sid in sellList:
#stock = self.AddEquity(sid.Value).Symbol
price = float(self.Securities[sid].Price)
if price == 0: continue
shares = buyPower/price
if shares > 0:
self.Debug("Stock : {} , # Shares {} , price {}".format(sid, shares, price))
#self.MarketOrder(sid, -shares)
#self.StopLimitOrder(sid, shares, decimal.Decimal(price * 1.10), decimal.Decimal(price * 1.10))
#self.StopMarketOrder(sid, shares, decimal.Decimal(price * 1.10));
#self.Debug("Stock : {} , # Shares {} , price {}".format(sid, shares, price))
#self.LimitOrder(sid.Value, shares)
#order(sid,-shares,limit_price=price,stop_price=price)
'''
'''
#for security in self.my_universe:
# self.Debug('#####################')
# self.SetHoldings(security, weight)
'''
def liquidate_portfolio(self):
self.Debug('Checking Holdings 2222222222222...')
for kvp in self.Portfolio:
holdings = kvp.Value
symbol = holdings.Symbol
if (holdings.Invested):
self.Debug("Symbol: {} -> Price: {}".format(symbol, holdings.AveragePrice))
for symbol in self.Portfolio.Keys:
self.SetHoldings(symbol, 0)
def cancel_orders(self):
self.Debug('=====================================================================')
openOrders = self.Transactions.GetOpenOrders()
if len(openOrders)> 0:
for x in openOrders:
self.Transactions.CancelOrder(x.Id)
################https://www.quantconnect.com/docs/algorithm-reference/overview
def returnNotMatches(a, b):
return [[x for x in a if x not in b], [x for x in b if x not in a]]
def to_dataframe(data_c, properties, labels):
# Helper function to make a dataframe from the coarse object.
# Then we can use all the handy dataframe methods.
# Set the 'coerce_float' parameter to get meaningful datatypes
# otherwise all the fields will be un-useful generic objects.
data = [[getattr(stock, name) for name in properties] for stock in data_c]
symbols = [stock.Symbol for stock in data_c ]
data_df = pd.DataFrame.from_records(
data,
index=symbols,
columns=labels,
coerce_float=True)
return data_df