Overall Statistics
Total Trades
0
Average Win
0%
Average Loss
0%
Compounding Annual Return
0%
Drawdown
0%
Expectancy
0
Net Profit
0%
Sharpe Ratio
0
Probabilistic Sharpe Ratio
0%
Loss Rate
0%
Win Rate
0%
Profit-Loss Ratio
0
Alpha
0
Beta
0
Annual Standard Deviation
0
Annual Variance
0
Information Ratio
2.351
Tracking Error
0.152
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
Portfolio Turnover
0%
#region imports
from AlgorithmImports import *
#endregion
from datetime import timedelta
import numpy as np
from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Brokerages import *
from QuantConnect.Data import *
from QuantConnect.Data.Market import *
from QuantConnect.Orders import *
from QuantConnect.Securities 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.Algorithm.Framework.Selection import *

class MeanReversionAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2022, 1, 1)
        self.SetEndDate(2022, 1, 31)
        self.SetCash(100000)
        self.SetBrokerageModel(BrokerageName.QuantConnectBrokerage, AccountType.Margin)
        self.UniverseSettings.Resolution = Resolution.Daily
        self.SetAlpha(MeanReversionAlphaModel())
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        self.SetExecution(ImmediateExecutionModel())
        self.SetRiskManagement(MaximumDrawdownPercentPerSecurity(0.01))

        self.Log("Algorithm initialized.")

        # Add symbols to the universe
        self.symbol = 'ZL'  # CBOT Soybean Oil futures symbol 
        # self.AddFutureContract(Futures.Grains.SoybeanOil, timedelta(days=30))
        # self.AddFutureContract(Futures.Grains.SoybeanOil, timedelta(days=60))

    def OnData(self, data):
        self.Log("Received new data.")

        insights = []
        for symbol, symbolData in self.symbolDataBySymbol.items():
            if not data.Bars.ContainsKey(symbol) or not symbolData.IsReady:
                continue
            price = data.Bars[symbol].Close
            symbolData.Update(price)
            if symbolData.IsOversold():
                insights.append(Insight.Price(symbol, timedelta(minutes=1), InsightDirection.Up))
            elif symbolData.IsOverbought():
                insights.append(Insight.Price(symbol, timedelta(minutes=1), InsightDirection.Down))
        self.EmitInsights(insights)

    def OnSecuritiesChanged(self, changes):
        for security in changes.RemovedSecurities:
            symbolData = self.symbolDataBySymbol.pop(security.Symbol, None)
            if symbolData is not None:
                symbolData.Dispose()
        addedSymbols = [x.Symbol for x in changes.AddedSecurities if x.Symbol not in self.symbolDataBySymbol]
        history = self.History(addedSymbols, 20, Resolution.Daily)
        for symbol in addedSymbols:
            symbolData = SymbolData(symbol, 20, history)
            self.symbolDataBySymbol[symbol] = symbolData

    def OnOrderEvent(self, orderEvent):
        self.Log(f"Order event: {orderEvent}")

    def OnEndOfAlgorithm(self):
        self.Log("Algorithm ended.")

class MeanReversionAlphaModel(AlphaModel):
    def __init__(self, lookback=20):
        self.lookback = lookback
        self.symbolDataBySymbol = {}

    def Update(self, algorithm, data):
        insights = []
        for symbol, symbolData in self.symbolDataBySymbol.items():
            if not data.Bars.ContainsKey(symbol) or not symbolData.IsReady:
                continue
            price = data.Bars[symbol].Close
            symbolData.Update(price)
            if symbolData.IsOversold():
                insights.append(Insight.Price(symbol, timedelta(minutes=1), InsightDirection.Up))
            elif symbolData.IsOverbought():
                insights.append(Insight.Price(symbol, timedelta(minutes=1), InsightDirection.Down))
        return insights

    def OnSecuritiesChanged(self, algorithm, changes):
        for security in changes.RemovedSecurities:
            symbolData = self.symbolDataBySymbol.pop(security.Symbol, None)
            if symbolData is not None:
                symbolData.Dispose()
        addedSymbols = [x.Symbol for x in changes.AddedSecurities if x.Symbol not in self.symbolDataBySymbol]
        history = algorithm.History(addedSymbols, self.lookback, Resolution.Daily)
        for symbol in addedSymbols:
            symbolData = SymbolData(symbol, self.lookback, history)
            self.symbolDataBySymbol[symbol] = symbolData

class SymbolData:
    def __init__(self, symbol, lookback, history):
        self.symbol = symbol
        self.lookback = lookback
        self.history = history
        self.prices = []
        self.IsReady = False

    def Update(self, price):
        self.prices.append(price)
        if len(self.prices) > self.lookback:
            self.prices.pop(0)
            self.IsReady = True

    def IsOversold(self):
        if not self.IsReady:
            return False
        return self.prices[-1] < np.mean(self.prices) - 2 * np.std(self.prices)

    def IsOverbought(self):
        if not self.IsReady:
            return False
        return self.prices[-1] > np.mean(self.prices) + 2 * np.std(self.prices)

    def Dispose(self):
        pass

algorithm = MeanReversionAlgorithm()
# algorithm.Run()