Overall Statistics
Total Trades
391
Average Win
0.44%
Average Loss
-0.47%
Compounding Annual Return
12.483%
Drawdown
14.300%
Expectancy
0.235
Net Profit
16.954%
Sharpe Ratio
0.532
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
0.93
Alpha
0.431
Beta
-15.171
Annual Standard Deviation
0.267
Annual Variance
0.071
Information Ratio
0.461
Tracking Error
0.267
Treynor Ratio
-0.009
Total Fees
$1161.88
import pandas as pd 
import numpy as np 
import decimal as d
from datetime import datetime, timedelta, time
from order_codes import (OrderTypeCodes, OrderDirectionCodes, OrderStatusCodes)

class ModulatedResistanceContainmentField(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 5, 1) # Set End Date
        
        self.SetCash(10000)  # Set Strategy Cash
        # self.AddEquity("SPY", Resolution.Minute)
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        # resolution for the data added to the universe 
        self.UniverseSettings.Resolution = Resolution.Daily
        
        self.AddUniverse(self.MyCoarseFilterFunction,self.FineSelectionFunction)

        self.symbols = []
        self.stocks = []
        self.orders_list = []
        self.Age = {}
        self.stateData = {}
        self.stocks_worst =[]
        self.volume_filter = None
        self.pct_diff = None
        
        self.MaxCandidates=30
        self.MaxBuyOrdersAtOnce=15
        self.MyLeastPrice=1.15
        self.MyMostPrice=1.49
        self.MyFireSalePrice= self.MyLeastPrice
        self.MyFireSaleAge=3

        self.MyCandidate = []
        
        self.lossOrders = {} 
        self.profitOrders = {}
        self.portfolioOrders = {}
        
        self.start_date = self.Time
        
        self.LowVar = 6
        self.HighVar = 40
        
        # 
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen(self.spy, -45), Action(self.EveryDayBeforeMarketOpen))
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.spy, 1), self.myRebalance)
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday,DayOfWeek.Tuesday,DayOfWeek.Wednesday,DayOfWeek.Thursday, DayOfWeek.Friday), self.TimeRules.At(13, 0), self.myRebalance)
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 10), self.track_variables)
        #self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.spy, 1), self.cancel_open_orders)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose(self.spy, 1), self.cancel_open_buy_orders)
        
    def MyCoarseFilterFunction(self,coarce):
        
        '''The stocks must have fundamental data
        The stock must have positive previous-day close price and volume
        '''
        # The first filter is to select stocks withs fundamental data, and price between the MyLeastPrice and MyMostPrice values
        filtered = [x for x in coarce if (x.HasFundamentalData) and (x.Volume > 0) and (x.Price >= self.MyLeastPrice) and (x.Price <= self.MyMostPrice)]
        
        first_filter_symbol = [x.Symbol for x in filtered]
        
        #self.Debug('first filter %s' % len(first_filter_symbol))
        #self.Debug('%s length coarse fundamental filter %s' % (self.Time.date(),len(filtered)))
        
        # In this loop, for each symbol, we add the symbol to the self.stateData dictionary and update it with the values of EndTime,AdjustedPrice and DollarVolume
        # in order to have these values in the required past windows for each stock.
        for cf in filtered:
            if cf.Symbol not in self.stateData:
                self.stateData[cf.Symbol] = SelectionData(cf.Symbol)
                
            # Updates the SymbolData object with current EOD price
            avg = self.stateData[cf.Symbol]
            avg.update(cf.EndTime, cf.AdjustedPrice, cf.DollarVolume)
        
        # Get the stocks with the values of the three variables. With the is_ready method we take the stocks that have the 
        # historical data required.
        values = list(filter(lambda x: x.is_ready, self.stateData.values()))
        
        number_of_days = (self.Time.date() - self.start_date.date()).days
        
        #self.Debug('Number of days loading stocks %s' % number_of_days)
        
        volume_values = [x.mean_volume for x in values]
        
        #self.Debug('Number of stocks in values %s' % len(values))
        if volume_values:
            vol_small_percentile = np.percentile(volume_values, self.LowVar)
            vol_large_percentile = np.percentile(volume_values, self.HighVar)
            volume_filter = [x for x in values if (x.mean_volume > vol_small_percentile) and (x.mean_volume < vol_large_percentile)] 

        #    self.Debug('Number of stocks after volume filter %s' % volume_filter)
        
            #self.Debug('small percentile volume is %s' % vol_small_percentile)
            #self.Debug('large percentile volume is %s' % vol_large_percentile)   
            
            stocks_by_perc_diff = sorted(volume_filter, key=lambda x:x.percent_difference)
            percent_diff = [x.percent_difference for x in stocks_by_perc_diff]
        
        #    short_avg = [x.ShortAvg.Current.Value for x in values]
        #    long_avg = [x.LongAvg.Current.Value for x in values]
            #self.Debug('short sma %s at date %s' % (short_avg, self.Time))
            #self.Debug('long sma %s' % long_avg)
        
            #self.Debug('percent_diff %s' % percent_diff)
        
            symbols = [x.symbol for x in stocks_by_perc_diff]
            self.stocks = [x.Value for x in symbols]
            #self.Debug('on date %s' % self.Time)    
            #self.Debug('symbol list length %s' % len(symbols))
            #self.Debug(self.stocks_worst)
        
            #self.Debug(symbols)
            #self.Debug('length final symbols %s' % len(symbols))
       
        if self.stocks:
            return [x.symbol for x in stocks_by_perc_diff]
        
        else:
            return [x.symbol for x in values[0:30]]
            
    def FineSelectionFunction(self,fine):
        ''' 
        This function takes the stock of the CoarceFundamental function and narrow the list adding specific fundamental filters
        '''
        #self.Debug('Lenght of Universe in fine at first %s' % len(fine))
        fine_filter = [x.Symbol for x in fine if x.SecurityReference.IsPrimaryShare == 1 and x.SecurityReference.SecurityType == 'ST00000001' and x.CompanyReference.IsLimitedPartnership == 0 and 
        x.SecurityReference.IsDepositaryReceipt == 0 ]
        
        # self.stocks_worst store the companies that meet all criteria
        self.symbols = [x for x in fine_filter]
        self.stocks_worst = [x for x in fine_filter[0:self.MaxCandidates]]
    #   
        #self.Debug('%s length fine fundamental filter %s' % (self.Time.date(),len(self.symbols)))
        values = [x.Value for x in self.symbols]
        #self.Debug('Length of Universe in fine at final %s' % len(self.stocks_worst))
        return self.stocks_worst 
     
    def EveryDayBeforeMarketOpen(self):
        '''
        This function runs at 8:45 of each day and look for stocks that are in the Portfolio. Then track the number of 
        days the stock is in the portfolio with the self.Age dictionary and update this value
        '''
        if not self.stocks_worst: return
    
        lowest_price = self.MyLeastPrice #reset beginning of day
        securities_invested =  [x.Key for x in self.Portfolio if x.Value.Invested]
        
        #self.Debug('securities invested')
        #self.Debug(securities_invested)

        symbols_invested = [x.Symbol.Value for x in self.Portfolio.Values if x.Invested ]
        for stock in securities_invested:
            if stock == 'SPY': continue
            #self.Debug('stock invested %s' % stock)
            CurrPrice = self.Securities[stock].Price
            #self.Debug(CurrPrice)
            
            if CurrPrice < lowest_price:
                lowest_price = CurrPrice
            if stock.Value in self.Age.keys():
                self.Age[stock.Value] += 1
            else:
                self.Age[stock.Value] = 1
        
        for stock in self.Age.keys():
            if stock not in symbols_invested:
                self.Age[stock] = 0
            message = 'stock.symbol: {symbol}  :  age: {age}'
            #self.Log(message.format(symbol=stock, age=self.Age[stock]))
    
      # this event fires whenever we have changes to our universe
  
    def OnSecuritiesChanged(self, changes):
        '''
        This function is a built in function of QC, who trigger events when stocks are added or removed from the 
        universe. In this function is possible to get the stock that are added to the universe using the AddedSecurities
        method and on the other hand is possible to know the stocks that were removed from the universe with the RemovedSecurities
        method 
        '''
        
 #       # If the self.stocks_worst is not ready, this function does't run as only run with the stocks that were added to the 
 #       # custom universe
        if not self.stocks_worst: 
            return
       # else:
    #        self.Debug(self.stocks_worst)
        #self.Debug('Active Securities')
        #self.cancel_open_buy_orders()
        
        self.orders_stocks = []
        BuyFactor = 0.97
        WeightThisBuyOrder=float(1.00/self.MaxBuyOrdersAtOnce)
        cash= self.Portfolio.Cash
        #self.ActiveSecurities.Keys
        for security in changes.AddedSecurities:
            #self.Debug('Open orders for stock %s' % symbol.Value)
            #self.Debug('Security added to Universe %s at date %s' % (security.Symbol.Value, self.Time))
            #self.Debug(self.Transactions.GetOpenOrders(symbol))
            open_orders = self.Transactions.GetOpenOrders(security.Symbol)
        
            # Add another time filter price because splits and dividends events could affect the price 
            #and not open_orders 
            if not self.Portfolio[security.Symbol].Invested and security.Symbol.Value != 'SPY' and  (self.MyLeastPrice <= self.Securities[security.Symbol].Price <= self.MyMostPrice):# and self.Securities.ContainsKey(security.Symbol): #not open_orders and:
                #self.Debug('Active Security in universe %s' % symbol.Value)
                    PH = self.History(security.Symbol, 20, Resolution.Daily)
                    if str(security.Symbol) not in PH.index: return
                    close_history = PH.loc[str(security.Symbol)]['close']
                    PH_Avg = float(close_history.mean())
                    #self.Debug('mean price %s' % PH_Avg)
                    CurrPrice = round(self.Securities[security.Symbol].Price,2)
                    #self.Debug('Curr Price and PH_Avg %s %s of stock %s' % (CurrPrice,PH_Avg,security.Symbol.Value))
                
                    if np.isnan(CurrPrice) or CurrPrice == d.Decimal(0):
                        pass # probably best to wait until nan goes away
                
                    else:
                        if CurrPrice > float(1.25 * PH_Avg):
                            BuyPrice= round(CurrPrice,3)
                        #   self.Debug('price %s symbol %s' % (BuyPrice,symbol.Value))
                        else:
                            BuyPrice= round(CurrPrice*BuyFactor,3)
                        #    self.Debug('price %s symbol %s' % (BuyPrice,symbol.Value))
                    
                    StockShares = int(WeightThisBuyOrder*cash/BuyPrice)
                    self.LimitOrder(security.Symbol, StockShares,BuyPrice, 'Purchase')
                    #self.Debug('Submit limit order for stock %s with price %s at time %s' % (symbol.Value, BuyPrice,self.Time))
                #self.SetHoldings(security.Symbol, 0.1)
    
    def myRebalance(self):
        '''
        This function would sell stocks that are invested and meet conditions to sell. The function would run two times each day,
        once ate 9:30 am and second at 13:00 pm
        '''
        #if not self.stocks_worst: 
        #    return

        stocks_worst = [x.Value for x in self.stocks_worst]
        
        #self.Debug('length of stocks_worst %s' % len(stocks_worst))
        
        #self.Debug(stocks_worst)
        BuyFactor = 0.97
        SellFactor=1.03 
    
        #self.Debug(self.Time)
        cash= self.Portfolio.Cash
        # Cancel all Open Orders
        
        #self.cancel_open_buy_orders()
        
        securities_invested =  [x.Key for x in self.Portfolio if x.Value.Invested]
