Overall Statistics
Total Trades
12
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
-0.241%
Drawdown
0.000%
Expectancy
-0.593
Net Profit
-0.002%
Sharpe Ratio
-10.938
Loss Rate
67%
Win Rate
33%
Profit-Loss Ratio
0.22
Alpha
-0.001
Beta
-0.104
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
-18.797
Tracking Error
0.001
Treynor Ratio
0.014
Total Fees
$12.00
import datetime

from QuantConnect import Orders


class Algorithm(QCAlgorithm):

    def Initialize(self):

        """
            In this example, we choose three companies,
            and place two limit orders for each, every day.
            At the end of the day, we cancel the limit orders
            that have *not* executed, sell off the shares that
            resulted from the limit orders that *have* executed,
            and verify that both attempts have resulted in an
            empty portfolio by the end of the day.

            We place two limit orders for each company, with
            limits of -1% and -10% relative to the company's
            price at market open. One of the limit orders will
            execute if and only if the company's stock price
            drops one percentage point throughout the day,
            relative to the open price, (a fairly common
            occurrence), while the other of the two limit
            orders will execute if and only if the company's
            stock price drops ten percent throughout the day
            (a very uncommon occurrence).

            These parameters were chosen for illustrative
            purposes, so that the typical trading day will
            come to an end the with us having two tasks we
            need to accomplish: we will have one sucessfully
            executed limit order (for which we need to sell off
            the shares we bought during the day), and we will
            have another limit order which we placed, but which
            generally will not have filled (for which we need
            to cancel the pending limit order).

            The resulting log demonstrates that we can successfully
            close out types of positions at the day's end,
            and then verify that we have.
        """

        self.SetStartDate(2018, 6, 25)
        self.SetEndDate(2018, 6, 27)

        self.SetCash(1_000_000)

        tickers = ["AAPL", "GOOGL", "MSFT"]

        for ticker in tickers:
            self.AddEquity(ticker, Resolution.Minute)

        self.Schedule.On(
            self.DateRules.EveryDay(),
            self.TimeRules.At(9, 31),
            self.marketOpenCallback
        )

        self.Schedule.On(
            self.DateRules.EveryDay(),
            self.TimeRules.At(15, 59),
            self.marketCloseCallback
        )

        self.Schedule.On(
            self.DateRules.EveryDay(),
            self.TimeRules.At(23, 0),
            self.nightTimeCallback
        )


    def OnData(self, data):
        """
            This method is normally called for each timestep
            of the data we have subscribed to. For example, if
            we called self.AddEquity('AAPL', Resolution.Minute)
            in the Initialize method, then the OnData method will
            be called every minute, and similarly for any other
            resolution we might have subscribed to. In this
            example, we will not have any reason to call this
            method, but the framework requires us to include it.
        """
        pass


    def marketOpenCallback(self):

        """
            Scheduled to be called soon after market open.

            In this function we place two limit orders for
            each ticker that we added to our equities in the
            Initialize method.
        """

        self.Debug(f"Executing marketOpenCallback")

        for ticker in self.Securities.Keys:
            price = self.getPrice(ticker)
            fraction_small = 0.01
            fraction_large = 0.10
            self.Debug(f"Placing two limit orders for {ticker}")
            self.LimitOrder(ticker, 1, price * (1 - fraction_small))
            self.LimitOrder(ticker, 1, price * (1 - fraction_large))


    def marketCloseCallback(self):

        """
            Scheduled to be called soon before market close.

            In this function we attempt to:
            (1) Sell off any remaining holdings.
            (2) Cancel any open limit orders.

            However, the resulting events can execute
            asynchronously in principle, so we have to
            wait until some time *after* the market is
            closed in order to check whether or not we
            have successfully performed (1) and (2).
        """

        self.Debug(f"Executing marketCloseCallback")

        for ticker in self.Securities.Keys:

            openOrders = self.Transactions.GetOpenOrders(ticker)
            invested = self.Portfolio[ticker].Invested
            quantity = self.Portfolio[ticker].Quantity
            numOpen = len(openOrders)

            # Cancel open limit orders that never hit their thresholds.
            if numOpen > 0:
                self.Debug(f"Cancelling {numOpen} open orders for {ticker}")
                self.Transactions.CancelOpenOrders(ticker)
            else:
                self.Debug(f"No remaining open order for {ticker}.")

            # Immediately sell off any remaining shares we're still holding.
            if invested:
                self.Debug(f"Selling off our {quantity} shares of {ticker}")
                self.MarketOrder(ticker, -quantity)
            else:
                self.Debug(f"No longer invested in {ticker}.")

            assert invested == (quantity > 0), (
                "Error: Quantity and invested don't agree."
            )
            

    def nightTimeCallback(self):

        """
            Scheduled to be called after market close.

            In this function we ensure that we properly:
            (1) Sold off any remaining holdings.
            (2) Cancelled any open limit orders.
        """

        self.Debug(f"Executing nightTimeCallback")

        anyInvested = self.Portfolio.Invested
        openOrders  = self.Transactions.GetOpenOrders()
        numOpen = len(openOrders)

        self.Debug(f"Any investments remaining?: {anyInvested}")
        self.Debug(f"Number of open orders: {numOpen}")

        assert not anyInvested, (
            "Error: Not all investments were sold off before close."
        )

        assert len(openOrders) == 0, (
            "Error: Not all open limit orders were cancelled before close."
        )


    def getPrice(self, symbol):
        return float(self.Securities[symbol].Price)


    def formatEventType(self, orderEvent):

        """
            This method turns the numeric values we receive from
            orderEvent.Status into a human readable string so we
            can tell what kind of event is being passed when the
            OnOrderEvent method is automatically called on fills
        """

        statuses = ('New', 'Submitted', 'PartiallyFilled', 'Filled',
                    'Canceled', 'None', 'Invalid', 'CancelPending')

        order_statuses = {
            getattr(Orders.OrderStatus, status): status
            for status in statuses
        }

        return order_statuses[orderEvent.Status]


    def OnOrderEvent(self, orderEvent):

        """
            This method is called by the QuantConnect framework whenever
            an "order event" occurs. Common events occur at the moment
            when an order is submitted, fully filled, partially filled,
            cancelled, or at the moment cancellation is requested, but
            before the cancellation occurs. For our purposes, the most
            relevant event type is the one which is emitted when a
            limit order fills.
        """

        symbol = orderEvent.Symbol
        filled = orderEvent.FillQuantity
        type   = self.formatEventType(orderEvent)

        self.Debug(f"Got '{type}' orderEvent for {symbol}, "
                   f"fill quantity is {filled}")