// Path: src/main/java/au/net/woodberry/ta/toolbox/enums/Sentiment.java
// public enum Sentiment {
//     
//     BULLISH, NEUTRAL, BEARISH;
// }
// 
// Path: src/main/java/au/net/woodberry/ta/toolbox/object/NR7Breakout.java
// public class NR7Breakout {
//     
//     private final Sentiment sentiment;
//     private final Tick nr7;
//     private final Tick breakout;
// 
//     /**
//      * *
//      * @param nr7 The tick at which the nr7 pattern has formed and the pattern ends
//      * @param breakout The tick at which a breakout is signalled
//      * @param sentiment The sentiment of the breakout
//      */
//     public NR7Breakout(Tick nr7, Tick breakout, Sentiment sentiment) {
//         this.nr7 = nr7;
//         this.breakout = breakout;
//         this.sentiment = sentiment;
//     }
// 
//     public Sentiment getSentiment() {
//         return sentiment;
//     }
// 
//     public Tick getNr7() {
//         return nr7;
//     }
// 
//     public Tick getBreakout() {
//         return breakout;
//     }
// 
//     @Override
//     public String toString() {
//         return "NR7 tick: [Date: " + nr7.getEndTime() + "] " +
//                "Breakout Tick: [Date:" + breakout.getEndTime() + "] " +
//                "Sentiment: " + sentiment;
//     }
// }


import au.net.woodberry.ta.toolbox.enums.Sentiment;
import au.net.woodberry.ta.toolbox.object.NR7Breakout;
import eu.verdelhan.ta4j.Tick;
import eu.verdelhan.ta4j.TimeSeries;
import eu.verdelhan.ta4j.indicators.CachedIndicator;
     * @param timeSeries A TimeSeries, containing both a max and min price
     * @param nr7Index The index position where the nr7 signal exists
     */
    public NR7BreakoutIndicator(TimeSeries timeSeries, int nr7Index) {
        if (timeSeries == null) {
            throw new IllegalArgumentException("Supplied TimeSeries is invalid: NULL");
        }
        if (timeSeries.getSize() <= 2) {
            throw new IllegalArgumentException("Supplied TimeSeries is invalid: Cannot be less than size of 2");
        }
        if (nr7Index == timeSeries.getEnd()) {
            throw new IllegalArgumentException("Supplied Integer index is invalid: Cannot be the last index within the TimeSeries");
        }
        if (nr7Index > timeSeries.getEnd()) {
            throw new IllegalArgumentException("Supplied Integer index is invalid: Not within the TimeSeries");
        }
        this.nr7Index = nr7Index;
        this.firstIndex = nr7Index + 1; // First possible breakout occurs after the nr7 tick
        this.timeSeries = timeSeries;
    }

    @Override
    protected NR7Breakout calculate(int index) {
        if (index <= nr7Index) {
            return null;
        }
        for (int i = firstIndex; i <= index; i++) {
            Tick tick = timeSeries.getTick(i);
            Tick nr7Tick = timeSeries.getTick(nr7Index);
            if (tick.getClosePrice().isGreaterThan(nr7Tick.getMaxPrice())) { // Return the first bullish signal found
// Path: src/main/java/au/net/woodberry/ta/toolbox/enums/Sentiment.java
// public enum Sentiment {
//     
//     BULLISH, NEUTRAL, BEARISH;
// }
// 
// Path: src/main/java/au/net/woodberry/ta/toolbox/object/NR7Breakout.java
// public class NR7Breakout {
//     
//     private final Sentiment sentiment;
//     private final Tick nr7;
//     private final Tick breakout;
// 
//     /**
//      * *
//      * @param nr7 The tick at which the nr7 pattern has formed and the pattern ends
//      * @param breakout The tick at which a breakout is signalled
//      * @param sentiment The sentiment of the breakout
//      */
//     public NR7Breakout(Tick nr7, Tick breakout, Sentiment sentiment) {
//         this.nr7 = nr7;
//         this.breakout = breakout;
//         this.sentiment = sentiment;
//     }
// 
//     public Sentiment getSentiment() {
//         return sentiment;
//     }
// 
//     public Tick getNr7() {
//         return nr7;
//     }
// 
//     public Tick getBreakout() {
//         return breakout;
//     }
// 
//     @Override
//     public String toString() {
//         return "NR7 tick: [Date: " + nr7.getEndTime() + "] " +
//                "Breakout Tick: [Date:" + breakout.getEndTime() + "] " +
//                "Sentiment: " + sentiment;
//     }
// }


// Path: src/main/java/au/net/woodberry/ta/toolbox/indicators/breakout/NR7BreakoutIndicator.java
import au.net.woodberry.ta.toolbox.enums.Sentiment;
import au.net.woodberry.ta.toolbox.object.NR7Breakout;
import eu.verdelhan.ta4j.Tick;
import eu.verdelhan.ta4j.TimeSeries;
import eu.verdelhan.ta4j.indicators.CachedIndicator;

     * @param timeSeries A TimeSeries, containing both a max and min price
     * @param nr7Index The index position where the nr7 signal exists
     */
    public NR7BreakoutIndicator(TimeSeries timeSeries, int nr7Index) {
        if (timeSeries == null) {
            throw new IllegalArgumentException("Supplied TimeSeries is invalid: NULL");
        }
        if (timeSeries.getSize() <= 2) {
            throw new IllegalArgumentException("Supplied TimeSeries is invalid: Cannot be less than size of 2");
        }
        if (nr7Index == timeSeries.getEnd()) {
            throw new IllegalArgumentException("Supplied Integer index is invalid: Cannot be the last index within the TimeSeries");
        }
        if (nr7Index > timeSeries.getEnd()) {
            throw new IllegalArgumentException("Supplied Integer index is invalid: Not within the TimeSeries");
        }
        this.nr7Index = nr7Index;
        this.firstIndex = nr7Index + 1; // First possible breakout occurs after the nr7 tick
        this.timeSeries = timeSeries;
    }

    @Override
    protected NR7Breakout calculate(int index) {
        if (index <= nr7Index) {
            return null;
        }
        for (int i = firstIndex; i <= index; i++) {
            Tick tick = timeSeries.getTick(i);
            Tick nr7Tick = timeSeries.getTick(nr7Index);
            if (tick.getClosePrice().isGreaterThan(nr7Tick.getMaxPrice())) { // Return the first bullish signal found
                return new NR7Breakout(nr7Tick, tick, Sentiment.BULLISH);