# indie:lang_version = 5
# Stochastic Optimized Trend Tracker (SOTT) — Indie port
# Original Pine Script v4 by KivancOzbilgic / Anil Ozeksi (Mozilla Public License 2.0)
# Migration notes:
#   Logic ported 1:1: %K = stoch(close, high, low, periodK) smoothed by VAR, then OTT on (%K + 1000)
#   The Pine input "Source" (src1) only feeds an unused VAR1 series in the original, so it is not ported
#   hline(1080/1020) -> constant lines; the band fill is not ported
#   alertcondition() — no Indie equivalent

from math import nan, isnan
from indie import indicator, param, plot, MainContext, color, format, MutSeriesF
from indie.algorithms import Sma, Highest, Lowest

@indicator('Stochastic Optimized Trend Tracker', format=format.PRICE, precision=2)
@param.int('period_k', default=500, min=1, title='%K Length')
@param.int('smooth_k', default=200, min=1, title='%K Smoothing')
@param.int('length', default=2, min=1, title='OTT Period')
@param.float('percent', default=0.5, min=0.0, step=0.1, title='OTT Percent')
@param.bool('showsupport', default=False, title='Show Support Line?')
@param.bool('showsignalsc', default=False, title='Show Stochastic/OTT Crossing Signals?')
@plot.line('upper_band', color=color.rgba(96, 96, 96, 1.0), line_width=1, title='Upper Band')
@plot.line('lower_band', color=color.rgba(96, 96, 96, 1.0), line_width=1, title='Lower Band')
@plot.line('k_line', color=color.rgba(0, 148, 255, 1.0), line_width=1, title='%K')
@plot.line('support', color=color.rgba(5, 133, 225, 1.0), line_width=2, title='Support Line')
@plot.line('ott', color=color.rgba(184, 0, 217, 1.0), line_width=2, title='OTT')
@plot.marker('buy_c', color=color.GREEN, style=plot.marker_style.LABEL)
@plot.marker('sell_c', color=color.RED, style=plot.marker_style.LABEL)
class Main(MainContext):
    def calc(self, period_k, smooth_k, length, percent, showsupport, showsignalsc):
        # stoch(close, high, low, periodK)
        hh: float = Highest.new(self.high, period_k)[0]
        ll: float = Lowest.new(self.low, period_k)[0]
        den: float = hh - ll
        st: float = 100.0 * (self.close[0] - ll) / den if (not isnan(den) and den != 0.0) else nan
        st_s = MutSeriesF.new(st)
        st_p: float = st_s[1]

        # Var_Func1(stoch, smoothK)
        a1: float = 2.0 / (smooth_k + 1)
        ud1: float = (st - st_p) if st > st_p else 0.0
        dd1: float = (st_p - st) if st < st_p else 0.0
        ud1_s = MutSeriesF.new(ud1)
        dd1_s = MutSeriesF.new(dd1)
        sud1: float = Sma.new(ud1_s, 9)[0] * 9.0
        sdd1: float = Sma.new(dd1_s, 9)[0] * 9.0
        den1: float = sud1 + sdd1
        cmo1: float = (sud1 - sdd1) / den1 if (not isnan(den1) and den1 != 0.0) else 0.0
        t1: float = a1 * abs(cmo1) * st
        t1 = 0.0 if isnan(t1) else t1
        var1_s = MutSeriesF.new(0.0)
        var1_p: float = var1_s[1] if not isnan(var1_s[1]) else 0.0
        var1_s[0] = t1 + (1.0 - a1 * abs(cmo1)) * var1_p
        k: float = var1_s[0]

        src: float = k + 1000.0
        src_s = MutSeriesF.new(src)
        src_p: float = src_s[1]

        # Var_Func(src, length) -> MAvg
        a2: float = 2.0 / (length + 1)
        ud2: float = (src - src_p) if src > src_p else 0.0
        dd2: float = (src_p - src) if src < src_p else 0.0
        ud2_s = MutSeriesF.new(ud2)
        dd2_s = MutSeriesF.new(dd2)
        sud2: float = Sma.new(ud2_s, 9)[0] * 9.0
        sdd2: float = Sma.new(dd2_s, 9)[0] * 9.0
        den2: float = sud2 + sdd2
        cmo2: float = (sud2 - sdd2) / den2 if (not isnan(den2) and den2 != 0.0) else 0.0
        t2: float = a2 * abs(cmo2) * src
        t2 = 0.0 if isnan(t2) else t2
        var2_s = MutSeriesF.new(0.0)
        var2_p: float = var2_s[1] if not isnan(var2_s[1]) else 0.0
        var2_s[0] = t2 + (1.0 - a2 * abs(cmo2)) * var2_p
        mavg: float = var2_s[0]

        # OTT
        fark: float = mavg * percent * 0.01
        long0: float = mavg - fark
        short0: float = mavg + fark
        ls_s = MutSeriesF.new(long0)
        ss_s = MutSeriesF.new(short0)
        long_prev: float = ls_s[1] if not isnan(ls_s[1]) else long0
        short_prev: float = ss_s[1] if not isnan(ss_s[1]) else short0
        ls_s[0] = max(long0, long_prev) if mavg > long_prev else long0
        ss_s[0] = min(short0, short_prev) if mavg < short_prev else short0

        dir_s = MutSeriesF.new(1.0)
        dir_prev: float = dir_s[1] if not isnan(dir_s[1]) else 1.0
        if dir_prev == -1.0 and mavg > short_prev:
            dir_s[0] = 1.0
        elif dir_prev == 1.0 and mavg < long_prev:
            dir_s[0] = -1.0
        else:
            dir_s[0] = dir_prev

        mt: float = ls_s[0] if dir_s[0] == 1.0 else ss_s[0]
        ott: float = (mt * (200.0 + percent) / 200.0) if mavg > mt else (mt * (200.0 - percent) / 200.0)
        ott_s = MutSeriesF.new(ott)
        o2: float = ott_s[2]
        o3: float = ott_s[3]

        buy_c: bool = src > o2 and src_p <= o3
        sell_c: bool = src < o2 and src_p >= o3
        ott_plot: float = 0.0 if isnan(o2) else o2

        return (
            plot.Line(1080.0),
            plot.Line(1020.0),
            plot.Line(k + 1000.0),
            plot.Line(mavg if showsupport else nan),
            plot.Line(ott_plot),
            plot.Marker(ott * 0.995 if (buy_c and showsignalsc) else nan, text='Buy'),
            plot.Marker(ott * 1.005 if (sell_c and showsignalsc) else nan, text='Sell'),
        )