#       # Order sell at profit target in hope that somebody actually buys it
        for stock in securities_invested:
            sym = str(stock.Value)
            #self.Debug(' stock %s containskey %s' % (stock, self.Securities.ContainsKey(stock)))
            is_in_age = stock.Value in self.Age.keys()
            open_orders = self.Transactions.GetOpenOrders(stock)
            #self.Debug('open order for stock %s are %s' % (stock.Value, len(open_orders)))
            #self.Debug(' stock % age %s ' % (stock.Value ,age))
            # self.Debug('stock %s is in age %s' % (stock.Value, is_in_age))
            
            #self.Debug('%s Open orders %s' % (stock.Value ,self.Transactions.GetOpenOrders(stock)))
            StockShares = self.Portfolio[stock].Quantity
            CurrPrice = round(self.Securities[stock].Price,3)
            CostBasis = round(self.Portfolio[stock].AveragePrice,3)
            SellPrice = round(CostBasis*SellFactor,2)
            profits = round(self.Portfolio[stock].UnrealizedProfit,2)
            
            if len(open_orders) == 0 :
                if np.isnan(SellPrice):
                    pass # probably best to wait until nan goes away
                
                if self.Securities.ContainsKey(stock):
                    profit_order = self.LimitOrder(stock, -StockShares, SellPrice,'Sell with profit')
                    self.profitOrders[sym] = profit_order
                    self.Debug('send a limit order to sell stock %s for profit at price %s' % (stock.Value, SellPrice))
                
            #    if np.isnan(SellPrice):
            #        pass # probably best to wait until nan goes away
        
            if sym in self.profitOrders.keys():
                orderData = self.profitOrders[sym]
                profit_order_id = orderData.OrderId
                order = self.Transactions.GetOrderTicket(profit_order_id)
                
                if (OrderStatusCodes[order.Status]=='Filled'):
                    return
                
                if  (OrderStatusCodes[order.Status]=='Submitted') and (sym not in self.lossOrders.keys()):
                    
                    if is_in_age and self.MyFireSaleAge < self.Age[stock.Value] and  ((self.MyFireSalePrice > CurrPrice or CostBasis>CurrPrice)): #and profits < -100 :
                        # self.Debug('Condition to sell at loss is met for stock age %s %s' % (stock.Value, self.Age[stock.Value]))
                        self.Debug('%s %s %s %s' % (stock.Value, self.Age[stock.Value], CurrPrice,CostBasis))
                        self.Debug('limit order to sell with loss stock %s' % stock.Value) 
                        #self.Liquidate(stock)
                        SellPrice = round(.95*CurrPrice,2)
                        loss_order = self.LimitOrder(stock, -StockShares, SellPrice,'Sell with loss')
                        self.lossOrders[sym] = loss_order
                        #self.loss_orders[stock.Value] = loss_order_ticket
                        #self.Debug('send a limit order to sell to cut loss for stock %s at price %s' % (stock, SellPrice))
    
               # else:
            #        self.LimitOrder(stock, -StockShares, SellPrice,'Sell with profit')
                    #self.Debug('send a limit order to sell to for profit for stock %s at price %s' % (stock, SellPrice))
           

    def track_variables(self):
        SellFactor=1.03
        if not self.stocks_worst: return
        securities_invested =  [x.Key for x in self.Portfolio if x.Value.Invested]
        for stock in securities_invested:
            underlying = self.Securities[stock].Price
            StockShares= self.Portfolio[stock].Quantity
            lastPrice = self.Securities[stock].Price
            profits = round(self.Portfolio[stock].UnrealizedProfit,0)
            profit_percentage = self.Portfolio[stock].UnrealizedProfitPercent
            CurrPrice = round(self.Securities[stock].Price,3)
            CostBasis = round(self.Portfolio[stock].AveragePrice,3)
            SellPrice = round(CostBasis*SellFactor,2)
            age = self.Age[stock.Value] 
            ##if  age > 10:
             #   self.Debug('Profits for stock %s are %s with age %s' % (stock.Value,profits,age))
   
         # Uncomment these lines to track individual stocks         
         #   self.Debug("Stock %s: Quantity %s: Last Price %s" % (stock.Value, quantity, lastPrice))
         #   self.Debug('Unrealized profits and profit percentage at date %s for stock %s are  %s  and  %s' % (self.Time.date(), stock.Value, profits,"{0:.0%}".format(profit_percentage)))
         #   self.Debug('-------------------------------------------------------------------------------')
        
        openOrders = self.Transactions.GetOpenOrders()          
        profits = self.Portfolio.TotalProfit          
        unrealizedProfits = self.Portfolio.TotalUnrealizedProfit
        positions = len(securities_invested)
        if 0<len(self.Age):
            MaxAge=self.Age[max(self.Age.keys(), key=(lambda k: self.Age[k]))]
            #self.Debug(self.Age)
            #self.Debug('Max Age is %s' % MaxAge)
        
        leverage = self.Portfolio.TotalMarginUsed
        account_leverage = self.Portfolio.TotalAbsoluteHoldingsCost / self.Portfolio.TotalPortfolioValue
        #self.Debug('Total Profits %s , Total UnrealizedProfits %s , number of positions %s number openOrders %s at time %s' % (profits, unrealizedProfits, positions, len(openOrders), self.Time)) 
        
    def log_open_orders():
        oo = self.Transactions.GetOpenOrders()
        if len(oo) == 0:
            return
        for stock, orders in oo.iteritems():
            for order in orders:
                message = 'Found open order for {amount} shares in {stock}'
                self.Log(message.format(amount=order.amount, stock=stock))

    def cancel_open_buy_orders(self):
        oo = self.Transactions.GetOpenOrders()
        if len(oo) == 0:
            return
       
        # Cancel open orders to buy stocks that were open for more than 5 days
        for order in oo: #stock, orders in oo.iteritems():
            order_time = order.Time.date()
            if order.Quantity > 0 and (self.Time.date() - order_time).days > 5:# and not self.Securities[order.Symbol].Invested and (self.Time.date() - order_time).days > 10:
                self.Transactions.CancelOrder(order.Id) #self.Transactions.CancelOpenOrders(order.Symbol)
            #for order in orders:
                message = 'Canceling buy order after 5 days with {amount} shares for {stock}'
                self.Log(message.format(amount=order.Quantity, stock=order.Symbol.Value))
            #    if 0 < order.amount: #it is a buy order
            #        self.Transactions.CancelOpenOrders(stock)
    
    def cancel_open_orders(self):
        oo = self.Transactions.GetOpenOrders()
        securities_invested =  [x.Key for x in self.Portfolio if x.Value.Invested]
    
        if len(oo) == 0:
            return
        
        #if not securities_invested: return 
        for order in oo:
            order_time = order.Time.date() #order.Quantity < 0 and 
            if (self.Time.date() - order_time).days > 10:
                self.Transactions.CancelOrder(order.Id)
            
            message = 'Canceling order of {amount} shares in {stock}'
            self.Log(message.format(amount=order.Quantity, stock=order.Symbol))
            #self.Transactions.CancelOpenOrders(order.Symbol)

            #if self.Securities[order.Symbol].Invested:
                # This line test if the stock is in Age dict that is the same that the stock is in portfolio
            #    self.Debug(order.Symbol)
            #    self.Debug(order.Symbol.Value)
            #    self.Debug(self.Age)
            #    self.Debug('Test if the order %s meet conditions to be cancelled at the end of day' % order)
                #self.Debug('Order Symbol age order Status and order Amount %s %s %s %s
                #Self.' (order.Symbol.Value , self.Age[order.Symbol.Value], order.Status, order.Quantity))
            #    if order.Symbol.Value in self.Age  and (self.Time.date() - order_time).days >5:
                    #self.Debug(order)
                
                #self.Debug('Cancel Open Order to sell stock %s in portfolio as the order is not filled on current day' % order.Symbol.Value)
                #    self.Debug('Cancel open Order to sell %s for stock %s status %s' % (order.Quantity, order.Symbol.Value,order.Status))
                    
            
            #else:
            #    self.Debug('stock is not in Portfolio yet %s as the order is not filled %s ' % (order.Symbol.Value, order.Status))
           
    def reset_parameters():
        self.orders_list = []
        
    def OnOrderEvent(self, orderEvent):
        ''' Event when the order is filled. Debug log the order fill. :OrderEvent:'''

    
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        
        self.Log(str(orderEvent))  
        
        if OrderStatusCodes[orderEvent.Status] == 'Submitted:#' or OrderStatusCodes[orderEvent.Status] == 'CancelPending':
            return 
            
        
        k = str(orderEvent.Symbol.Value)
        symbol = str(orderEvent.Symbol)
        
        
        
        if k in self.profitOrders.keys():
        
            orderData = self.profitOrders[k]
            profit_order_id = orderData.OrderId
            order = self.Transactions.GetOrderTicket(profit_order_id)
            
            # sometimes order is nonetype due to security price
            # is equal to zero
            #------------------------------------------------------#
            if not order: 
                self.Log('order is nonetype: {}'.format(k))
                del self.profitOrders[k] # delete order ticket data  
                return
            
            if (OrderStatusCodes[order.Status]=='Filled') and (k in self.lossOrders.keys()):
                loss_order = self.lossOrders[k]
                loss_order_id = loss_order.OrderId
                if loss_order.Status == OrderStatus.Submitted:
                    self.Log('cancelling loss open limitOrder for: {} {} with id {}'.format(self.Time, k,loss_order_id))
                    #self.Transactions.CancelOrder(loss_order_id)
                    loss_order.Cancel()
                    #self.Transactions.CancelOpenOrders(symbol)
                    del self.lossOrders[k]
                    del self.profitOrders[k]
                
        if k in self.lossOrders.keys():
            
            orderData = self.lossOrders[k]
            loss_order_id = orderData.OrderId
            order = self.Transactions.GetOrderTicket(loss_order_id)
            
            # sometimes order is nonetype due to security price
            # is equal to zero
            #------------------------------------------------------#
            if not order: 
                self.Log('order is nonetype: {}'.format(k))
                del self.lossOrders[k] # delete order ticket data  
                return
            
            if (OrderStatusCodes[order.Status]=='Filled') and (k in self.profitOrders.keys()):
                profit_order = self.profitOrders[k]
                profit_order_id = profit_order.OrderId
                if profit_order.Status == OrderStatus.Submitted:
                    self.Log('cancelling profit open limitOrder for: {} {} with id'.format(self.Time, k, profit_order_id))
                    self.Transactions.CancelOpenOrders(symbol)
                    del self.lossOrders[k]
                    del self.profitOrders[k]
            
        
        #if order.Status == OrderStatus.Filled:
        #    if self.Securities[order.Symbol.Value].Invested and self.Securities.ContainsKey(order.Symbol):
        #        self.Debug('llega hasta aca')
        #        if len(self.Transactions.GetOpenOrders(order.Symbol)) > 0:
        #            oo = self.Transactions.GetOpenOrders(order.Symbol)
        #            for order in oo:
        #                order.Cancel()
        #                del self.loss_orders[order.Symbol.Value] 
                    
        #self.Debug("%s: %s: %s: %s: %s: %s" % (self.Time, order.Symbol,order.Type, order.Price, order.Quantity,order.Status))
        

