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