Overall Statistics
Total Trades
27683
Average Win
0.36%
Average Loss
-0.07%
Compounding Annual Return
11.573%
Drawdown
19.100%
Expectancy
0.042
Net Profit
89.501%
Sharpe Ratio
0.466
Sortino Ratio
0.56
Probabilistic Sharpe Ratio
11.195%
Loss Rate
82%
Win Rate
18%
Profit-Loss Ratio
4.86
Alpha
0
Beta
0
Annual Standard Deviation
0.15
Annual Variance
0.022
Information Ratio
0.606
Tracking Error
0.15
Treynor Ratio
0
Total Fees
$0.00
Estimated Strategy Capacity
$8700000.00
Lowest Capacity Asset
QQQ RIWIV7K5Z9LX
Portfolio Turnover
2233.05%
# region imports
from AlgorithmImports import *
from QuantConnect.Data import Slice
# endregion

# Fee model to test zero fees
class CustomFeeModel(FeeModel):
    def __init__(self):
        pass  
    def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee:
        return OrderFee.Zero

# Buying power model so that we can aoivd trading single shares
class MinimumOrderSizeBuyingPowerModel(BuyingPowerModel):
    def __init__(self, min_order_size):
        self.min_order_size = min_order_size

    def GetMinimumOrderQuantityForResolution(self, security, targetOrderValue):
        return OrderQuantity(self.min_order_size, 0)

    def GetBuyingPower(self, parameters):
        return BuyingPower(self.Portfolio.Cash, 0)



class Vwaptrend(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2018, 1, 1)
        self.SetEndDate(2023, 11, 1)
        self.SetCash(25000)
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        self.SetExecution(ImmediateExecutionModel())
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        self.asset1 = self.AddEquity("QQQ", Resolution.Minute)
        self.asset1.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.asset1.SetBuyingPowerModel(MinimumOrderSizeBuyingPowerModel(10))  # Set minimum order size to 10 so that insights don't trade single shares
        self.asset1_vwap = self.VWAP(self.asset1.Symbol)
        self.asset1_long = None
        self.period = timedelta(days=1)

        # turn off fees
        self.asset1.SetFeeModel(CustomFeeModel())

    def OnData(self, slice: Slice) -> None:
        if not self.asset1_vwap.IsReady or (self.Time.hour == 9 and self.Time.minute == 30) or (self.Time.hour == 16 and self.Time.minute == 0):
            return

        # exit position EOD
        if self.Time.hour == 15 and self.Time.minute == 59:
            asset1_insight = Insight.Price(self.asset1.Symbol, self.period, InsightDirection.Flat)
            self.EmitInsights(asset1_insight)
            self.Log(f'Exiting position at EOD')
            self.asset1_long = None
            return
        
        # if getting second data but don't want to trade every second
        # if not self.Time.second == 0:
        #     return
        
        # if we want to trade every 5 minutes
        # if not self.Time.minute % 5 == 0:
        #     return
        
        price = self.asset1.Close
        if price > self.asset1_vwap.Current.Value and (not self.asset1_long or self.asset1_long is None):
            asset1_insight = Insight.Price(self.asset1.Symbol, self.period, InsightDirection.Up)
            self.EmitInsights(asset1_insight)
            self.asset1_long = True
            self.Log(f'vwap: {round(self.asset1_vwap.Current.Value, 2)} price: {price} Flipping to long')

        if price < self.asset1_vwap.Current.Value and (self.asset1_long or self.asset1_long is None):
            asset1_insight = Insight.Price(self.asset1.Symbol, self.period, InsightDirection.Down)
            self.EmitInsights(asset1_insight)
            self.asset1_long = False
            self.Log(f'vwap: {round(self.asset1_vwap.Current.Value, 2)} price: {price} Flipping to short')

        # do this every few minutes
        # if self.Time.minute % 1  == 0:    
        #     self.Plot("IntradayVwap", "vwap", self.asset1_vwap.Current.Value)
        #     self.Plot("IntradayVwap", "price", price)
        #     self.Log(f"vwap: {self.asset1_vwap.Current.Value} price: {price}")



# region imports
from AlgorithmImports import *
from QuantConnect.Data import Slice
# endregion

class CustomFeeModel(FeeModel):
    def __init__(self):
        pass  
    def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee:
        return OrderFee.Zero


class MinimumOrderSizeBuyingPowerModel(BuyingPowerModel):
    def __init__(self, min_order_size):
        self.min_order_size = min_order_size

    def GetMinimumOrderQuantityForResolution(self, security, targetOrderValue):
        return OrderQuantity(self.min_order_size, 0)

    def GetBuyingPower(self, parameters):
        return BuyingPower(self.Portfolio.Cash, 0)

class Vwaptrend(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2018, 1, 1)
        self.SetEndDate(2023, 11, 1)
        self.SetCash(30000)
        self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
        self.SetExecution(ImmediateExecutionModel())
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        self.asset1 = self.AddEquity("QQQ", Resolution.Minute)
        self.asset1.SetDataNormalizationMode(DataNormalizationMode.Raw)
        self.asset1.SetBuyingPowerModel(MinimumOrderSizeBuyingPowerModel(10))  # Set minimum order size to 100
        self.asset1_vwap = self.VWAP(self.asset1.Symbol)
        self.asset1_long = None
        self.period = timedelta(days=1)

        self.asset1.SetFeeModel(CustomFeeModel())

    def OnData(self, slice: Slice) -> None:
        if not self.asset1_vwap.IsReady or (self.Time.hour == 9 and self.Time.minute == 30) or (self.Time.hour == 16 and self.Time.minute == 0):
            return
        
        if not self.Time.second == 0:
            return
        
        # exit position EOD
        if self.Time.hour == 15 and self.Time.minute == 59:
            asset1_insight = Insight.Price(self.asset1.Symbol, self.period, InsightDirection.Flat)
            self.EmitInsights(asset1_insight)
            self.Log(f'Exiting position at EOD')
            self.asset1_long = None
            return
        
        # if not self.Time.minute % 5 == 0:
        #     return
        
        price = self.asset1.Close
        if price > self.asset1_vwap.Current.Value and (not self.asset1_long or self.asset1_long is None):
            asset1_insight = Insight.Price(self.asset1.Symbol, self.period, InsightDirection.Up)
            self.EmitInsights(asset1_insight)
            self.asset1_long = True
            self.Log(f'vwap: {round(self.asset1_vwap.Current.Value, 2)} price: {price} Flipping to long')

        if price < self.asset1_vwap.Current.Value and (self.asset1_long or self.asset1_long is None):
            asset1_insight = Insight.Price(self.asset1.Symbol, self.period, InsightDirection.Down)
            self.EmitInsights(asset1_insight)
            self.asset1_long = False
            self.Log(f'vwap: {round(self.asset1_vwap.Current.Value, 2)} price: {price} Flipping to short')

        # do this every few minutes
        # if self.Time.minute % 1  == 0:    
        #     self.Plot("IntradayVwap", "vwap", self.asset1_vwap.Current.Value)
        #     self.Plot("IntradayVwap", "price", price)
        #     self.Log(f"vwap: {self.asset1_vwap.Current.Value} price: {price}")