Overall Statistics
""" This is a docstring!

DONE In the algo there is initialize the data

DONE There is consolidate the minute data into daily bars (to calculate the pivots) and, when done,

DONE there is call the pivot function to return a dictionary of pivots for the day, 5 on each side + one central

DONE Store the data, and the prior day's pivot bars

DONE Begin trading logic AFTER warm-up period complete


TRADE LOGIC:
    Objective: For this implemtation I'm selling at lvl 3 with a stop at lvl 4. TP'ing at opposite for this simple algorithm.
    (unlikely to be profitable?)

    -Positions will be held overnight until they are either stopped out or limit ordered.
    -If stopped out during day, no more trading for the day
    -Orders will be replaced each day unless position is still current, at which point, however, opposite stop order has been canceled.

When minute bars start flowing in, pivots will have been calculated. Now we place limit and stop orders according to the pivots if there isn't a current position:

DONE, for now trade fixed number of shares
--Need to have a proper risk management concept: want the algo to trade 1% of account value spread based on the pivot points. However, it's possible that....why not just have it trade a fixed number of shares for the first go?
Don't want to overleverage and get errors as a result. Not sure what that does to the algorithm

DONE 1)
if not self.traded and not self.Portfolio.Invested: #if we haven't traded today and don't have a position from the previous day (or anymore)
-cancel all potentially open orders using liquidate #this allows prior trade to complete and still place new trades for the day
-place limit orders on H3 and L3 pivots, rounding to nearest cent with GTC expiry
-self.tradePivots == self.currentPivots
-set self.traded == True

2) SKIP for now because does nothing 
elif self.Portfolio.Invested: #we've placed orders for the day and are currently in position
-do nothing. All orders already in place.
return

3) SKIP for now because does nothing
elif [there exist 4 open orders]: #in this case we're waiting for a trade
return

INSTEAD 4) cancelling other side for the day
--to check and see if we've filled one set of order we can determine how many open stopmarketorders there are. If there are none means we've lost and should liquidate.
else: #in this case we've lost.'
liquidate

^INSTEAD 5) if less than 2 open stop market orders: means we've had a losing trade. Cancel the other limit and stop order.

ORDER FILL EVENT HANDLER:

SKIP due to functionality being implemented on a not-as-ideal basis by #5 in the above
If one limit order is filled, immediately cancel the stop order on the opposite limit so the opposite side can be used as a take profit.




To make more resilient in the future:
-Implement OCO orders via interactive brokers, natively
-Check to ensure entire order is filled. If not and has passed center, cancel remaining order and update all other orders appropriately.
-


Algo weaknesses:
-In trending market should keep getting stopped out over and over again.
--Use pivot point width to compensate or a momentum indicator such as MACD histogram...slope of moving average.
-Unsure if liquidating on the opposite leve is reasonable

## Resources!

Order ticket demo algorithm:
https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/OrderTicketDemoAlgorithm.py

Plotting indicators on price charts (2015):
https://www.quantconnect.com/forum/discussion/859/how-to-plot-indicator-on-price-chart/p1

Order sample Python code:
https://github.com/QuantConnect/Lean/blob/master/Algorithm.Python/OrderTicketDemoAlgorithm.py

C# OCO Sample:
https://www.quantconnect.com/forum/discussion/1700/support-for-one-cancels-the-other-order-oco/p1

Monitor order event handler to see when stops triggered:
https://www.quantconnect.com/forum/discussion/2380/know-when-stop-order-or-stop-limit-order-has-triggered/p1



DEBUGGING:
Debug python in visual studio:
https://www.quantconnect.com/tutorials/open-source/debugging-python-in-visual-studio



"""
from datetime import datetime, timedelta
#from pivots import carmillaPivots <<don't know how to import pivots.py, keep getting an error that it's not defined

class CarmillaH3(QCAlgorithm):
    
    def __init__(self):
        self.traded = False
    
    def Initialize(self):
#main parameters        
        self.SetStartDate(2018, 11, 25)  # Set Start Date
        self.SetEndDate(datetime.now()) # Set End Date
        self.SetCash(100000)  # Set Strategy Cash
        
#pivot storage / trade flags       
        self.currentPivots = {}
        self.priorPivots = {}
        self.tradePivots = {}
        self.traded = False
        
#order tracking        
        self.__openLimitOrders = []
        self.__openStopMarketOrders = []
        
#Symbol Setyo
        #in the future will use universes. Excited QC supports multiple asset selection like this!
        symbol = "SPY"
        equity = self.AddEquity(symbol, Resolution.Minute)
        self.equity = equity.Symbol
        
                #may want to account for dividends based on backtest results!
                #eventually want to include universe of stocks
        
        #set warm-up period of 1 days so can calculate pivots (need 2 days when compare the two)
        self.SetWarmUp(timedelta(2)) # this is correct
        
        #Brokerage model and account type:
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)
        
        ## EXTRANEOUS Set benchmark
        self.SetBenchmark("SPY")
        
