Overall Statistics
Total Trades
294
Average Win
0.09%
Average Loss
-0.05%
Compounding Annual Return
1.956%
Drawdown
4.800%
Expectancy
0.506
Net Profit
3.456%
Sharpe Ratio
0.44
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.87
Alpha
-0.018
Beta
2.124
Annual Standard Deviation
0.037
Annual Variance
0.001
Information Ratio
0.005
Tracking Error
0.037
Treynor Ratio
0.008
Total Fees
$0.00
from datetime import datetime, timedelta
import numpy as np
import pandas as pd
from decimal import Decimal
from System.Drawing import Color

# https://www.investoo.com/pivot-breakout-forex-strategy/

class SvePivotsAlgorithm(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2017, 1, 1)
        self.SetEndDate(2018, 10, 1)
        self.SetCash(10000)
        self.symbol = self.AddForex("EURUSD", Resolution.Minute, Market.Oanda).Symbol
        self.pivot = SvePivots(1)
        self.SetWarmUp(timedelta(2))
        dailyConsolidator = QuoteBarConsolidator(timedelta(1))
        dailyConsolidator.DataConsolidated += self.DailyBarHandler
        self.SubscriptionManager.AddConsolidator(self.symbol, dailyConsolidator)
        self.RegisterIndicator(self.symbol, self.pivot, dailyConsolidator)

        fiveMinuteConsolidator = QuoteBarConsolidator(timedelta(minutes=5))
        dailyConsolidator.DataConsolidated += self.fiveMinuteBarHandler
        self.SubscriptionManager.AddConsolidator(self.symbol, fiveMinuteConsolidator)
        self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.AfterMarketOpen(self.symbol, 1), self.EveryDayAfterMarketOpen)
        self.bars = RollingWindow[QuoteBar](2)
        self.state = None
 
        plot = Chart("PivotPlot")
        for i in ["S1","S2","S3","R1","R2","R3"]:
            plot.AddSeries(Series(i, SeriesType.Line, 0))
        plot.AddSeries(Series("PP", SeriesType.Line, '$', Color.Red))
        plot.AddSeries(Series("Price", SeriesType.Line, '$', Color.Blue))

        self.AddChart(plot)
        
    def OnData(self, data):
        pass
    
    def EveryDayAfterMarketOpen(self):
         # establish the market state bias for the day after the market open
         # the bias for the day is bullish if the price action opens for the trading day above the central pivot point
        if self.Securities[self.symbol].Price > self.pivot.PP:
            self.state = True
         # the bias for the day is bearish if the price action opens for the trading day below the central pivot point
        if self.Securities[self.symbol].Price < self.pivot.PP:
            self.state = False

    def DailyBarHandler(self, sender, bar):
        self.Plot("PivotPlot", 'S1', self.pivot.S1)
        self.Plot("PivotPlot", 'S2', self.pivot.S2)
        self.Plot("PivotPlot", 'S3', self.pivot.S3)
        self.Plot("PivotPlot", 'R1', self.pivot.R1)
        self.Plot("PivotPlot", 'R2', self.pivot.R2)
        self.Plot("PivotPlot", 'R3', self.pivot.R3)
        self.Plot("PivotPlot", 'PP', self.pivot.PP)
        self.Plot("PivotPlot", 'Price', self.Securities[self.symbol].Price)

    
    def fiveMinuteBarHandler(self, sender, bar):
        ''' When the price action eventually breaks out of one boundary, 
            use the principles of the breakout trade to make the entry and use the next pivot point 
            in the path of the price action as the profit target, while setting a stop loss below 
            the broken pivot (long trade) or above the broken pivot (short trade). 
        '''
        
        if self.IsWarmingUp or not self.pivot.IsReady: return
        self.bars.Add(bar)
        if not self.bars.IsReady: return
        # Be bullish when the price is above the main pivot point
        if self.state:
            # trip from below to above R1
            if self.bars[1].High < self.pivot.R1 < self.bars[0].High:
                    self.MarketOrder(self.symbol, 1000)
                    self.LimitOrder(self.symbol, -1000, self.pivot.R2)
                    self.StopMarketOrder(self.symbol, -1000, self.pivot.R1-Decimal(0.00500))

            # trip from below to above R2
            if self.bars[1].High < self.pivot.R2 < self.bars[0].High:
                    self.Transactions.CancelOpenOrders(self.symbol)
                    self.MarketOrder(self.symbol, 1000)
                    self.LimitOrder(self.symbol, -1000, self.pivot.R3)
                    self.StopMarketOrder(self.symbol, -1000, self.pivot.R2-Decimal(0.00500))


        # Be bearish when the price is below the main pivot point
        if not self.state:
            # trip from above to below S1
            if self.bars[0].Low < self.pivot.S1 < self.bars[1].Low:
                    self.MarketOrder(self.symbol, -1000)
                    self.LimitOrder(self.symbol, 1000, self.pivot.S2)
                    self.StopMarketOrder(self.symbol, 1000, self.pivot.S1+Decimal(0.00500))

            # trip from above to below S2
            if self.bars[0].Low < self.pivot.S2 < self.bars[1].Low:
                    self.Transactions.CancelOpenOrders(self.symbol)
                    self.MarketOrder(self.symbol, -1000)
                    self.LimitOrder(self.symbol, 1000, self.pivot.S3)
                    self.StopMarketOrder(self.symbol, 1000, self.pivot.S2+Decimal(0.00800))


class SvePivots:
    def __init__(self, period):
        self.Time = datetime.min
        self.PP = None 
        self.R1 = self.R2 = self.R3 = None 
        self.S1 = self.S2 = self.S3 = None 
        self.RM1 = self.RM2 = self.RM3 = None
        self.SM1 = self.SM2 = self.SM3 = None
        self.PH = self.PL = None
        self.IsReady = False

    def Update(self, input):
        self.Time = input.EndTime
        self.PP = (input.High + input.Low + input.Close) / Decimal(3)  # Pivot point 
        self.R1 = self.PP*Decimal(2) - input.Low   # Resistance 1
        self.R2 = self.PP + (input.High - input.Low)  # Resistance 2
        self.R3 = self.PP*Decimal(2) + (input.High - input.Low*Decimal(2)) # Resistance 3
        self.S1 = self.PP*Decimal(2) - input.High  # Support 1
        self.S2 = self.PP - (input.High - input.Low)  # Support 2
        self.S3 = self.PP*Decimal(2) - (input.High*Decimal(2) - input.Low)  # Support 3
        self.RM1 = (self.R1-self.PP)/Decimal(2) + self.PP  # Resistance Mean value 1
        self.RM2 = (self.R2-self.R1)/Decimal(2) + self.R1  # Resistance Mean value 2
        self.RM3 = (self.R3-self.R2)/Decimal(2) + self.R2  # Resistance Mean value 3
        self.SM1 = (self.PP-self.S1)/Decimal(2) + self.S1  # Support Mean value 1
        self.SM2 = (self.S1-self.S2)/Decimal(2) + self.S2  # Support Mean value 2
        self.SM3 = (self.S2-self.S3)/Decimal(2) + self.S3  # Support Mean value 3
        self.PH = input.High # Previous day’s high
        self.PL = input.Low  # Previous day’s low

        self.IsReady = self.PP is not None