| Overall Statistics |
|
Total Trades 412 Average Win 0.17% Average Loss -0.08% Compounding Annual Return 805.120% Drawdown 16.300% Expectancy 1.859 Net Profit 22.307% Sharpe Ratio 5.449 Loss Rate 7% Win Rate 93% Profit-Loss Ratio 2.07 Alpha 3.329 Beta -86.865 Annual Standard Deviation 0.344 Annual Variance 0.118 Information Ratio 5.399 Tracking Error 0.344 Treynor Ratio -0.022 Total Fees $5024.07 |
import numpy as np
import pandas as pd
# from QuantConnect.Data.UniverseSelection import *
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 System.Collections.Generic import List
from QuantConnect.Data.UniverseSelection import *
from decimal import Decimal
from datetime import datetime, timedelta
import numpy as np
import math
from itertools import *
class JcbFinalSellQuick(QCAlgorithm):
def Initialize(self):
#User Defined Variables
self.MyLeastPrice=0.40
self.MyMostPrice=1.40
self.shrtPeriod = 3
self.lngPeriod = 45
self.MaxCandidates = 30
self.MaxBuyOrdersAtOnce = 10
self.MyFireSalePrice=self.MyLeastPrice
self.MyFireSaleAge=3
self.stock_worst = []
# self.MyCandidate =
# symbolDataDict = {}
self.SetStartDate(2018,1,1) #Set Start Date
self.SetEndDate(2018,1,31) #Set End Date
self.SetCash(100000) #Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Daily
# self.UniverseSettings.Leverage = 2
self.averages = { };
self.averagesCoarse = { };
self.age = { };
self.AddUniverse(self.courseSelection, self.fineSelection)
self.AddEquity("SPY")
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY",1),
Action(self.beforeTradingStart))
EveryThisManyMinutes=105
TradingDayHours=6.5
TradingDayMinutes=int(TradingDayHours*60)
for minutez in range(
10,
TradingDayMinutes,
EveryThisManyMinutes
):
# self.Log(minutez)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY",minutez),
Action(self.rebalance))
self.SetWarmUp(self.lngPeriod)
def beforeTradingStart(self):
if self.IsWarmingUp:
return
self.Log("In beforeTradingStart")
self.LowestPrice=self.MyLeastPrice
# self.Log("stock_worst: {}".format([x.Value for x in self.Portfolio.Keys]))
# self.MyCandidate = cycle(self.stock_worst)
# self.Log("Stocks in Portfolio {}".format(len(self.Portfolio.Keys)))
# positions = [x.Symbol for x in self.Portfolio if x.Invested]
# positions = {k:v for k,v in self.Portfolio.Securities.iteritems() if v.Invested}.Keys
positions = []
self.Log("Stocks in before trading {}".format(len(self.Portfolio.Keys)))
for stock in self.Portfolio.Keys:
# self.Securities[stock].SetSlippageModel(CustomSlippageModel())
if self.Portfolio[stock].Invested:
# for stock in positions:
positions.append(stock)
self.CurrPrice = float(self.Securities[stock].Price)
# self.Log("CurrPrice "+str(self.CurrPrice))
if self.CurrPrice < self.LowestPrice:
self.LowestPrice = self.CurrPrice
# Old Stock => add age by 1
if stock in self.age:
self.age[stock] += 1
else:
self.age[stock] = 1
for stock in self.age:
if stock not in positions:
self.age[stock] = 0
self.Log("completed beforeTradingStart")
# self.Log("LowestPrice: "+str(self.LowestPrice))
# if len(self.age)>0:
# for y in self.age:
# self.Log(str(y)+" :Age = "+str(self.age[y]))
def rebalance(self):
if self.IsWarmingUp:
return
self.Log("In rebalance")
BuyFactor=.99
SellFactor=1.01
cash=self.Portfolio.Cash
self.Log('Cash :{}'.format(str(self.Portfolio.Cash)))
self.cancel_open_buy_orders()
# positions = [x.Symbol for x in self.Portfolio if x.Invested]
# positions = {k:v for k,v in self.Portfolio.iteritems() if v.Invested}.Keys
# for stock in positions:
for stock in self.Portfolio.Keys:
if self.Portfolio[stock].Invested and str(stock) != 'SPY':
if not self.Transactions.GetOpenOrders(stock):
StockShares =round(self.Portfolio[stock].Quantity, 2)
CurrPrice = float(self.Securities[stock].Price)
CostBasis = float(self.Portfolio[stock].AveragePrice)
s=float(CostBasis)*float(SellFactor)
SellPrice = float(self.make_div_by_05(s, buy=False))
if np.isnan(SellPrice):
pass # probably best to wait until nan goes away
elif (stock in self.age and self.MyFireSaleAge<=self.age[stock]
and (self.MyFireSalePrice>CurrPrice or CostBasis>CurrPrice)):
# TODO: Sell at market price here.
marketOrder=self.MarketOrder(stock,-StockShares)
# SellPrice = float(self.make_div_by_05(.95* CurrPrice, buy=False))
# # self.Log("Sell LimitOrder {},{},{}".format(stock, -StockShares, SellPrice))
# limitOrderTicket = self.LimitOrder(stock, -StockShares, SellPrice)
else:
# self.Log("Sell LimitOrder {},{},{}".format(stock, -StockShares, SellPrice))
self.LimitOrder(stock, -StockShares, SellPrice)
# self.Log(self.Portfolio.Keys)
# if self.stock_worst is not None:
# self.Log(self.stock_worst)
# self.Log(self.Portfolio.Keys)
if True : #len(self.stock_worst) > 0:
WeightThisBuyOrder=float(1.00/self.MaxBuyOrdersAtOnce)
# self.Log('WeightThisBuyOrder: {}'.format(str(WeightThisBuyOrder)))
all_symbols = [ x.Value for x in self.Portfolio.Keys ]
self.Log("stocks in rebalnce {}".format(all_symbols))
self.MyCandidate = cycle(self.Portfolio.Keys)
for ThisBuyOrder in range(self.MaxBuyOrdersAtOnce):
# self.Log("blah")
# self.Log("ThisBuyOrder "+str(ThisBuyOrder))
stock = next(self.MyCandidate)
if str(stock) == 'SPY':
# self.Log('skipping SPY')
continue
# self.Log("self.stocks "+str(self.stocks))
PH=self.History(stock, 20, Resolution.Daily)
# self.Log(PH.columns)
# PH_Float = PH.astype(float)
if 'close' not in PH.columns:
continue
PH_Avg = float(PH['close'].mean())
CurrPrice = float(self.Securities[stock].Price)
# self.Log("PH_Avg: {}, CurrPrice: {}".format(PH_Avg, CurrPrice))
if np.isnan(CurrPrice): # safety check
pass # probably best to wait until nan goes away
else:
if CurrPrice > float(1.25* PH_Avg): # if current price is higher than the 20 day avg * 1.25 => Stock is on UpTrend
BuyPrice=float(CurrPrice)
else:
# Hoping to buy low (99 % of current price)
BuyPrice=float(CurrPrice*BuyFactor)
BuyPrice=float(self.make_div_by_05(BuyPrice, buy=True))
if BuyPrice != 0:
StockShares = int(WeightThisBuyOrder*float(cash)/BuyPrice)
# self.Log("BuyPrice: {}, StockShares: {}".format(BuyPrice, StockShares))
# self.Log('Order Quantity: {}'.format(str(StockShares)))
self.LimitOrder(stock, StockShares, BuyPrice)
self.Log("completed rebalance")
# if len(self.Portfolio.Keys) > 1:
# self.MyCandidate = cycle(self.Portfolio.Keys)
# self.Log("Next : {}".format(next(self.MyCandidate)))
def cancel_open_buy_orders(self):
# self.Log("In cancel_open_buy_orders")
orders = self.Transactions.GetOpenOrders()
# self.Log("GetOpenOrders "+str(self.oo))
if len(orders) == 0:
return
# for stock, orders in oo.iteritems():
for order in orders:
#message = 'Canceling order of {amount} shares in {stock}'
#log.info(message.format(amount=order.amount, stock=stock))
if 0 < order.Quantity: #it is a buy order
# cancel_order(order)
self.Transactions.CancelOrder(order.Id)
def make_div_by_05(self,s, buy=False):
# if s == 0:
# return s
s *= 20.00
s = math.floor(s) if buy else math.ceil(s)
s /= 20.00
return s
def cancel_open_orders(self):
orders = self.Transactions.GetOpenOrders()
if len(orders) == 0:
return
# for stock, orders in self.oo.iteritems():
for order in orders:
#message = 'Canceling order of {amount} shares in {stock}'
#log.info(message.format(amount=order.amount, stock=stock))
# cancel_order(order)
self.Transactions.CancelOrder(order.Id)
def courseSelection(self, data):
self.Log("In coarseSelection")
columns = ['Price', 'DollarVolume', 'HasFundamentalData', 'Volume', 'EndTime','Market']
column_names = ['price', 'dollar_volume', 'has_fundamentals', 'volume', 'EndTime', 'Market']
data_df = self.to_dataframe(data, columns, column_names)
data_df.fillna(0)
market = "usa"
# dv6 = data_df['dollar_volume'].quantile(0.06)
# dv40 = data_df['dollar_volume'].quantile(0.4)
# and (dollar_volume > @dv6 and dollar_volume < @dv40)
# nullColumns = data_df.columns[data_df.isna().any()].tolist()
# if len(nullColumns) > 0:
# self.Log(nullColumns)
# subList = ['price','has_fundamentals','Market']
# if not all(elem in subList for elem in data_df.columns):
# return []
# if 'price' in nullColumns or 'has_fundamentals' in nullColumns or 'Market' in nullColumns:
# return []
# medianPrice = data_df['price'].median()
# data_df = data_df.dropna(subset=['price','has_fundamentals','Market'])
try:
my_universe = (data_df.
query("(price > @self.MyLeastPrice and price < @self.MyMostPrice) and has_fundamentals and Market == @market"))
except NameError as e:
# self.Log(e.message)
self.Log("completed courseSelection after my_universe query(try method)")
return []
# shorten the filter to run the algorithm faster (Delete later)
self.Log("coarse_symbols size is: {}".format(len(my_universe.index.tolist())))
self.Log("completed courseSelection")
# self.Log(my_universe.index.tolist())
return my_universe.index.tolist()[:self.MaxCandidates]
for index, row in my_universe.iterrows():
if index not in self.averagesCoarse:
self.averagesCoarse[index] = SymbolDataCoarse(index)
avg = self.averagesCoarse[index]
avg.update(row['EndTime'], row['dollar_volume'])
values = list(self.averagesCoarse.values())
# nonzeros = len([x for x in values if x.avgdv20 > 0])
# self.Log(nonzeros)
avgdv20lst = [float(x.avgdv20) for x in values]
# if nonzeros > 0:
# self.Log(avgdv20lst)
dv6 = np.percentile(avgdv20lst, 6)
dv40 = np.percentile(avgdv20lst, 40)
# self.Log(dv6)
# self.Log(dv40)
my_univ_dvfiltered = (my_universe.query("dollar_volume > @dv6 and dollar_volume < @dv40"))
# See how many securities are found in our universe
# self.Log("coarse sel size is: {}".format(len(my_univ_dvfiltered.index.tolist())))
# self.Log(my_univ_dvfiltered.index.tolist())
return my_univ_dvfiltered.index.tolist()
def fineSelection(self, ff):
self.Log("In fineSelection")
my_fine_symbols = [x for x in ff if x.SecurityReference.SecurityType == 'ST00000001'
and x.SecurityReference.IsPrimaryShare
and not x.SecurityReference.IsDepositaryReceipt
and not x.SecurityReference.ExchangeId.startswith('OTC')
and not x.SecurityReference.SecuritySymbol.endswith('.WI')
# and not x.CompanyReference.StandardName.matches('.* L[. ]?P.?$')
and not x.CompanyReference.IsLimitedPartnership]
# self.Log("fine symbols size is: {}".format(len(my_fine_symbols)))
# shorten the filter to run the algorithm faster (Delete later)
# return [x.Symbol for x in my_fine_symbols]
for cf in my_fine_symbols:
if cf.Symbol not in self.averages:
self.averages[cf.Symbol] = SymbolData(cf.Symbol)
avg = self.averages[cf.Symbol]
avg.update(cf.EndTime, cf.Price)
values = list(self.averages.values())
# self.Log(len([x for x in values if x.pct_diff > 0]))
values.sort(key=lambda x: x.pct_diff, reverse=True)
# for x in values[:self.MaxCandidates]:
# self.Log('symbol: ' + str(x.symbol.Value) + ' pct_diff: ' + str(x.pct_diff))
self.stocks_worst = [ x.symbol for x in values[:self.MaxCandidates] ]
self.Log("stock_worst size is: {}".format(len( self.stocks_worst)))
self.Log("completed fineSelection")
return self.stocks_worst
def OnData(self, data):
# foreach ( var bar in slice.Bars)
# SetHoldings(bar.Value.Symbol, _positionweight);
# for bar in data.Bars:
# self.Securities[bar.Value.Symbol].SetSlippageModel(CustomSlippageModel())
pass
def to_dataframe(self, data_c, properties, labels):
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
class SymbolDataCoarse(object):
def __init__(self, symbol):
self.symbol = symbol
self.avgdv20Ind = SimpleMovingAverage(20)
self.avgdv20 = 0
def update(self, time, value):
if self.avgdv20Ind.Update(time, value) :
self.avgdv20 = self.avgdv20Ind.Current.Value
class SymbolData(object):
def __init__(self, symbol):
self.symbol = symbol
self.tolerance = Decimal(1.01)
self.shrt = SimpleMovingAverage(3) #fast
self.lng = SimpleMovingAverage(45) #slow
self.pct_diff = 0
def update(self, time, value):
if self.shrt.Update(time, value) and self.lng.Update(time, value):
shrt = self.shrt.Current.Value
lng = self.lng.Current.Value
self.pct_diff = (shrt - lng)/lng
# class CustomSlippageModel:
# def GetSlippageApproximation(self, asset, order):
# return 0.1