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