class SelectionData(object):
    
    def __init__(self, symbol):
        self.symbol = symbol
        self.ShortAvg = SimpleMovingAverage(3)
        self.LongAvg = SimpleMovingAverage(45)
        self.is_ready = False
        self.percent_difference = 0
        self.volume = SimpleMovingAverage(20)
        self.mean_volume = 0
        
    def update(self, time, price,volume):
        ### In "A and B and C" statement, when A is False, B and C will not be executed.
        ### Your strategy wants to update all the three SMA and then check if all of them are ready,
        ### So, the Update() methods should not be in the "and" statement condition.
        self.LongAvg.Update(time,price)
        self.ShortAvg.Update(time,price)
        self.volume.Update(time,volume)
        if self.LongAvg.IsReady and self.ShortAvg.IsReady and self.volume.IsReady:
        
        # if self.LongAvg.Update(time,price) and self.ShortAvg.Update(time,price) and self.volume.Update(time,volume):
            shortAvg = self.ShortAvg.Current.Value
            longAvg = self.LongAvg.Current.Value
            
            self.mean_volume = self.volume.Current.Value
            
            self.percent_difference = (shortAvg - longAvg) / longAvg       
            self.is_ready = True
            #self.Debug('se actualizaron los indicadores')
            #self.is_ready = True
            #shortAvg = self.ShortAvg.Current.Value
            
            #longAvg = self.LongAvg.Current.Value
            #self.percent_difference = (shortAvg - longAvg) / longAvg                        
