Overall Statistics
Total Trades
246
Average Win
1.18%
Average Loss
-1.33%
Compounding Annual Return
2.498%
Drawdown
18.100%
Expectancy
0.257
Net Profit
48.438%
Sharpe Ratio
0.344
Probabilistic Sharpe Ratio
0.224%
Loss Rate
33%
Win Rate
67%
Profit-Loss Ratio
0.89
Alpha
0.025
Beta
-0.029
Annual Standard Deviation
0.066
Annual Variance
0.004
Information Ratio
-0.37
Tracking Error
0.192
Treynor Ratio
-0.794
Total Fees
$246.00
# Default Imports START
from QuantConnect import *
from QuantConnect import Orders
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 System.Drawing import Color
QCAlgorithmFramework = QCAlgorithm
QCAlgorithmFrameworkBridge = QCAlgorithm
# Default Imports ENDfrom typing import Dictfrom typing import Dict

from typing import Dict
from System import String

class MyAlgo(QCAlgorithm):
    
    # general
    secs = [ "SPY" ]
    maxPositions = len(secs)

    # indicators
    sma_duration = 200
    rsi_duraction = 14
    
    # buy
    rsi_buy_level = 30
    
    # sell
    rsi_sell_level = 40
    sell_after_days = 14

    def Initialize(self):
        self.SetStartDate(2005, 1, 1)
        self.SetEndDate(2020, 12, 31)
        
        self.SetCash(10000)

        self.smas: Dict[String, SimpleMovingAverage] = {}
        self.rsis: Dict[String, RelativeStrengthIndex] = {}
        self.bought_at: Dict[String, datetime] = {}

        for sec in self.secs:
            self.Debug("Add " + sec + " to view list!")
            self.AddEquity(sec, Resolution.Daily)
            self.smas[sec] = self.SMA(sec, self.sma_duration, Resolution.Daily, Field.Close)
            self.rsis[sec] = self.RSI(sec, self.rsi_duraction, MovingAverageType.Simple , Resolution.Daily, Field.Close)

            chart = Chart(sec)
            chart.AddSeries(Series("Price", SeriesType.Line, 0))
            chart.AddSeries(Series("Buy", SeriesType.Scatter, 0))
            chart.AddSeries(Series("Sell", SeriesType.Scatter, 0))
            chart.AddSeries(Series("SMA", SeriesType.Line, 0))
            chart.AddSeries(Series("RSI", SeriesType.Line, 1))
            chart.AddSeries(Series("RSI Buy Level", SeriesType.Line, 1))
            self.AddChart(chart)
        
        self.SetWarmUp(max(self.sma_duration, self.rsi_duraction))
        
    def OnData(self, data: Slice):
        for sec in self.secs:
            if not data.ContainsKey(sec):
                self.Debug("Data for " + sec + " not found. Skipping for this day!")
                break
            
            if self.IsWarmingUp:
                break
        
            self.handleSells(sec, data)
            self.handleBuys(sec, data)
            
            self.Plot(sec, "Price", data[sec].Close)
            self.Plot(sec, "SMA", self.smas[sec].Current.Value)
            self.Plot(sec, "RSI", self.rsis[sec].Current.Value)
            self.Plot(sec, "RSI Buy Level", self.rsi_buy_level)
        
    def handleSells(self, sec: String, data: Slice):
        if self.Portfolio[sec].Invested and self.isSellSignal(sec, data):
            self.Liquidate(sec)
            currentPrice = data[sec].Close
            self.Plot(sec, "Sell", currentPrice)
    
    def handleBuys(self, sec: String, data: Slice):
        if self.Portfolio[sec].Invested:
            return
        
        if self.isBuySignal(sec, data):
            
            currentPositions = self.countPositions()
            currentCash = self.Portfolio.Cash
            if currentPositions == self.maxPositions:
                return
            orderVolume = currentCash / (self.maxPositions - currentPositions)
            currentPrice = data[sec].Close
            quantity = orderVolume / currentPrice
            
            self.MarketOnOpenOrder(sec, quantity)
            self.bought_at[sec] = self.Time
            self.Plot(sec, "Buy", currentPrice)

    def isBuySignal(self, sec: String, data: Slice):
        if data[sec].Close <= self.smas[sec].Current.Value:
            return False
        return self.rsis[sec].Current.Value < self.rsi_sell_level

    def isSellSignal(self, sec: String, data: Slice):
        return self.rsis[sec].Current.Value >= self.rsi_sell_level or (self.Time - self.bought_at[sec]).days >= self.sell_after_days
        
    def countPositions(self):
        count = 0
        for sec in self.Portfolio:
            if sec.Value.Invested:
                count += 1
        return count