#CONSOLIDATOR as per: https://www.quantconnect.com/tutorials/api-tutorials/consolidating-data-to-build-bars
        
        # define our 1 day bar consolidator
        pivotConsolidator = TradeBarConsolidator(timedelta(days=1))

        # attach our event handler. The event handler is a function that will
        # be called each time we produce a new consolidated piece of data.
        pivotConsolidator.DataConsolidated += self.PivotBarHandler

        # this call adds the pivot consolidator to
        # the manager to receive updates from the engine
        self.SubscriptionManager.AddConsolidator(symbol, pivotConsolidator)

#Create a function which accepts a consolidated daily OHLC bar and returns a dictionary of pivots:
#Including (H5,H4,H3,H2,H1,C,L1,L2,L3,L4,L5)
#Decided to use a dictionary instead of a tuple due to code readablity on lookup
#Equation from "Secrets of a Pivot Boss"
        
    def carmillaPivots(self, bar):
        close = bar.Close
        high = bar.High
        low =  bar.Low
        r = high - low
        return {
            'H5': (high/low)*close,
            'H4': close + r*(1+1/2),
            'H3': close + r*(1+1/4),
            'H2': close + r*(1+1/6),
            'H1': close + r*(1+1/12),
            'C' : close,
            'L1': close - r*(1+1/12),
            'L2': close - r*(1+1/6),
            'L3': close - r*(1+1/4),
            'L4': close - r*(1+1/2),
            'L5': close - ((high/low)*close-close)
            }

    def PivotBarHandler(self, sender, bar):
        '''This is our event handler for our daily bar defined above in Initialize(). So each time the consolidator produces a new bar, this function will be called automatically. The sender parameter will be the instance of the IDataConsolidator that invoked the event '''
        #self.Debug(str(self.Time) + " " + str(bar))
        self.priorPivots = self.currentPivots
        self.currentPivots = self.carmillaPivots(bar)
        self.traded = False
        


#Main event handler
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
#Only run trade logic if IsWarmingUp is false, which will prevent errors due to unpopulated pivot dictionaries
        if self.IsWarmingUp:
            return

#If traded is false, create orders
        
        if not self.traded and not self.Portfolio.Invested: #if we haven't traded today and don't have a position from the previous day (or anymore)
            self.Liquidate() #cancel all potentially open orders using liquidate this allows prior trade to complete and still place new trades for the day
    #place limit orders on H3 and L3 pivots, rounding to nearest cent with GTC expiry
        #close = self.Securities[self.equity.Value].Close
        #H3 short
            newTicket = self.LimitOrder(self.equity, -10, self.currentPivots["H3"])
            self.__openLimitOrders.append(newTicket)
        #L3 long
            newTicket = self.LimitOrder(self.equity, 10, self.currentPivots["L3"])
            self.__openLimitOrders.append(newTicket)
        
    #place stop orders on H4 and L4 pivot        
        #H4 stop (market sell) 
            newTicket = self.StopMarketOrder(self.equity, 10, self.currentPivots["H4"])
            self.__openStopMarketOrders.append(newTicket)
        #L4 stop (market buy) 
            newTicket = self.StopMarketOrder(self.equity, -10, self.currentPivots["L4"])
            self.__openStopMarketOrders.append(newTicket)

    #Set flag and pivots
            self.traded == True
            self.tradePivots == self.currentPivots

        
        else: #check to see how many stop orders remain. If none means we've lost (because we're cancelling the other on trade fill) and need to close the opposite limit and stop order.
            if len(self.__openStopMarketOrders) <2:
                self.Liquidate()
'''
if not self.Portfolio.Invested:
            self.SetHoldings(symbol, 1)
                holdings = self.Portfolio[self.contract.Symbol].Quantity
                
                if holdings <= 0:
                    # Go long
                    if self.fast_sma > self.slow_sma:
                        self.Log("BUY >> {}".format(price))
                        self.MarketOrder(self.contract.Symbol, 1)
                if holdings > 0 and self.fast_sma < self.slow_sma:
                    self.Log("SELL >> {}".format(price))
                    self.Liquidate()
'''

#Helper order methods:
    #def ......
#Create a function which accepts a consolidated daily OHLC bar and returns a dictionary of pivots:
#Including (H5,H4,H3,H2,H1,C,L1,L2,L3,L4,L5)
#Decided to use a dictionary instead of a tuple due to code readablity on lookup
#Equation from "Secrets of a Pivot Boss"

def carmillaPivots(bar):
    close = bar.Close
    high = bar.High
    low =  bar.Low
    r = high - low
    return {
        'H5': (high/low)*close,
        'H4': close + r*(1+1/2),
        'H3': close + r*(1+1/4),
        'H2': close + r*(1+1/6),
        'H1': close + r*(1+1/12),
        'C' : close,
        'L1': close - r*(1+1/12),
        'L2': close - r*(1+1/6),
        'L3': close - r*(1+1/4),
        'L4': close - r*(1+1/2),
        'L5': close - ((high/low)*close-close),
    }