"""
This file contains QuantConnect order codes for easy conversion and more 
intuitive custom order handling

References:
    https://github.com/QuantConnect/Lean/blob/master/Common/Orders/OrderTypes.cs
    https://github.com/QuantConnect/Lean/blob/master/Common/Orders/OrderRequestStatus.cs
"""

OrderTypeKeys = [
    'Market', 'Limit', 'StopMarket', 'StopLimit', 'MarketOnOpen',
    'MarketOnClose', 'OptionExercise',
]

OrderTypeCodes = dict(zip(range(len(OrderTypeKeys)), OrderTypeKeys))

OrderDirectionKeys = ['Buy', 'Sell', 'Hold']
OrderDirectionCodes = dict(zip(range(len(OrderDirectionKeys)), OrderDirectionKeys))

## NOTE ORDERSTATUS IS NOT IN SIMPLE NUMERICAL ORDER

OrderStatusCodes = {
    0:'New', # new order pre-submission to the order processor
    1:'Submitted', # order submitted to the market
    2:'PartiallyFilled', # partially filled, in market order
    3:'Filled', # completed, filled, in market order
    5:'Canceled', # order cancelled before filled
    6:'None', # no order state yet
    7:'Invalid', # order invalidated before it hit the market (e.g. insufficient capital)
    8:'CancelPending', # order waiting for confirmation of cancellation
}