Overall Statistics
Total Trades
1625
Average Win
0.30%
Average Loss
-0.34%
Compounding Annual Return
48.294%
Drawdown
33.500%
Expectancy
-0.053
Net Profit
120.387%
Sharpe Ratio
1.519
Probabilistic Sharpe Ratio
64.950%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.89
Alpha
0.481
Beta
-0.169
Annual Standard Deviation
0.292
Annual Variance
0.085
Information Ratio
0.55
Tracking Error
0.393
Treynor Ratio
-2.63
Total Fees
$1833.50
Estimated Strategy Capacity
$250000000.00
Lowest Capacity Asset
DIS R735QTJ8XC9X
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm

from QuantConnect import *
from QuantConnect.Parameters import *
from QuantConnect.Benchmarks import *
from QuantConnect.Brokerages import *
from QuantConnect.Util import *
from QuantConnect.Interfaces import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Selection import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Indicators import *
from QuantConnect.Data import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Custom import *
from QuantConnect.Data.Fundamental import *
from QuantConnect.Data.Market import *
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Notifications import *
from QuantConnect.Orders import *
from QuantConnect.Orders.Fees import *
from QuantConnect.Orders.Fills import *
from QuantConnect.Orders.Slippage import *
from QuantConnect.Scheduling import *
from QuantConnect.Securities import *
from QuantConnect.Securities.Equity import *
from QuantConnect.Securities.Forex import *
from QuantConnect.Securities.Interfaces import *
from datetime import date, datetime, timedelta
from QuantConnect.Python import *
from QuantConnect.Storage import *

from QuantConnect import Resolution
from QuantConnect.Algorithm import QCAlgorithm


# topics
# 1. use of CoarseSelectionFunction with indicator
# 2. creation of self-defined SelectionData class used in CoarseSelectionFunction
# 3. use of historical data to warm up indicator
# takeaways
# 1. avoid updating the indicator using the same price by specificying a time stamp
# 2. avoid placing invalid orders by checking if the current slice has data for a symbol
class EMAMomentumUniverse(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2019, 1, 7)
        self.SetEndDate(2021, 1, 7)
        self.SetCash(100000)
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction)
        self.averages = {}

    def CoarseSelectionFunction(self, universe):
        selected = []
        universe = sorted(universe, key=lambda c: c.DollarVolume, reverse=True)
        universe = [c for c in universe if c.Price > 10][:100]

        for coarse in universe:
            symbol = coarse.Symbol

            if symbol not in self.averages:
                # 1. Call history to get an array of 200 days of history data
                history = self.History(symbol, 200, Resolution.Daily)

                # 2. Adjust SelectionData to pass in the history result
                self.averages[symbol] = SelectionData(history)
                # self.Debug(str(symbol))
                # self.Debug(history.index.get_level_values(1)[-1])  # last history date
                # self.Debug(f"history price: {history.close[-1]}")  # last history price
                # self.Debug(self.Time)  # algo date
                # self.Debug(f"algo price: {coarse.AdjustedPrice}")  # algo price
            self.averages[symbol].update(self.Time, coarse.AdjustedPrice)

            if (
                self.averages[symbol].is_ready()
                and self.averages[symbol].fast > self.averages[symbol].slow
            ):
                selected.append(symbol)

        return selected[:10]

    def OnSecuritiesChanged(self, changes):
        # self.Debug(self.Time)
        for security in changes.RemovedSecurities:
            self.Liquidate(security.Symbol)

        for security in changes.AddedSecurities:
            # self.Debug(str(security))
            if self.CurrentSlice.ContainsKey(
                security.Symbol
            ):  # check if the current slice has data for this symbol
                self.SetHoldings(security.Symbol, 0.10)


class SelectionData:
    # 3. Update the constructor to accept a history array
    def __init__(self, price_hist):
        self.slow = ExponentialMovingAverage(200)
        self.fast = ExponentialMovingAverage(50)
        # 4. Loop over the history data and update the indicators
        for bar in price_hist.itertuples():
            self.fast.Update(bar.Index[1], bar.close)
            self.slow.Update(bar.Index[1], bar.close)

    def is_ready(self):
        return self.slow.IsReady and self.fast.IsReady

    def update(self, time, price):
        self.fast.Update(time, price)
        self.slow.Update(time, price)