Overall Statistics
Total Trades
132
Average Win
0.10%
Average Loss
-0.11%
Compounding Annual Return
-1.659%
Drawdown
1.600%
Expectancy
-0.057
Net Profit
-0.428%
Sharpe Ratio
-0.775
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.89
Alpha
-0.069
Beta
2.768
Annual Standard Deviation
0.021
Annual Variance
0
Information Ratio
-1.712
Tracking Error
0.02
Treynor Ratio
-0.006
Total Fees
$0.00
import math
import datetime

import QuantConnect
from QuantConnect import Time
from QuantConnect import Orders
from QuantConnect.Brokerages import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Data.UniverseSelection import *

class Algorithm(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2017, 7, 31)
        self.SetEndDate(2017, 11, 1)
        self.SetCash(1_000_000)

        self.SetSecurityInitializer(self.ZeroFeesSecurityInitializer)

        self.ticker = "AAPL"
        self.shares = 1000

        aapl = self.AddEquity(self.ticker, Resolution.Minute, fillDataForward = True, extendedMarketHours = True)
        spy  = self.AddEquity("SPY", Resolution.Minute, fillDataForward = True, extendedMarketHours = True)

        # You can control the Data Normalization Mode for each asset individually. This is done with the: security.SetDataNormalizationMode() method.
        # The accepted values are: Raw, Adjusted, SplitAdjusted and TotalReturn.
        aapl.SetDataNormalizationMode(DataNormalizationMode.Raw)
        spy.SetDataNormalizationMode(DataNormalizationMode.Raw)

        # If we schedule this event at 9:30, and place a LimitOrder once 9:30
        # arrives, Quantconnect ends up returning the open of the previous day's
        # 15:59 minute bar when we request the open price. This is unfortunate,
        # and different from what they do when we place a MarketOrder.
        # As a result, we'll place a LimitOrder at 9:31, and compare the results
        # to a local backtest in which we place a LimitOrder at the same time.
        self.Schedule.On(
            self.DateRules.EveryDay("SPY"),
            self.TimeRules.At(9, 31),
            self.placeEntryOrders
        )

        self.Schedule.On(
            self.DateRules.EveryDay("SPY"),
            self.TimeRules.At(15, 59),
            self.placeExitOrders
        )

        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse("earnings-universe", self.universeSelector)


    def ZeroFeesSecurityInitializer(self, security):
        """ Initialize the security with zero fees. """
        security.SetFeeModel(ConstantFeeModel(0))
        return security

    def isSkippedDay(self):
        """ We need to skip the first day to let QuantConnect warm up,
            or else it thinks the price of any ticker is 0 on the first
            day of the backtest.
        """
        year, month, day = self.Time.year, self.Time.month, self.Time.day
        if year == 2017 and month == 7 and day == 31:
            return True
        else:
            return False

    def placeEntryOrders(self):

        self.Debug(f"{self.Time}: placeEntryOrders: entering")
        if self.isSkippedDay():
            self.Debug(f"{self.Time}: placeEntryOrders: exiting early")
            return

        current_cash = float(self.Portfolio.Cash)

        ticker = self.ticker
        shares = self.shares

        price = self.getPrice(ticker)
        stoploss_percent = 0.01
        stoploss_price = price * (1 - stoploss_percent)
        stoploss_price = round(stoploss_price, 2)

        try:
            volume = self.getVolume(self.ticker)
            self.Log(f"{self.Time}: placeEntryOrders: placing order for {shares} shares of {ticker} at price {price}, stop loss price {stoploss_price} (vol: {volume})")
            self.MarketOrder(ticker, shares)
            self.StopMarketOrder(ticker, -shares, stoploss_price)
        except ZeroDivisionError:
            return

    def placeExitOrders(self):

        self.Debug(f"{self.Time}: placeExitOrders: entering")
        if self.isSkippedDay():
            self.Debug(f"{self.Time}: placeExitOrders: exiting early")
            return

        ticker = self.ticker
        volume = self.getVolume(ticker)
        price = self.getClose(ticker)
        self.Log(f"{self.Time}: placeExitOrders: Liquidating {ticker} at price {price} (vol: {volume})")
        self.Liquidate(ticker)


    def universeSelector(self, dt):
        today = dt.date().isoformat()
        return [self.ticker]

    def getVolume(self, symbol):
        volume = float(self.Securities[symbol].Volume)
        return volume

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

    def getClose(self, symbol):
        price = float(self.Securities[symbol].Close)
        return price
        
    def getOpen(self, symbol):
        price = float(self.Securities[symbol].Open)
        return price


    def decodeEventType(self, orderEvent):
        """
            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):

        ticker = orderEvent.Symbol.Value # Need the ".Value" to actually make this a real str object
        filled = orderEvent.FillQuantity
        eventType = self.decodeEventType(orderEvent)

        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        orderId = orderEvent.OrderId
        createdTime = order.CreatedTime

        # self.Debug(f"{self.Time}: OnOrderEvent: Got '{eventType}' event for {ticker}")

        if eventType == 'Filled':
            fillQuantity = orderEvent.FillQuantity
            fillPrice = orderEvent.FillPrice
            side = 'BUY' if fillQuantity > 0 else 'SELL'
            nowPrice = self.getPrice(self.ticker)
            nowOpen  = self.getOpen(self.ticker)
            nowClose = self.getClose(self.ticker)
            nowVolume = self.getVolume(self.ticker)
            self.Debug(f"{self.Time}: OnOrderEvent: fillPrice for {side} order is {fillPrice}, (shares: {fillQuantity})")
            self.Debug(f"    * Now: open = {nowOpen}, close = {nowClose}, price = {nowPrice} (vol: {nowVolume})")