скрин · код · дизайн каждого

Каждый индикатор — живой скрин + исходник

Все 80 из 80 встроенных индикаторов отсняты вживую (залогиненный терминал, DOGE/USD · 1h, каждый по отдельности). У 56 из них — полный Indie-исходник + дизайн (цвета/тип plot/format/параметры) под спойлером.

Метод: каждый индикатор добавлялся на чистый чарт по одному, рендерился сервером (R_PYTHON/R_WASM), снимался кадр, затем удалялся — изоляция гарантирована. Остальные 24 (Ichimoku/ZigZag/Pivots и пр.) — сложные мульти-классовые painter-индикаторы, у которых исходник в офиц. доках; скрин есть у всех 80. Инструменты рисования: 25 нарисованы вживую на чарте + скрины из доков для остальных.

Индикаторы (80) — живой скрин + код+дизайн каждого

Accumulation/Distribution
Accumulation/DistributionAccum/Dist
sub-paneformat: VOLUMEolive@plot.line
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, plot, color
from indie.algorithms import CumSum, Mfv


@indicator('Accum/Dist', format=format.VOLUME)  # Accumulation/Distribution
@plot.line(color=color.OLIVE)
def Main(self):
    return CumSum.new(Mfv.new())[0]
Advance/Decline Ratio (Bars)
Advance/Decline Ratio (Bars)ADR_B
sub-paneformat: PRICEbluegray@plot.line
length=9
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, level, color, plot, MutSeriesF
from indie.algorithms import Sum
from indie.math import divide


@indicator('ADR_B', format=format.PRICE)  # Advance/Decline Ratio (Bars)
@param.int('length', default=9, min=1)
@level(1, line_color=color.GRAY, title='Equality Line')
@plot.line(color=color.BLUE, title='ADR_B')
def Main(self, length):
    is_up = (self.close[0] - self.open[0]) >= 0.0

    is_down_ints = MutSeriesF.new(0 if is_up else 1)
    is_up_ints = MutSeriesF.new(1 if is_up else 0)

    down_bars = Sum.new(is_down_ints, length)
    up_bars = Sum.new(is_up_ints, length)

    return divide(up_bars[0], down_bars[0], up_bars[0])
Arnaud Legoux Moving Average
Arnaud Legoux Moving AverageALMA
overlayblue@plot.line
window_size=9 · offset=0.85 · sigma=6.0
Indie исходник
# indie:lang_version = 5
from math import exp, pow
from indie import indicator, param, plot, color
from indie.math import divide


@indicator('ALMA', overlay_main_pane=True)  # Arnaud Legoux Moving Average
@param.int('window_size', default=9, min=1, title='Window Size')
@param.float('offset', default=0.85)
@param.float('sigma', default=6.0, min=0.001)
@plot.line(color=color.BLUE)
def Main(self, window_size, offset, sigma):
    m = offset * (window_size - 1)
    s = window_size / sigma
    sum, norm = 0.0, 0.0
    for i in range(window_size):
        weight = exp(-1 * pow(i - m, 2) / (2 * pow(s, 2)))
        norm += weight
        sum += self.close[window_size - i - 1] * weight
    return divide(sum, norm)
Aroon
AroonAroon
sub-paneformat: PRICEblueyellow@plot.line
length=14
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color
from indie.algorithms import SinceLowest, SinceHighest


@indicator('Aroon', format=format.PRICE)  # TODO: format=format.PERCENT
@param.int('length', default=14, min=1)
@plot.line(color=color.BLUE, title='Aroon Down')
@plot.line(color=color.YELLOW, title='Aroon Up')
def Main(self, length):
    lo_offset = SinceLowest.new(self.low, length)
    hi_offset = SinceHighest.new(self.high, length)

    down = 100 * (length - lo_offset[0]) / length
    up = 100 * (length - hi_offset[0]) / length

    return down, up
Average Day Range
Average Day RangeADR
sub-paneblue@plot.line
length=14
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, plot, color
from indie.algorithms import Sma


@indicator('ADR')  # Average Day Range
@param.int('length', default=14, min=1)
@plot.line(color=color.BLUE)
def Main(self, length):
    sma_high = Sma.new(self.high, length)
    sma_low = Sma.new(self.low, length)
    return sma_high[0] - sma_low[0]
Average Directional Index
Average Directional IndexADX
sub-paneformat: PRICEred@plot.line
adx_len=14 · di_len=14
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color
from indie.algorithms import Adx


@indicator('ADX', format=format.PRICE)  # Average Directional Index
@param.int('adx_len', default=14, min=1, title='ADX Smoothing')
@param.int('di_len', default=14, min=1, title='DI Length')
@plot.line(color=color.RED)
def Main(self, adx_len, di_len):
    _, adx, _ = Adx.new(adx_len, di_len)
    return adx[0]
Average True Range
Average True RangeATR
sub-panered@plot.line
length=14 · smoothing='RMA'
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, plot, color
from indie.algorithms import Atr


@indicator('ATR')  # Average True Range
@param.int('length', default=14, min=1)
@param.str('smoothing', default='RMA', options=['RMA', 'SMA', 'EMA', 'WMA'], title='Smoothing')
@plot.line(color=color.RED)
def Main(self, length, smoothing):
    return Atr.new(length, smoothing)[0]
Awesome Oscillator
Awesome OscillatorAO
sub-panemaroon@plot.columns
Indie исходник
# indie:lang_version = 5
from indie import indicator, plot, color, MutSeriesF
from indie.algorithms import Sma


# TODO: Support palettes of colors in indicators
@indicator('AO')  # Awesome Oscillator
@plot.columns()
def Main(self):
    ao = MutSeriesF.new(Sma.new(self.hl2, 5)[0] - Sma.new(self.hl2, 34)[0])
    d = ao[0] - ao[1]
    c = color.MAROON if d = 0 else 0
    m2 = 0.0 if momm >= 0 else -momm

    sm1 = Sum.new(MutSeriesF.new(m1), length)[0]
    sm2 = Sum.new(MutSeriesF.new(m2), length)[0]

    return 100 * divide(sm1 - sm2, sm1 + sm2)
Balance of Power
Balance of Powerскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Bollinger Bands
Bollinger Bandsскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Bollinger Bands %B
Bollinger Bands %Bскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Bollinger Bands Width
Bollinger Bands Widthскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Bull Bear Power
Bull Bear Powerскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Chaikin Money Flow
Chaikin Money Flowскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Chaikin Oscillator
Chaikin Oscillatorскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Chande Kroll Stop
Chande Kroll Stopскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Chande Momentum Oscillator
Chande Momentum Oscillatorскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Chop Zone
Chop ZoneChop Zone
sub-paneformat: PRICEaquablackbluegray@plot.columns@plot.line
len_rsi=3 · len_up_down=2 · len_roc=100
Indie исходник
# indie:lang_version = 5
from math import nan, isnan, atan, pi
from indie import indicator, format, plot, color, Color
from indie.algorithms import Highest, Lowest, Ema
from indie.math import divide


@indicator('Chop Zone', format=format.PRICE, precision=0)
@plot.columns()
def Main(self):
    color_turquoise = color.rgba(38, 198, 218)
    color_dark_green = color.rgba(67, 160, 71)
    color_pale_green = color.rgba(165, 214, 167)
    color_lime = color.rgba(0, 150, 136)
    color_dark_red = color.rgba(213, 0, 0)
    color_red = color.rgba(233, 30, 99)
    color_orange = color.rgba(255, 109, 0)
    color_light_orange = color.rgba(255, 183, 77)
    color_yellow = color.rgba(253, 216, 53)

    periods = 30
    highest_high = Highest.new(self.high, periods)[0]
    lowest_low = Lowest.new(self.low, periods)[0]
    span = divide(25, highest_high - lowest_low) * lowest_low

    ema34 = Ema.new(self.close, 34)
    y2_ema34 = divide(ema34[1] - ema34[0], self.hlc3[0]) * span

    ema_angle_1 = nan
    if not isnan(y2_ema34):
        ema_angle_1 = round(180 * atan(abs(y2_ema34)) / pi)
    ema_angle = -ema_angle_1 if y2_ema34 > 0 else ema_angle_1

    chop_zone_color = color.BLACK  # default value
    if ema_angle >= 5:
        chop_zone_color = color_turquoise
    elif ema_angle >= 3.57:
        chop_zone_color = color_dark_green
    elif ema_angle >= 2.14:
        chop_zone_color = color_pale_green
    elif ema_angle >= 0.71:
        chop_zone_color = color_lime
    elif ema_angle  -0.71 and ema_angle  float:
    return 0 if isnan(val) else val


@indicator('CRSI', format=format.PRICE)  # Connors RSI
@param.int('len_rsi', default=3, min=1, title='RSI Length')
@param.int('len_up_down', default=2, min=1, title='UpDown Length')
@param.int('len_roc', default=100, min=1, title='ROC Length')
@band(30, 70, line_color=color.GRAY, fill_color=color.AQUA(0.1), title='Background')
@level(50, line_color=color.GRAY(0.5), title='Middle Band')
@plot.line(color=color.BLUE, title='CRSI')
def Main(self, len_rsi, len_up_down, len_roc):
    is_equal = self.close[0] == self.close[1]
    is_growing = self.close[0] > self.close[1]
    ud = MutSeriesF.new(0)  # why not mut_series(init=0)
    if is_growing:
        if nan_to_zero(ud[1]) = 0:
            ud[0] = -1
        else:
            ud[0] = nan_to_zero(ud[1]) - 1

    rsi = Rsi.new(self.close, len_rsi)[0]
    ud_rsi = Rsi.new(ud, len_up_down)[0]
    pr = PercentRank.new(Roc.new(self.close, 1), len_roc)[0]
    return (rsi + ud_rsi + pr) / 3
Choppiness Index
Choppiness Indexскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Commodity Channel Index
Commodity Channel Indexскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Connors RSI
Connors RSIскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Coppock Curve
Coppock CurveCoppock
sub-paneblue@plot.line
wma_length=10 · long_roc_length=14 · short_roc_length=11
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, plot, color, MutSeriesF
from indie.algorithms import Roc, Wma


@indicator('Coppock')  # Coppock Curve
@param.int('wma_length', default=10, min=1, title='WMA Length')
@param.int('long_roc_length', default=14, min=1, title='Long RoC Length')
@param.int('short_roc_length', default=11, min=1, title='Short RoC Length')
@plot.line(color=color.BLUE)
def Main(self, wma_length, long_roc_length, short_roc_length):
    l_roc = Roc.new(self.close, long_roc_length)[0]
    s_roc = Roc.new(self.close, short_roc_length)[0]
    curve = Wma.new(MutSeriesF.new(l_roc + s_roc), wma_length)[0]
    return curve
Correlation Coefficient
Correlation CoefficientCC
sub-paneformat: PRICEbluegray@plot.line
exchange='NASDAQ' · ticker='GOOG' · source=source.CLOSE · length=20
Indie исходник
# indie:lang_version = 5
from indie import indicator, MainContext, sec_context, format, param, source, param_ref, level, color, plot
from indie.algorithms import Corr


@sec_context
@param_ref('source')
def SecContext(self, source):
    return source[0]


@indicator('CC', format=format.PRICE)  # Correlation Coefficient
@param.str('exchange', default='NASDAQ')
@param.str('ticker', default='GOOG')
@param.source('source', default=source.CLOSE)
@param.int('length', default=20, min=1)
@level(1, line_color=color.GRAY)
@level(0)
@level(-1, line_color=color.GRAY)
@plot.line(color=color.BLUE, title='Correlation')
class Main(MainContext):
    def __init__(self, exchange, ticker):
        self.ctx_other = self.calc_on(SecContext, exchange=exchange, ticker=ticker)

    def calc(self, source, length):
        corr = Corr.new(source, self.ctx_other, length)
        return corr[0]
Detrended Price Oscillator
Detrended Price OscillatorDPO
sub-paneformat: PRICEgraygreen@plot.line
length=21 · centered=False
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, level, color, plot
from indie.algorithms import Sma


@indicator('DPO', format=format.PRICE)  # Detrended Price Oscillator
@param.int('length', default=21, min=1)
@param.bool('centered', default=False, title='Centered')
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.GREEN, title='Detrended Price Oscillator')
def Main(self, length, centered):
    bars_back = length // 2 + 1
    ma = Sma.new(self.close, length)
    dpo = self.close[bars_back] - ma[0] if centered else self.close[0] - ma[bars_back]
    return plot.Line(dpo, offset=-bars_back if centered else 0)
Directional Movement Index
Directional Movement IndexDMI
sub-paneformat: PRICEbluemaroonred@plot.line
adx_len=14 · di_len=14
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color
from indie.algorithms import Adx


@indicator('DMI', format=format.PRICE, precision=4)  # Directional Movement Index
@param.int('adx_len', default=14, min=1, title='ADX Smoothing')
@param.int('di_len', default=14, min=1, title='DI Length')
@plot.line(color=color.MAROON, title='-DI')
@plot.line(color=color.RED, title='ADX')
@plot.line(color=color.BLUE, title='+DI')
def Main(self, adx_len, di_len):
    minus, adx, plus = Adx.new(adx_len, di_len)
    return minus[0], adx[0], plus[0]
Donchian Channels
Donchian ChannelsDC
overlayaquabluered@plot.fill@plot.line
length=20
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, plot, color
from indie.algorithms import Lowest, Highest, Donchian


@indicator('DC', overlay_main_pane=True)  # Donchian Channels
@param.int('length', default=20, min=1)
@plot.line('lower', color=color.BLUE, title='Lower')
@plot.line(color=color.RED, title='Basis')
@plot.line('upper', color=color.BLUE, title='Upper')
@plot.fill('lower', 'upper', color=color.AQUA(0.05), title='Background')
def Main(self, length):
    lower = Lowest.new(self.low, length)
    upper = Highest.new(self.high, length)
    basis = Donchian.new(length)
    return lower[0], basis[0], upper[0], plot.Fill()
Double EMA
Double EMADEMA
overlaygreen@plot.line
length=9 · src=source.CLOSE
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Ema


@indicator('DEMA', overlay_main_pane=True)  # Double EMA
@param.int('length', default=9, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@plot.line(color=color.GREEN)
def Main(self, length, src):
    ema1 = Ema.new(src, length)
    ema2 = Ema.new(ema1, length)
    return 2 * ema1[0] - ema2[0]
Ease of Movement
Ease of MovementEOM
sub-paneformat: VOLUMEgreen@plot.line
length=14 · divisor=10000
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color, MutSeriesF
from indie.algorithms import Change, Sma
from indie.math import divide


@indicator('EOM', format=format.VOLUME)  # Ease of Movement
@param.int('length', default=14, min=1)
@param.int('divisor', default=10000, min=1)
@plot.line(color=color.GREEN)
def Main(self, length, divisor):
    eom = divisor * Change.new(self.hl2)[0] * divide(self.high[0] - self.low[0], self.volume[0])
    return Sma.new(MutSeriesF.new(eom), length)[0]
Elders Force Index
Elders Force IndexEFI
sub-paneformat: VOLUMEgrayred@plot.line
length=13
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, level, color, plot, MutSeriesF
from indie.algorithms import Change, Ema


@indicator('EFI', format=format.VOLUME)  # Elders Force Index
@param.int('length', default=13, min=1)
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.RED, title='Elders Force Index')
def Main(self, length):
    efi = Change.new(self.close)[0] * self.volume[0]
    return Ema.new(MutSeriesF.new(efi), length)[0]
Envelope
EnvelopeEnv
overlayaquabluered@plot.fill@plot.line
length=20 · percent=10.0 · src=source.CLOSE · exponential=False
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Ema, Sma


@indicator('Env', overlay_main_pane=True)  # Envelope
@param.int('length', default=20, min=1)
@param.float('percent', default=10.0)
@param.source('src', default=source.CLOSE, title='Source')
@param.bool('exponential', default=False)
@plot.line('lower', color=color.BLUE, title='Lower')
@plot.line('basis', color=color.RED, title='Basis')
@plot.line('upper', color=color.BLUE, title='Upper')
@plot.fill('lower', 'upper', color=color.AQUA(0.05), title='Background')
def Main(self, length, percent, src, exponential):
    basis = 0.0
    if exponential:
        basis = Ema.new(src, length)[0]
    else:
        basis = Sma.new(src, length)[0]
    k = percent / 100.0
    upper = basis * (1 + k)
    lower = basis * (1 - k)
    return lower, basis, upper, plot.Fill()
Exponential Moving Average
Exponential Moving AverageEMA
overlayblue@plot.line
length=9 · src=source.CLOSE · offset=0
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Ema


@indicator('EMA', overlay_main_pane=True)  # Moving Average Exponential
@param.int('length', default=9, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@param.int('offset', default=0, min=-500, max=500, title='Offset')
@plot.line(color=color.BLUE)
def Main(self, length, src, offset):
    ema = Ema.new(src, length)
    return plot.Line(ema[0], offset=offset)
    # TODO: implement smoothing when display.none is supported
Fisher Transform
Fisher TransformIchimoku
overlaybluegreenredtransparent@plot.fill@plot.line
conversion_periods=9 · base_periods=26 · lagging_span_2_periods=52 · displacement=26
Indie исходник
# indie:lang_version = 5
from math import isnan, nan, log
from indie import indicator, format, param, level, color, plot, MutSeriesF
from indie.algorithms import Highest, Lowest
from indie.math import divide


def nan_to_zero(val: float) -> float:
    return 0 if isnan(val) else val


def round(val: float) -> float:
    if val > 0.99:
        val = 0.999
    elif val  float:
    # TODO: implement @algorithm avg with variable number of arguments in indie.algorithms
    return (price1[0] + price2[0]) / 2


# TODO: Add palette of colors
@indicator('Ichimoku', overlay_main_pane=True)  # Ichimoku Cloud
@param.int('conversion_periods', default=9, min=1, title='Conversion Line Length')
@param.int('base_periods', default=26, min=1, title='Base Line Length')
@param.int('lagging_span_2_periods', default=52, min=1, title='Leading Span B Length')
@param.int('displacement', default=26, min=1, title='Lagging Span')
@plot.line(color=color.BLUE, title='Conversion Line')
@plot.line(color=color.RED, title='Base Line')
@plot.line(color=color.GREEN, title='Lagging Span')
@plot.line('ls_a', color=color.GREEN(0.6), title='Leading Span A')
@plot.line('ls_b', color=color.RED(0.6), title='Leading Span B')
@plot.fill('ls_a', 'ls_b', title='Background Up', color=color.GREEN(0.1))
@plot.fill('ls_a', 'ls_b', title='Background Down', color=color.RED(0.1))
def Main(self, conversion_periods, base_periods, lagging_span_2_periods, displacement):
    conversion_line = Donchian.new(conversion_periods)
    base_line = Donchian.new(base_periods)
    lead_line1 = mean(conversion_line, base_line)
    lead_line2 = Donchian.new(lagging_span_2_periods)[0]

    fill_up_color = None if lead_line1 > lead_line2 else color.TRANSPARENT
    fill_down_color = color.TRANSPARENT if lead_line1 > lead_line2 else None
    return (
        conversion_line[0],
        base_line[0],
        plot.Line(self.close[0], offset=-displacement + 1),
        plot.Line(lead_line1, offset=displacement - 1),
        plot.Line(lead_line2, offset=displacement - 1),
        plot.Fill(offset=displacement - 1, color=fill_up_color),
        plot.Fill(offset=displacement - 1, color=fill_down_color),
    )
Hull Moving Average
Hull Moving Averageскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Ichimoku Cloud
Ichimoku Cloudскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Keltner Channels
Keltner ChannelsKC
overlayaquablue@plot.fill@plot.line
length=20 · mult=2.0 · src=source.CLOSE · exp=True · bands_style='Average True Range' · atr_length=10
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color, MutSeriesF
from indie.algorithms import Ma, Tr, Atr, Rma


@indicator('KC', overlay_main_pane=True)  # Keltner Channels
@param.int('length', default=20, min=1)
@param.float('mult', default=2.0, title='Multiplier')
@param.source('src', default=source.CLOSE, title='Source')
@param.bool('exp', default=True, title='Use Exponential MA')
@param.str('bands_style', default='Average True Range', title='Bands Style',
           options=['Average True Range', 'True Range', 'Range'])
@param.int('atr_length', default=10, min=1, title='ATR Length')
@plot.line('lower', color=color.BLUE, title='Lower')
@plot.line(color=color.BLUE, title='Basis')
@plot.line('upper', color=color.BLUE, title='Upper')
@plot.fill('lower', 'upper', color=color.AQUA(0.05), title='Background')
def Main(self, length, mult, src, exp, bands_style, atr_length):
    ma = Ma.new(src, length, 'EMA' if exp else 'SMA')[0]
    range_ma = 0.0
    if bands_style == 'True Range':
        range_ma = Tr.new(True)[0]
    elif bands_style == 'Average True Range':
        range_ma = Atr.new(atr_length)[0]
    else:  # bands_style == 'Range'
        range_ma = Rma.new(MutSeriesF.new(self.high[0] - self.low[0]), length)[0]
    upper = ma + range_ma * mult
    lower = ma - range_ma * mult
    return lower, ma, upper, plot.Fill()
Klinger Oscillator
Klinger OscillatorKlinger Osc
sub-paneformat: VOLUMEbluegreen@plot.line
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, plot, color, MutSeriesF
from indie.algorithms import Change, Ema


@indicator('Klinger Osc', format=format.VOLUME)  # Klinger Oscillator
@plot.line(color=color.BLUE, title='KO')
@plot.line(color=color.GREEN, title='Signal')
def Main(self):
    sv = MutSeriesF.new(self.volume[0] if Change.new(self.hlc3)[0] >= 0 else -self.volume[0])
    kvo = MutSeriesF.new(Ema.new(sv, 34)[0] - Ema.new(sv, 55)[0])
    sig = Ema.new(kvo, 13)
    return kvo[0], sig[0]
Know Sure Thing
Know Sure ThingKST
sub-paneformat: PRICEgraygreenred@plot.line
roc_len1=10 · roc_len2=15 · roc_len3=20 · roc_len4=30 · sma_len1=10 · sma_len2=10 · sma_len3=10 · sma_len4=15 · sig_len=9
Indie исходник
# indie:lang_version = 5
from indie import algorithm, MutSeriesF, SeriesF, indicator, format, param, level, color, plot
from indie.algorithms import Sma, Roc


@algorithm
def SmaRoc(self, roc_len: int, sma_len: int) -> SeriesF:
    return Sma.new(Roc.new(self.ctx.close, roc_len), sma_len)


@indicator('KST', format=format.PRICE, precision=4)  # Know Sure Thing
@param.int('roc_len1', default=10, min=1, title='ROC Length #1')
@param.int('roc_len2', default=15, min=1, title='ROC Length #2')
@param.int('roc_len3', default=20, min=1, title='ROC Length #3')
@param.int('roc_len4', default=30, min=1, title='ROC Length #4')
@param.int('sma_len1', default=10, min=1, title='SMA Length #1')
@param.int('sma_len2', default=10, min=1, title='SMA Length #2')
@param.int('sma_len3', default=10, min=1, title='SMA Length #3')
@param.int('sma_len4', default=15, min=1, title='SMA Length #4')
@param.int('sig_len', default=9, min=1, title='Signal Line Length')
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.GREEN, title='KST')
@plot.line(color=color.RED, title='Signal')
def Main(self, roc_len1, roc_len2, roc_len3, roc_len4, sma_len1, sma_len2, sma_len3, sma_len4, sig_len):
    kst = 1 * SmaRoc.new(roc_len1, sma_len1)[0] + \
          2 * SmaRoc.new(roc_len2, sma_len2)[0] + \
          3 * SmaRoc.new(roc_len3, sma_len3)[0] + \
          4 * SmaRoc.new(roc_len4, sma_len4)[0]
    sig = Sma.new(MutSeriesF.new(kst), sig_len)[0]
    return kst, sig
Least Squares Moving Average
Least Squares Moving AverageLSMA
overlay@plot.line
length=25 · offset=0 · src=source.CLOSE
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot
from indie.algorithms import LinReg


@indicator('LSMA', overlay_main_pane=True)  # Least Squares Moving Average
@param.int('length', default=25, min=1)
@param.int('offset', default=0)
@param.source('src', default=source.CLOSE)
@plot.line(title='LSMA')
def Main(self, length, offset, src):
    return LinReg.new(src, length, offset)[0]
Linear Regression Channel
Linear Regression ChannelLinear Regression Channel
overlayblueredtransparent
length_input=100 · source_input=source.CLOSE · use_upper_dev_input=True · upper_mult_input=2.0 · use_lower_dev_input=True · lower_mult_input=2.0 · show_pearson_input=True · extend_left_input=False · extend_right_input=True
Indie исходник
# indie:lang_version = 5
from math import sqrt, isnan, nan
from indie import indicator, param, source, color, MainContext, SeriesF
from indie.drawings import LineSegment, LabelAbs, AbsolutePosition, extend_type, callout_position
from indie.math import divide


def calc_slope(src: SeriesF, length: int) -> tuple[float, float, float]:
    sum_x = 0.0
    sum_y = 0.0
    sum_x_sqr = 0.0
    sum_xy = 0.0

    for i in range(length):
        val = src[i]
        per = i + 1.0
        sum_x += per
        sum_y += val
        sum_x_sqr += per * per
        sum_xy += val * per

    slope = divide(length * sum_xy - sum_x * sum_y, length * sum_x_sqr - sum_x * sum_x)
    average = sum_y / length
    intercept = average - slope * sum_x / length + slope

    return slope, average, intercept


def calc_dev(slope: float, average: float, intercept: float,
             src: SeriesF, high: SeriesF, low: SeriesF, length: int) -> tuple[float, float, float, float]:
    up_dev = 0.0
    dn_dev = 0.0
    std_dev_acc = 0.0
    dsxx = 0.0
    dsyy = 0.0
    dsxy = 0.0
    periods = length - 1
    day_y = intercept + slope * periods / 2
    val = intercept

    for j in range(length):
        price = high[j] - val
        if price > up_dev:
            up_dev = price

        price = val - low[j]
        if price > dn_dev:
            dn_dev = price

        price = src[j]
        dxt = price - average
        dyt = val - day_y
        price -= val
        std_dev_acc += price * price
        dsxx += dxt * dxt
        dsyy += dyt * dyt
        dsxy += dxt * dyt
        val += slope

    std_dev = sqrt(std_dev_acc / (periods if periods != 0 else 1))
    pearson_r = divide(dsxy, sqrt(dsxx * dsyy), 0.0)

    return std_dev, pearson_r, up_dev, dn_dev


@indicator('Linear Regression Channel', overlay_main_pane=True)
@param.int('length_input', default=100, min=1, max=5000, title='Length')
@param.source('source_input', default=source.CLOSE, title='Source')
@param.bool('use_upper_dev_input', default=True, title='Upper Deviation')
@param.float('upper_mult_input', default=2.0, title='Upper Multiplier')
@param.bool('use_lower_dev_input', default=True, title='Lower Deviation')
@param.float('lower_mult_input', default=2.0, title='Lower Multiplier')
@param.bool('show_pearson_input', default=True, title="Show Pearson's R")
@param.bool('extend_left_input', default=False, title='Extend Lines Left')
@param.bool('extend_right_input', default=True, title='Extend Lines Right')
class Main(MainContext):
    def __init__(self, extend_left_input, extend_right_input):
        # Determine extend type
        extend_style = extend_type.NONE
        if extend_left_input and extend_right_input:
            extend_style = extend_type.BOTH
        elif extend_left_input:
            extend_style = extend_type.LEFT
        elif extend_right_input:
            extend_style = extend_type.RIGHT

        self._base_line = LineSegment(
            AbsolutePosition(0, 0),
            AbsolutePosition(0, 0),
            extend_type=extend_style,
            color=color.RED,
        )
        self._upper_line = LineSegment(
            AbsolutePosition(0, 0),
            AbsolutePosition(0, 0),
            extend_type=extend_style,
            color=color.BLUE,
        )
        self._lower_line = LineSegment(
            AbsolutePosition(0, 0),
            AbsolutePosition(0, 0),
            extend_type=extend_style,
            color=color.BLUE,
        )
        self._pearson_label = LabelAbs(
            '',
            AbsolutePosition(0, 0),
            text_color=color.BLUE,
            callout_position=callout_position.BOTTOM_LEFT,
            bg_color=color.TRANSPARENT,
        )

    def calc(self,
             length_input, source_input,
             use_upper_dev_input, upper_mult_input,
             use_lower_dev_input, lower_mult_input,
             show_pearson_input):
        if not self.is_last_bar:
            return

        # Calculate regression parameters
        slope, average, intercept = calc_slope(source_input, length_input)

        if isnan(slope):
            return

        start_price = intercept + slope * (length_input - 1)
        end_price = intercept

        # Calculate deviations
        std_dev, pearson_r, up_dev, dn_dev = calc_dev(slope, average, intercept, source_input,
                                                      self.high, self.low, length_input)

        upper_start_price = start_price + (upper_mult_input * std_dev if use_upper_dev_input else up_dev)
        upper_end_price = end_price + (upper_mult_input * std_dev if use_upper_dev_input else up_dev)
        lower_start_price = start_price + (-lower_mult_input * std_dev if use_lower_dev_input else -dn_dev)
        lower_end_price = end_price + (-lower_mult_input * std_dev if use_lower_dev_input else -dn_dev)

        # Draw lines
        if not isnan(start_price) and not isnan(upper_start_price) and not isnan(lower_start_price):
            start_x = self.time[length_input - 1]
            end_x = self.time[0]

            # Update base line coordinates
            self._base_line.point_a = AbsolutePosition(start_x, start_price)
            self._base_line.point_b = AbsolutePosition(end_x, end_price)
            self.chart.draw(self._base_line)

            # Update upper line coordinates
            self._upper_line.point_a = AbsolutePosition(start_x, upper_start_price)
            self._upper_line.point_b = AbsolutePosition(end_x, upper_end_price)
            self.chart.draw(self._upper_line)

            # Update lower line coordinates
            self._lower_line.point_a = AbsolutePosition(start_x, lower_start_price)
            self._lower_line.point_b = AbsolutePosition(end_x, lower_end_price)
            self.chart.draw(self._lower_line)

            # Update Pearson's R label text and position
            if show_pearson_input and not isnan(pearson_r):
                self._pearson_label.text = str(round(pearson_r, 8))
                self._pearson_label.position = AbsolutePosition(start_x, lower_start_price)
                self.chart.draw(self._pearson_label)
MA Cross
MA CrossMA Cross
overlaybluegreenmaroon@plot.line@plot.marker
short_len=9 · long_len=21
Indie исходник
# indie:lang_version = 5
from math import nan
from indie import indicator, param, plot, color
from indie.algorithms import Sma
from indie.math import cross


@indicator('MA Cross', overlay_main_pane=True)  # MA Cross
@param.int('short_len', default=9, min=1, title='Short MA Length')
@param.int('long_len', default=21, min=1, title='Long MA Length')
@plot.line(color=color.MAROON, title='Short')
@plot.line(color=color.GREEN, title='Long')
@plot.marker(color=color.BLUE, style=plot.marker_style.CROSS,
             position=plot.marker_position.CENTER, size=7, title='MA Cross')
def Main(self, short_len, long_len):
    short = Sma.new(self.close, short_len)
    long = Sma.new(self.close, long_len)
    return short[0], long[0], short[0] if cross(short, long) else nan
Mass Index
Mass Indexскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

McGinley Dynamic
McGinley Dynamicскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Median
Medianскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Momentum
MomentumMom
sub-paneblue@plot.line
length=10 · src=source.CLOSE
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Change


@indicator('Mom')  # Momentum
@param.int('length', default=10, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@plot.line(color=color.BLUE)
def Main(self, length, src):
    return Change.new(src, length)[0]
Money Flow Index
Money Flow IndexMFI
sub-paneformat: PRICEgraypurple@plot.line
length=14 · src=source.HLC3
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, source, band, color, level, plot
from indie.algorithms import Mfi


@indicator('MFI', format=format.PRICE)  # Money Flow Index
@param.int('length', default=14, min=1)
@param.source('src', default=source.HLC3, title='Source')
@band(20, 80, line_color=color.GRAY, fill_color=color.PURPLE(0.1), title='Background')
@level(50, line_color=color.GRAY, title='Middle Band')
@plot.line(color=color.PURPLE, title='MF')
def Main(self, length, src):
    return Mfi.new(src, length)[0]
Moving Average Convergence Divergence
Moving Average Convergence DivergenceMACD
sub-paneblackbluefuchsiagraymaroon@plot.columns@plot.line
fast_length=12 · slow_length=26 · src=source.CLOSE · signal_length=9 · sma_source='EMA' · sma_signal='EMA'
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, level, color, plot, Color
from indie.algorithms import Macd


@indicator('MACD')  # Moving Average Convergence Divergence
@param.int('fast_length', default=12, min=1, title='Fast Length')
@param.int('slow_length', default=26, min=1, title='Slow Length')
@param.source('src', default=source.CLOSE, title='Source')
@param.int('signal_length', default=9, min=1, max=50, title='Signal Smoothing')
@param.str('sma_source', default='EMA', title='Oscillator MA Type', options=['SMA', 'EMA'])
@param.str('sma_signal', default='EMA', title='Signal Line MA Type', options=['SMA', 'EMA'])
@level(0, line_color=color.GRAY(0.5))
@plot.columns(title='Histogram')
@plot.line(color=color.BLUE, title='MACD')
@plot.line(color=color.MAROON, title='Signal')
def Main(self, fast_length, slow_length, src, signal_length, sma_source, sma_signal):
    macd, signal, hist = Macd.new(src, fast_length, slow_length, signal_length, sma_source, sma_signal)

    hist_color = color.BLACK  # default value
    if hist[0] >= 0:
        if hist[1]  median_ema else color.FUCHSIA(0.9)
    return (
        median_ema,
        median - atr,
        median,
        median + atr,
        plot.Fill(color=fill_color),
    )
Moving Average Ribbon
Moving Average RibbonMA Ribbon
overlay@plot.line
ma1=True · ma1_type='SMA' · ma1_source=source.CLOSE · ma1_length=20 · ma2=True · ma2_type='SMA' · ma2_source=source.CLOSE · ma2_length=50 · ma3=True · ma3_type='SMA' · ma3_source=source.CLOSE · ma3_length=100 · ma4=True · ma4_type='SMA' · ma4_source=source.CLOSE · ma4_length=200
Indie исходник
# indie:lang_version = 5
from math import nan
from indie import indicator, param, source, plot, color
from indie.algorithms import Ma


@indicator('MA Ribbon', overlay_main_pane=True)  # Moving Average Ribbon
@param.bool('ma1', default=True, title='Show MA №1')
@param.str('ma1_type', default='SMA', title='MA №1 Type', options=['SMA', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'])
@param.source('ma1_source', default=source.CLOSE, title='MA №1 Source')
@param.int('ma1_length', default=20, min=1, title='MA №1 Length')
@param.bool('ma2', default=True, title='Show MA №2')
@param.str('ma2_type', default='SMA', title='MA №2 Type', options=['SMA', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'])
@param.source('ma2_source', default=source.CLOSE, title='MA №2 Source')
@param.int('ma2_length', default=50, min=1, title='MA №2 Length')
@param.bool('ma3', default=True, title='Show MA №3')
@param.str('ma3_type', default='SMA', title='MA №3 Type', options=['SMA', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'])
@param.source('ma3_source', default=source.CLOSE, title='MA №3 Source')
@param.int('ma3_length', default=100, min=1, title='MA №3 Length')
@param.bool('ma4', default=True, title='Show MA №4')
@param.str('ma4_type', default='SMA', title='MA №4 Type', options=['SMA', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'])
@param.source('ma4_source', default=source.CLOSE, title='MA №4 Source')
@param.int('ma4_length', default=200, min=1, title='MA №4 Length')
@plot.line(color=color.rgba(246, 195, 9), title='MA №1')
@plot.line(color=color.rgba(251, 152, 0), title='MA №2')
@plot.line(color=color.rgba(251, 101, 0), title='MA №3')
@plot.line(color=color.rgba(246, 12, 12), title='MA №4')
def Main(self, ma1, ma1_type, ma1_source, ma1_length, \
         ma2, ma2_type, ma2_source, ma2_length, \
         ma3, ma3_type, ma3_source, ma3_length, \
         ma4, ma4_type, ma4_source, ma4_length):
    ma1_val = Ma.new(ma1_source, ma1_length, ma1_type)[0]
    ma2_val = Ma.new(ma2_source, ma2_length, ma2_type)[0]
    ma3_val = Ma.new(ma3_source, ma3_length, ma3_type)[0]
    ma4_val = Ma.new(ma4_source, ma4_length, ma4_type)[0]
    return ma1_val if ma1 else nan, \
           ma2_val if ma2 else nan, \
           ma3_val if ma3 else nan, \
           ma4_val if ma4 else nan
Moving Average Weighted
Moving Average WeightedWMA
overlayblue@plot.line
length=9 · src=source.CLOSE · offset=0
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Wma


@indicator('WMA', overlay_main_pane=True)  # Weighted Moving Average
@param.int('length', default=9, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@param.int('offset', default=0, min=-500, max=500, title='Offset')
@plot.line(color=color.BLUE)
def Main(self, length, src, offset):
    result = Wma.new(src, length)
    return plot.Line(result[0], offset=offset)
Net Volume
Net VolumeNet Volume
sub-paneformat: VOLUMEblue@plot.line
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, plot, color
from indie.algorithms import NetVolume


@indicator('Net Volume', format=format.VOLUME)
@plot.line(color=color.BLUE)
def Main(self):
    return NetVolume.new(self.close)[0]
On Balance Volume
On Balance VolumeOBV
sub-paneformat: VOLUMEblue@plot.line
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, plot, color
from indie.algorithms import CumSum, NanToZero, NetVolume


@indicator('OBV', format=format.VOLUME)  # On Balance Volume
@plot.line(color=color.BLUE)
def Main(self):
    return CumSum.new(NanToZero.new(NetVolume.new(self.close)))[0]
    # TODO: implement smoothing when display.none is supported
Parabolic Stop And Reverse
Parabolic Stop And ReverseSAR
overlayblue@plot.marker
start=0.02 · increment=0.02 · maximum=0.2
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, plot, color
from indie.algorithms import Sar


@indicator('SAR', overlay_main_pane=True)  # Parabolic Stop And Reverse
@param.float('start', default=0.02)
@param.float('increment', default=0.02)
@param.float('maximum', default=0.2)
@plot.marker(style=plot.marker_style.CROSS, color=color.BLUE, title='ParabolicSAR')
def Main(self, start, increment, maximum):
    return Sar.new(start, increment, maximum)[0]
Pivot Points High/Low
Pivot Points High/LowPivots High/Low
overlaygreenredtransparent
length_high_left=10 · length_low_left=10 · length_high_right=10 · length_low_right=10 · show_unconfirmed=True
Indie исходник
# indie:lang_version = 5
from math import isnan
from indie import indicator, param, Optional, MainContext, color
from indie.algorithms import PivotHighLow, SinceHighest, SinceLowest
from indie.drawings import LabelAbs, AbsolutePosition, callout_position


@indicator('Pivots High/Low', overlay_main_pane=True)
@param.int('length_high_left', default=10, min=1, title='High left length')
@param.int('length_low_left', default=10, min=1, title='Low left length')
@param.int('length_high_right', default=10, min=1, title='High right length')
@param.int('length_low_right', default=10, min=1, title='Low right length')
# Display potential pivot points in real-time before right-side confirmation
@param.bool('show_unconfirmed', default=True, title='Show potential pivot points')
class Main(MainContext):
    def __init__(self):
        none_label: Optional[LabelAbs] = None
        self._prev_high_pivot = self.new_var(none_label)
        self._prev_high_pivot_bar_index = self.new_var(0)
        self._prev_low_pivot = self.new_var(none_label)
        self._prev_low_pivot_bar_index = self.new_var(0)

    def calc(self, length_high_left, length_low_left, length_high_right, length_low_right, show_unconfirmed):
        if show_unconfirmed:
            sh = SinceHighest.new(self.high, length_high_left + 1)
            sl = SinceLowest.new(self.low, length_low_left + 1)

            if sh[0] == 0:
                # we have a new high pivot candidate
                new_h_pivot_candidate_y = self.high[0]
                hp_opt: Optional[LabelAbs] = self._prev_high_pivot.get()
                if hp_opt is not None:  # we have a previous high pivot, must decide which one is better
                    if self.bar_index - self._prev_high_pivot_bar_index.get() > length_high_right:
                        self._prev_high_pivot.set(None)  # prev_high_pivot is too far away in history, forget about it
                    elif hp_opt.value().position.price  length_low_right:
                        self._prev_low_pivot.set(None)  # prev_low_pivot is too far away in history, forget about it
                    elif lp_opt.value().position.price > new_l_pivot_candidate_y:
                        self._update_low_pivot()  # new pivot candidate is better, update previous pivot
                    # else new pivot candidate is worse, keep the previous pivot
                if self._prev_low_pivot.get() is None:
                    self._update_low_pivot()  # create new low pivot
        else:
            ph, _ = PivotHighLow.new(self.high, left_bars=length_high_left, right_bars=length_high_right)
            _, pl = PivotHighLow.new(self.low, left_bars=length_low_left, right_bars=length_low_right)

            if not isnan(ph[0]):
                new_pivot_y = self.high[length_high_right]
                new_pivot_x = self.time[length_high_right]
                self.chart.draw(self._create_new_label(new_pivot_x, new_pivot_y, is_high=True))  # create and draw

            if not isnan(pl[0]):
                new_pivot_y = self.low[length_low_right]
                new_pivot_x = self.time[length_low_right]
                self.chart.draw(self._create_new_label(new_pivot_x, new_pivot_y, is_high=False))  # create and draw

    def _update_high_pivot(self) -> None:
        new_pivot_y = self.high[0]
        new_pivot_x = self.time[0]
        new_pivot_bar_index = self.bar_index

        if self._prev_high_pivot.get() is None:  # create and draw
            self._prev_high_pivot.set(self._create_new_label(new_pivot_x, new_pivot_y, is_high=True))
        else:  # update field values and redraw
            hp = self._prev_high_pivot.get().value()
            hp.position = AbsolutePosition(new_pivot_x, new_pivot_y)
            hp.text = str(round(new_pivot_y, self.info.price_precision))

        self._prev_high_pivot_bar_index.set(new_pivot_bar_index)
        self.chart.draw(self._prev_high_pivot.get().value())

    def _update_low_pivot(self) -> None:
        new_pivot_y = self.low[0]
        new_pivot_x = self.time[0]
        new_pivot_bar_index = self.bar_index

        if self._prev_low_pivot.get() is None:  # create and draw
            self._prev_low_pivot.set(self._create_new_label(new_pivot_x, new_pivot_y, is_high=False))
        else:  # update field values and redraw
            lp = self._prev_low_pivot.get().value()
            lp.position = AbsolutePosition(new_pivot_x, new_pivot_y)
            lp.text = str(round(new_pivot_y, self.info.price_precision))

        self._prev_low_pivot_bar_index.set(new_pivot_bar_index)
        self.chart.draw(self._prev_low_pivot.get().value())

    def _create_new_label(self, time: float, price: float, is_high: bool) -> LabelAbs:
        text_color = color.GREEN if is_high else color.RED
        callout_pos = callout_position.TOP_RIGHT if is_high else callout_position.BOTTOM_LEFT
        return LabelAbs(
            str(round(price, self.info.price_precision)),
            AbsolutePosition(time, price),
            callout_position=callout_pos,
            bg_color=color.TRANSPARENT,
            text_color=text_color,
            font_size=11,
        )
Pivot Points Standard
Pivot Points StandardPivot Points Standard
overlayformat: PRICEgreenred@plot.line
higher_tf='Auto' · show_labels=True · show_prices=True · lookahead=True · levels_number=3 · length=10 · offset=0
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, sec_context, Optional, MainContext, MutSeriesF, Var, color, TimeFrame
from indie.drawings import LineSegment, LabelAbs, AbsolutePosition, callout_position
from math import isnan


@sec_context
def HigherTfMain(self):
    return self.high[1], self.low[1], self.close[1]

@indicator('Pivot Points Standard', overlay_main_pane=True)
@param.str('higher_tf', default='Auto',
           options=['Auto', '1m', '3m', '5m', '15m', '30m', '45m',
                    '1h', '2h', '3h', '4h',
                    '1D', '1W', '1M', '12M'])
@param.bool('show_labels', default=True, title='Show Labels')
@param.bool('show_prices', default=True, title='Show Prices')
@param.bool('lookahead', default=True, title='Lookahead on history bars')
@param.int('levels_number', default=3, min=1, max=3, title='Levels Number')
class Main(MainContext):
    def __init__(self, higher_tf, lookahead, levels_number):
        chosen_higher_tf = TimeFrame.from_str('12M')  # default value
        if higher_tf == 'Auto':
            if self.time_frame  SeriesF:
    return MutSeriesF.new(src[3] / 6 + src[2] / 3 + src[1] / 3 + src[0] / 6)


@indicator('RVGI', format=format.PRICE, precision=4)  # Relative Vigor Index
@param.int('length', default=10, min=1)
@param.int('offset', default=0, min=-500, max=500)
@plot.line(color=color.GREEN, title='RVGI')
@plot.line(color=color.RED, title='Signal')
def Main(self, length, offset):
    cl_op_sum = Sum.new(Swma.new(MutSeriesF.new(self.close[0] - self.open[0])), length)[0]
    hi_lo_sum = Sum.new(Swma.new(MutSeriesF.new(self.high[0] - self.low[0])), length)[0]
    rvi = divide(cl_op_sum, hi_lo_sum)
    sig = Swma.new(MutSeriesF.new(rvi))[0]
    return plot.Line(rvi, offset=offset), plot.Line(sig, offset=offset)
Price Oscillator
Price Oscillatorскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Price Volume Trend
Price Volume Trendскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Rate Of Change
Rate Of Changeскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Relative Strength Index
Relative Strength IndexRSI
sub-paneformat: PRICEgraygreenpurpleyellow@plot.fill@plot.line
rsi_length=14 · src=source.CLOSE · ma_type='SMA' · ma_length=14 · bb_mult=2.0
Indie исходник
# indie:lang_version = 5
from math import nan
from indie import indicator, format, param, source, band, color, level, plot
from indie.algorithms import Rsi, Ma, StdDev


@indicator('RSI', format=format.PRICE)  # Relative Strength Index
@param.int('rsi_length', default=14, title='RSI Length', min=1)
@param.source('src', default=source.CLOSE, title='Source')
@param.str('ma_type', default='SMA', title='MA Type',
           options=['SMA', 'Bollinger Bands', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'])
@param.int('ma_length', default=14, title='MA Length', min=1)
@param.float('bb_mult', default=2.0, title='BB StdDev', min=0.001, max=50)
@band(30, 70, line_color=color.GRAY, fill_color=color.PURPLE(0.1))
@level(50, line_color=color.GRAY(0.5))
@plot.line(color=color.YELLOW, title='RSI-based MA')
@plot.line(color=color.PURPLE, title='RSI')
@plot.line('bb_lower', color=color.GREEN, title='RSI Lower Band')
@plot.line('bb_upper', color=color.GREEN, title='RSI Upper Band')
@plot.fill('bb_lower', 'bb_upper', color=color.GREEN(0.1), title='Bollinger Bands Background Fill')
def Main(self, rsi_length, src, ma_type, ma_length, bb_mult):
    rsi = Rsi.new(src, rsi_length)

    is_bb = ma_type == 'Bollinger Bands'

    ma_algorithm = 'SMA' if is_bb else ma_type
    rsi_ma = Ma.new(rsi, ma_length, ma_algorithm)

    std_dev = StdDev.new(rsi, ma_length)
    bb_lower = rsi_ma[0] - std_dev[0] * bb_mult if is_bb else nan
    bb_upper = rsi_ma[0] + std_dev[0] * bb_mult if is_bb else nan
    return rsi_ma[0], rsi[0], bb_lower, bb_upper, plot.Fill()
Relative Vigor Index
Relative Vigor Indexскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Relative Volatility Index
Relative Volatility IndexRVI
sub-paneformat: PRICEgraygreenpurpleyellow@plot.fill@plot.line
length=10 · offset=0 · ma_type='SMA' · ma_length=14 · bb_mult=2.0
Indie исходник
# indie:lang_version = 5
from math import nan
from indie import indicator, format, param, band, color, level, plot, MutSeriesF
from indie.algorithms import StdDev, Ema, Change, Bb, Ma
from indie.math import divide


@indicator('RVI', format=format.PRICE)  # Relative Volatility Index
@param.int('length', default=10, min=1)
@param.int('offset', default=0, min=-500, max=500)
@param.str('ma_type', default='SMA', title='MA Type', options=['SMA', 'Bollinger Bands', 'EMA', 'SMMA (RMA)', 'WMA', 'VWMA'])
@param.int('ma_length', default=14, title='MA Length', min=1)
@param.float('bb_mult', default=2.0, title='BB StdDev', min=0.001, max=50)
@band(20, 80, line_color=color.GRAY, fill_color=color.PURPLE(0.1))
@level(50, line_color=color.GRAY(0.5))
@plot.line(color=color.YELLOW, title='RVI-based MA')
@plot.line(color=color.PURPLE, title='RVI')
@plot.line('bb_lower', color=color.GREEN, title='Lower Bollinger Band')
@plot.line('bb_upper', color=color.GREEN, title='Upper Bollinger Band')
@plot.fill('bb_lower', 'bb_upper', color=color.GREEN(0.1), title='Bollinger Bands Background Fill')
def Main(self, length, offset, ma_type, ma_length, bb_mult):
    dev = StdDev.new(self.close, length)[0]
    upper = Ema.new(MutSeriesF.new(0 if Change.new(self.close)[0]  0 else dev), 14)[0]
    rvi = MutSeriesF.new(100 * divide(upper, upper + lower))

    low_value, rvi_ma, high_value = nan, nan, nan
    if ma_type == 'Bollinger Bands':
        (bb_lower, bb_middle, bb_upper) = Bb.new(rvi, ma_length, bb_mult)
        low_value = bb_lower[0]
        rvi_ma = bb_middle[0]
        high_value = bb_upper[0]
    else:
        rvi_ma = Ma.new(rvi, ma_length, ma_type)[0]

    return (
        plot.Line(rvi_ma, offset=offset),
        plot.Line(rvi[0], offset=offset),
        low_value,
        high_value,
        plot.Fill(),
    )
Simple Moving Average
Simple Moving AverageSMA
overlayblue@plot.line
length=9 · src=source.CLOSE · offset=0
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Sma


@indicator('SMA', overlay_main_pane=True)  # Simple Moving Average
@param.int('length', default=9, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@param.int('offset', default=0, min=-500, max=500, title='Offset')
@plot.line(color=color.BLUE)
def Main(self, length, src, offset):
    sma = Sma.new(src, length)
    return plot.Line(sma[0], offset=offset)
    # TODO: implement smoothing when display.none is supported
SMI Ergodic Indicator
SMI Ergodic IndicatorSMII
sub-paneformat: PRICEbluemaroon@plot.line
long_len=20 · short_len=5 · signal_len=5
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color
from indie.algorithms import Tsi, Ema


@indicator('SMII', format=format.PRICE, precision=4)  # SMI Ergodic Indicator
@param.int('long_len', default=20, min=1, title='Long Length')
@param.int('short_len', default=5, min=1, title='Short Length')
@param.int('signal_len', default=5, min=1, title='Signal Length')
@plot.line(color=color.BLUE, title='SMI')
@plot.line(color=color.MAROON, title='Signal')
def Main(self, long_len, short_len, signal_len):
    tsi = Tsi.new(self.close, long_len, short_len)
    return tsi[0], Ema.new(tsi, signal_len)[0]
SMI Ergodic Oscillator
SMI Ergodic OscillatorSMIO
sub-paneformat: PRICEred@plot.histogram
long_len=20 · short_len=5 · signal_len=5
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color
from indie.algorithms import Tsi, Ema


@indicator('SMIO', format=format.PRICE, precision=4)  # SMI Ergodic Oscillator
@param.int('long_len', default=20, min=1, title='Long Length')
@param.int('short_len', default=5, min=1, title='Short Length')
@param.int('signal_len', default=5, min=1, title='Signal Length')
@plot.histogram(color=color.RED)
def Main(self, long_len, short_len, signal_len):
    tsi = Tsi.new(self.close, long_len, short_len)
    return tsi[0] - Ema.new(tsi, signal_len)[0]
Smoothed Moving Average
Smoothed Moving AverageSMMA
overlaypurple@plot.line
length=7 · src=source.CLOSE
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Rma


@indicator('SMMA', overlay_main_pane=True)  # Smoothed Moving Average
@param.int('length', default=7, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@plot.line(color=color.PURPLE)
def Main(self, length, src):
    return Rma.new(src, length)[0]
Stochastic Oscillator
Stochastic OscillatorStoch
sub-paneformat: PRICEaquabluegrayred@plot.line
k_length=14 · k_smoothing=1 · d_smoothing=3
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, band, color, level, plot
from indie.algorithms import Sma, Stoch


@indicator('Stoch', format=format.PRICE)  # Stochastic
@param.int('k_length', default=14, min=1, title='Fast K stochastic length')
@param.int('k_smoothing', default=1, min=1, title='Fast K stochastic smoothing')
@param.int('d_smoothing', default=3, min=1, title='Slow D stochastic smoothing')
@band(20, 80, fill_color=color.AQUA(0.1), line_color=color.GRAY, title='Background')
@level(50, line_color=color.GRAY(0.5), title='Middle Band')
@plot.line(color=color.BLUE, title='%K')
@plot.line(color=color.RED, title='%D')
def Main(self, k_length, k_smoothing, d_smoothing):
    k = Sma.new(Stoch.new(self.close, self.low, self.high, k_length), k_smoothing)
    d = Sma.new(k, d_smoothing)
    return k[0], d[0]
Stochastic RSI
Stochastic RSIStoch RSI
sub-paneformat: PRICEaquabluegrayred@plot.line
k_smoothing=3 · d_smoothing=3 · rsi_length=14 · k_length=14 · src=source.CLOSE
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, source, band, color, level, plot
from indie.algorithms import Rsi, Stoch, Sma


@indicator('Stoch RSI', format=format.PRICE)  # Stochastic RSI
@param.int('k_smoothing', default=3, min=1, title='Fast K stochastic smoothing')
@param.int('d_smoothing', default=3, min=1, title='Slow D stochastic smoothing')
@param.int('rsi_length', default=14, min=1, title='RSI length')
@param.int('k_length', default=14, min=1, title='Stochastic Length')
@param.source('src', default=source.CLOSE, title='Source')
@band(20, 80, fill_color=color.AQUA(0.1), line_color=color.GRAY, title='Background')
@level(50, line_color=color.GRAY(0.5), title='Middle Band')
@plot.line(color=color.BLUE, title='K')
@plot.line(color=color.RED, title='D')
def Main(self, k_smoothing, d_smoothing, rsi_length, k_length, src):
    rsi = Rsi.new(src, rsi_length)
    k = Sma.new(Stoch.new(rsi, rsi, rsi, k_length), k_smoothing)
    d = Sma.new(k, d_smoothing)
    return k[0], d[0]
Supertrend
SupertrendSupertrend
overlaygraygreenredtransparent@plot.fill@plot.line
atr_period=10 · factor=3.0 · ma_algorithm='RMA'
Indie исходник
# indie:lang_version = 5
from math import nan
from indie import indicator, param, plot, color
from indie.algorithms import Supertrend


@indicator('Supertrend', overlay_main_pane=True)
@param.int('atr_period', default=10, min=1)
@param.float('factor', default=3.0)
@param.str('ma_algorithm', default='RMA', options=['RMA', 'SMA', 'EMA', 'WMA'])
@plot.line('middle', color=color.GRAY(0.5), title='Body Middle')  # TODO: support display.none
@plot.line('down', color=color.RED, title='Down Trend')
@plot.line('up', color=color.GREEN, title='Up Trend')
@plot.fill('down', 'middle', color=color.RED(0.1), title='Down-Middle Fill')
@plot.fill('middle', 'up', color=color.GREEN(0.1), title='Middle-Up Fill')
def Main(self, factor, atr_period, ma_algorithm):
    st, direction = Supertrend.new(factor, atr_period, ma_algorithm)
    st_down = st[0] if direction[0] > 0 else nan
    st_up = st[0] if direction[0] = TimeFrame(1, time_frame_unit.DAY):
        return plot.Background(color=color.TRANSPARENT), plot.Background(color=color.TRANSPARENT)

    pre_market_background = plot.Background()
    if self.time[0] not in self.trading_session.pre_market:
        pre_market_background.color = color.TRANSPARENT

    after_hours_background = plot.Background()
    if self.time[0] not in self.trading_session.after_hours:
        after_hours_background.color = color.TRANSPARENT

    after_hours_background.outline_left = (
        not isnan(self.time[1]) and
        not self.trading_session.is_same_period(self.time[0], self.time[1])
    )

    return pre_market_background, after_hours_background
Trading Sessions
Trading Sessionsскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Triple EMA
Triple EMAскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

TRIX
TRIXTRIX
sub-paneformat: PRICEgrayred@plot.line
length=18
Indie исходник
# indie:lang_version = 5
from math import log
from indie import indicator, format, param, level, color, plot, MutSeriesF
from indie.algorithms import Change, Ema


@indicator('TRIX', format=format.PRICE)
@param.int('length', default=18, min=1)
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.RED, title='TRIX')
def Main(self, length):
    return 10000 * Change.new(Ema.new(Ema.new(Ema.new(MutSeriesF.new(log(self.close[0])), length), length), length))[0]
True Strength Indicator
True Strength IndicatorTSI
sub-paneformat: PRICEbluegrayred@plot.line
long_len=25 · short_len=13 · signal_len=13
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, level, color, plot
from indie.algorithms import Tsi, Ema


@indicator('TSI', format=format.PRICE, precision=4)  # True Strength Indicator
@param.int('long_len', default=25, min=1, title='Long Length')
@param.int('short_len', default=13, min=1, title='Short Length')
@param.int('signal_len', default=13, min=1, title='Signal Length')
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.BLUE, title='TSI')
@plot.line(color=color.RED, title='Signal')
def Main(self, long_len, short_len, signal_len):
    tsi = Tsi.new(self.close, long_len, short_len)
    return 100 * tsi[0], 100 * Ema.new(tsi, signal_len)[0]
Ultimate Oscillator
Ultimate OscillatorUO
sub-paneformat: PRICEred@plot.line
fast_len=7 · middle_len=14 · slow_len=28
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color
from indie.algorithms import Uo


@indicator('UO', format=format.PRICE)  # Ultimate Oscillator
@param.int('fast_len', default=7, min=1, title='Fast Length')
@param.int('middle_len', default=14, min=1, title='Middle Length')
@param.int('slow_len', default=28, min=1, title='Slow Length')
@plot.line(color=color.RED)
def Main(self, fast_len, middle_len, slow_len):
    return Uo.new(fast_len, middle_len, slow_len)[0]
Volatility Stop
Volatility StopVStop
overlayredteal@plot.line@plot.marker
src=source.CLOSE · length=20 · factor=2.0
Indie исходник
# indie:lang_version = 5
from math import isnan, nan
from indie import indicator, param, source, plot, color, Var, MutSeries
from indie.algorithms import Atr, Tr


@indicator('VStop', overlay_main_pane=True)  # Volatility Stop
@param.source('src', default=source.CLOSE, title='Source')
@param.int('length', default=20, min=2, title='ATR Length')
@param.float('factor', default=2.0, min=0.25, step=0.25, title='Multiplier')
@plot.marker(title='VStop (Cross)', size=3, style=plot.marker_style.CROSS, position=plot.marker_position.CENTER)
@plot.line(title='VStop (Line)', display_options=plot.LineDisplayOptions())
def Main(self, src, length, factor):
    if isnan(src[0]):
        return plot.Marker(value=nan), plot.Line(value=nan)

    atr_val = Atr.new(length)[0]
    atr_mult = (atr_val * factor) if not isnan(atr_val) else Tr.new()[0]

    uptrend = MutSeries[bool].new(init=True)
    stop = Var[float].new(src[0])
    mn = Var[float].new(src[0])
    mx = Var[float].new(src[0])

    not_first = self.bar_index > 0
    mx.set(max(mx.get(), src[0]))
    mn.set(min(mn.get(), src[0]))
    prev_stop = stop.get()

    prev_trend = uptrend[0]
    vstop = nan
    if prev_trend:
        vstop = max(prev_stop, mx.get() - atr_mult)
    else:
        vstop = min(prev_stop, mn.get() + atr_mult)

    curr_trend = (src[0] >= vstop)
    if not_first and curr_trend != prev_trend:
        mx.set(src[0]); mn.set(src[0])
        vstop = (mx.get() - atr_mult) if curr_trend else (mn.get() + atr_mult)

    stop.set(vstop)
    uptrend[0] = curr_trend

    col = color.TEAL if curr_trend else color.RED
    return (
        plot.Marker(vstop, color=col),
        plot.Line(vstop, color=col)
    )
Volume Moving Average Weighted
Volume Moving Average WeightedVWMA
overlayblue@plot.line
length=20 · src=source.CLOSE · offset=0
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Vwma


@indicator('VWMA', overlay_main_pane=True)  # Volume Weighted Moving Average
@param.int('length', default=20, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@param.int('offset', default=0, min=-500, max=500, title='Offset')
@plot.line(color=color.BLUE)
def Main(self, length, src, offset):
    result = Vwma.new(src, length)
    return plot.Line(result[0], offset=offset)
Volume Oscillator
Volume OscillatorVolume Osc
sub-paneformat: PRICEbluegray@plot.line
short_len=5 · long_len=10
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, level, color, plot
from indie.algorithms import Ema
from indie.math import divide


@indicator('Volume Osc', format=format.PRICE)  # Volume Oscillator  # TODO: format=format.PERCENT
@param.int('short_len', default=5, min=1, title='Short Length')
@param.int('long_len', default=10, min=1, title='Long Length')
@level(0, line_color=color.GRAY, title='Zero')
@plot.line(color=color.BLUE, title='VO')
def Main(self, short_len, long_len):
    short = Ema.new(self.volume, short_len)[0]
    long = Ema.new(self.volume, long_len)[0]
    return 100 * divide(short - long, long)
Volume Weighted Average Price
Volume Weighted Average PriceVWAP
overlaybluegreen@plot.fill@plot.line
src=source.HLC3 · anchor='Session' · offset=0
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, source, plot, color
from indie.algorithms import Vwap


@indicator('VWAP', overlay_main_pane=True)  # Volume Weighted Average Price
@param.source('src', default=source.HLC3, title='Source')
# TODO: add Earnings, Dividends, Splits
@param.str('anchor', default='Session', options=['Session', 'Week', 'Month', 'Year'], title='Anchor')
@param.int('offset', default=0, min=-500, max=500, title='Offset')
@plot.line('vwap', title='VWAP', color=color.BLUE)
@plot.line('upper', title='Upper band', color=color.GREEN)
@plot.line('lower', title='Lower band', color=color.GREEN)
@plot.fill('upper', 'lower', color=color.GREEN(0.1), title='Background')
def Main(self, src, anchor, offset):
    std_dev_mult = 1.0
    main_line, upper, lower = Vwap.new(src, anchor, std_dev_mult)
    return (
        plot.Line(main_line[0], offset=offset),
        plot.Line(upper[0], offset=offset),
        plot.Line(lower[0], offset=offset),
        plot.Fill(offset=offset),
    )
Vortex Indicator
Vortex IndicatorVI
sub-paneformat: PRICEbluered@plot.line
length=14
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, plot, color, MutSeriesF
from indie.algorithms import Sum, Atr
from indie.math import divide


@indicator('VI', format=format.PRICE, precision=4)  # Vortex Indicator
@param.int('length', default=14, min=2)
@plot.line(color=color.RED, title='VI -')
@plot.line(color=color.BLUE, title='VI +')
def Main(self, length):
    vmm = Sum.new(MutSeriesF.new(abs(self.low[0] - self.high[1])), length)[0]
    vmp = Sum.new(MutSeriesF.new(abs(self.high[0] - self.low[1])), length)[0]
    satr = Sum.new(Atr.new(1), length)[0]
    vim = divide(vmm, satr)
    vip = divide(vmp, satr)
    return vim, vip
Williams Alligator
Williams AlligatorAlligator
overlaybluegreenred@plot.line
jaw_period=13 · teeth_period=8 · lips_period=5 · jaw_offset=8 · teeth_offset=5 · lips_offset=3
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, plot, color
from indie.algorithms import Rma


@indicator('Alligator', overlay_main_pane=True)  # Williams Alligator
@param.int('jaw_period', default=13, min=1, title='Jaw Period')
@param.int('teeth_period', default=8, min=1, title='Teeth Period')
@param.int('lips_period', default=5, min=1, title='Lips Period')
@param.int('jaw_offset', default=8, min=1, title='Jaw Offset')
@param.int('teeth_offset', default=5, min=1, title='Teeth Offset')
@param.int('lips_offset', default=3, min=1, title='Lips Offset')
@plot.line(color=color.BLUE, title='Jaw')
@plot.line(color=color.RED, title='Teeth')
@plot.line(color=color.GREEN, title='Lips')
def Main(self, jaw_period, teeth_period, lips_period, jaw_offset, teeth_offset, lips_offset):
    jaw = Rma.new(self.hl2, jaw_period)
    teeth = Rma.new(self.hl2, teeth_period)
    lips = Rma.new(self.hl2, lips_period)

    return (
        plot.Line(jaw[0], offset=jaw_offset),
        plot.Line(teeth[0], offset=teeth_offset),
        plot.Line(lips[0], offset=lips_offset),
    )
Williams Percent Range
Williams Percent RangeWilliams %R
sub-paneformat: PRICEgraypurple@plot.line
length=14 · src=source.CLOSE
Indie исходник
# indie:lang_version = 5
from indie import indicator, format, param, source, band, color, level, line_style, plot
from indie.algorithms import Highest, Lowest
from indie.math import divide


@indicator('Williams %R', format=format.PRICE)  # Williams Percent Range
@param.int('length', default=14, min=1)
@param.source('src', default=source.CLOSE, title='Source')
@band(-20, -80, line_color=color.GRAY, fill_color=color.PURPLE(0.1), title='Background')
@level(-50, line_color=color.GRAY, line_style=line_style.DOTTED, title='Middle Level')
@plot.line(color=color.PURPLE, title='%R')
def Main(self, length, src):
    max = Highest.new(self.high, length)[0]
    min = Lowest.new(self.low, length)[0]
    return 100 * divide(src[0] - max, max - min)
Woodies CCI
Woodies CCIWoodies CCI
overlayblackbluegraygreenredtransparent@plot.histogram@plot.line
cci_turbo_len=6 · cci_14_len=14 · deviation_input=5.0 · depth_input=10 · line_color=color.BLUE · extend_input=True · show_price_input=True · show_vol_input=True · show_chg_input=True · price_diff_input='Absolute'
Indie исходник
# indie:lang_version = 5
from indie import indicator, param, level, color, line_style, plot, Color
from indie.algorithms import Cci


@indicator('Woodies CCI')
@param.int('cci_turbo_len', default=6, min=3, max=14, title='CCI Turbo Length')
@param.int('cci_14_len', default=14, min=7, max=20, title='CCI 14 Length')
@level(-100, line_color=color.GRAY, line_style=line_style.DOTTED, title='Minus Line')
@level(0, line_color=color.GRAY, line_style=line_style.SOLID, title='Zero Line')
@level(100, line_color=color.GRAY, line_style=line_style.DOTTED, title='Hundred Line')
@plot.histogram(title='CCI Turbo Histogram')
@plot.line(color=color.GREEN, title='CCI Turbo')
@plot.line(color=color.RED, title='CCI 14')
def Main(self, cci_turbo_len, cci_14_len):
    cci_turbo = Cci.new(self.close, cci_turbo_len)
    cci_14 = Cci.new(self.close, cci_14_len)
    last_5_down = cci_14[5]  0 and cci_14[4] > 0 and cci_14[3] > 0 and cci_14[2] > 0 and cci_14[1] > 0

    hist_color = color.BLACK  # default value
    if last_5_up:
        hist_color = color.GREEN
    elif last_5_down:
        hist_color = color.RED
    elif cci_14[0]  None:
        self.end = end
        self.vol = vol
        if self.lb is not None:
            self.lb.value().position = self.end
            self.lb.value().text = price_rotation_aggregate(self.start.value().price, self.end.price,
                                                            self.vol, settings)
            self.chart.draw(self.lb.value())
        if self.ln is not None:
            self.ln.value().point_b = self.end
            self.chart.draw(self.ln.value())

    def delete(self) -> None:
        if self.ln is not None:
            self.chart.erase(self.ln.value())
        if self.lb is not None:
            self.chart.erase(self.lb.value())


class ZigZagPainter(Algorithm):
    def __init__(self, ctx: Context):
        super().__init__(ctx)
        self.last_pivot = ctx.new_var(Optional[Pivot]())
        self.sum_vol = ctx.new_var(0.0)

    def calc(self, chart: Chart, settings: Settings) -> None:
        ctx = self.ctx
        length = max(2, settings.depth // 2)
        if not isnan(ctx.volume[length]):
            self.sum_vol.set(self.sum_vol.get() + ctx.volume[length])

        new_high, upd_high, new_low, upd_low = ZigZag.new(
            length, length, settings.dev_threshold, settings.allow_zig_zag_on_one_bar)
        if new_high or upd_high:
            self._handle_zigzag_event(ctx.time[length], ctx.high[length],
                                      new_high, upd_high, True, chart, settings)
        if new_low or upd_low:
            self._handle_zigzag_event(ctx.time[length], ctx.low[length],
                                      new_low, upd_low, False, chart, settings)

        if settings.extend_last:
            zigzag_updated = new_high or upd_high or new_low or upd_low
            self._draw_extend(zigzag_updated, length, chart, settings)

    def _handle_zigzag_event(self, time: float, price: float,
                             new_pivot: bool, upd_pivot: bool, is_high: bool,
                             chart: Chart, settings: Settings) -> None:
        point = AbsolutePosition(time, price)
        if new_pivot and self.last_pivot.get() is None:
            self.last_pivot.set(Pivot(None, point, nan, is_high, chart, settings))
            self.sum_vol.set(0)
            return
        last_pivot = self.last_pivot.get().value()
        if upd_pivot:
            last_pivot.update_pivot(point, last_pivot.vol + self.sum_vol.get(), settings)
            self.sum_vol.set(0)
        elif new_pivot:
            self.last_pivot.set(Pivot(last_pivot.end, point, self.sum_vol.get(), is_high,
                                      chart, settings))
            self.sum_vol.set(0)

    def _draw_extend(self, zigzag_updated: bool, length: int,
                     chart: Chart, settings: Settings) -> None:
        ctx = self.ctx
        last_pivot = self.last_pivot.get()
        extend = Var[Optional[Pivot]].new(None)

        rem_vol = Sum.new(ctx.volume, length)[0]
        if ctx.is_last_bar and last_pivot is not None:
            is_high = not last_pivot.value().is_high
            cur_series = ctx.high if is_high else ctx.low
            end = AbsolutePosition(ctx.time[0], cur_series[0])
            extend_vol = self.sum_vol.get() + rem_vol
            if extend.get() is None:  # there was no extend before, create a new one
                extend.set(Pivot(last_pivot.value().end, end, extend_vol, is_high,
                                 chart, settings))
            elif zigzag_updated:  # existing extend is obsolete, recreate it
                extend.get().value().delete()
                extend.set(Pivot(last_pivot.value().end, end, extend_vol, is_high,
                                 chart, settings))
            else:  # update right point of the extend
                extend.get().value().update_pivot(end, extend_vol, settings)


def price_rotation_diff(start: float, end: float, settings: Settings) -> str:
    diff = end - start
    sign = '+' if diff > 0 else ''
    diff_str = ''
    if settings.difference_price_mode == 'Absolute':
        diff_str = str(round(diff, settings.price_precision))
    else:
        diff_str = str(round(diff * 100 / start, 2)) + '%'
    return '(' + sign + diff_str + ')'


def to_vol_format(v: float) -> str:
    if v > 1000000:
        return str(round(v / 1000000, 3)) + 'M'
    if v > 1000:
        return str(round(v / 1000, 3)) + 'K'
    if v == 0:
        return '0'
    return str(v)


def price_rotation_aggregate(start: float, end: float, vol: float, settings: Settings) -> str:
    s = ''
    if settings.display_reversal_price:
        s += str(round(end, settings.price_precision)) + ' '
    if settings.display_reversal_price_change:
        s += price_rotation_diff(start, end, settings) + ' '
    if settings.display_cumulative_volume:
        s += '\n' + to_vol_format(vol)
    return s


def make_pivot_label(is_high: bool, point: AbsolutePosition,
                     settings: Settings) -> Optional[LabelAbs]:
    if (not settings.display_reversal_price and
            not settings.display_reversal_price_change and
            not settings.display_cumulative_volume):
        return None
    txt_color = color.RED
    callout_pos = callout_position.BOTTOM_RIGHT  # TODO: callout_position.BOTTOM
    if is_high:
        txt_color = color.GREEN
        callout_pos = callout_position.TOP_RIGHT  # TODO: callout_position.TOP
    return LabelAbs(text='', position=point, text_color=txt_color, callout_position=callout_pos,
                    font_size=11, bg_color=color.TRANSPARENT)  # TODO: text_align=text_align.CENTER


@indicator('Zig Zag', overlay_main_pane=True)
@param.float('deviation_input', default=5.0, min=0.00001, max=100.0, step=0.5,
             title='Price deviation for reversals (%)')
@param.int('depth_input', default=10, min=2, title='Pivot legs')
@param.color('line_color', default=color.BLUE, title='Line color')
@param.bool('extend_input', default=True, title='Extend to last bar')
@param.bool('show_price_input', default=True, title='Display reversal price')
@param.bool('show_vol_input', default=True, title='Display cumulative volume')
@param.bool('show_chg_input', default=True, title='Display reversal price change')
@param.str('price_diff_input', default='Absolute', options=['Absolute', 'Percent'],
           title='Price Reversal')
class Main(MainContext):
    def __init__(self, deviation_input, depth_input, line_color, extend_input,
                 show_price_input, show_vol_input, show_chg_input, price_diff_input):
        self._settings = Settings(
            line_color, deviation_input,
            depth_input, extend_input,
            show_price_input, show_vol_input,
            show_chg_input, price_diff_input,
        )

    def pre_calc(self):
        self._settings.price_precision = self.info.price_precision

    def calc(self):
        ZigZagPainter.new(self.chart, self._settings)
ZigZag
ZigZagскрин

Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.

Алгоритмы indie.algorithms (44) — сигнатуры и параметры

Алгоритмы indie.algorithms (44)

— Примитивы indie.algorithms (44 класса)

Источник: indie__Library-reference__package-indie-algorithms.md. Сигнатуры взяты из блоков «Source code» (@algorithm def … / class … (Algorithm)), дефолты — из сигнатур и блоков «Example». Вызов всегда через X.new(...); первый неявный аргумент self/ctx опущен. SeriesF = Series[float].

АлгоритмПараметры (тип, дефолт)Что считаетВыход
Smasrc: SeriesF, length: intSimple Moving AverageSeriesF
Emasrc: SeriesF, length: intExponential MA (alpha=2/(len+1))SeriesF
Wmasrc: SeriesF, length: intWeighted MA (линейные веса)SeriesF
Vwmasrc: SeriesF, length: intVolume-Weighted MA = SMA(src·vol)/SMA(vol)SeriesF
Rmasrc: SeriesF, length: intWilder's smoothed MA (SMMA/RMA), seed = SMASeriesF
Masrc: SeriesF, length: int, algorithm: strДиспетчер MA: 'EMA'/'SMA'/'RMA'/'VWMA'/'WMA'SeriesF
Macdsrc: SeriesF, fast_len: int, slow_len: int, sig_len: int, ma_source: str='EMA', ma_signal: str='EMA'MACD line, signal, histogram (пример: 12/26/9)tuple[SeriesF, SeriesF, SeriesF] (macd, signal, hist)
Rsisrc: SeriesF, length: intRelative Strength Index = 100·Rma(up)/(Rma(up)+Rma(down))SeriesF
Bbsrc: SeriesF, length: int, mult: floatBollinger Bands: SMA ± mult·StdDev (пример: len=20, mult=2.0)tuple (lower, middle, upper)¹
Atrlength: int, ma_algorithm: str='RMA'Average True Range = Ma(Tr, length)SeriesF
Adxadx_len: int, di_len: intAverage Directional Index + ±DI (через Tr, Rma, FixNan)tuple (minus_di, adx, plus_di)
Ccisrc: SeriesF, length: intCommodity Channel Index = (src−SMA)/(0.015·Dev)SeriesF
Stochsrc: SeriesF, low: SeriesF, high: SeriesF, length: intСтохастик %K = 100·(src−LL)/(HH−LL)SeriesF
Sarstart: float, increment: float, maximum: floatParabolic SAR (пример: 0.02/0.02/0.2)SeriesF
Supertrendfactor: float, atr_period: int, ma_algorithm: strSupertrend по hl2 ± factor·ATR (пример: 3.0/10/'SMA')tuple (super_trend, direction)
Vwapsrc: SeriesF, anchor: str, std_dev_mult: floatVWAP + полосы ст.откл.; anchor='Session'/'Week'/'Month'/'Year' (док-текст также упоминает day/week/month/year)tuple (main_line, upper, lower)
Donchianlength: intDonchian middle = (Highest(high)+Lowest(low))/2SeriesF²
ZigZagleft_bars: int, right_bars: int, dev_threshold: float, allow_zig_zag_on_one_bar: bool=TrueПивоты ZigZag по dev-порогу; пивот приходит с лагом right_bars баровtuple[bool,bool,bool,bool] (new_high, upd_high, new_low, upd_low)
Uofast_len: int, middle_len: int, slow_len: intUltimate Oscillator (пример: 7/14/28)SeriesF³
Tsisrc: SeriesF, long_len: int, short_len: intTrue Strength Index = двойное EMA-сглаживание Change (пример: 21/12); внутр. помощник DoubleSmoothSeriesF
Rocsrc: SeriesF, length: intRate of Change = 100·Change(src,len)/src[len]SeriesF
StdDevsrc: SeriesF, length: intСтандартное отклонение скользящего окнаSeriesF
Devsrc: SeriesF, length: intСреднее абсолютное отклонение от SMA (mean deviation)SeriesF
LinRegy: SeriesF, length: int, offset: int=0Linear Regression (точка на МНК-прямой со сдвигом offset)SeriesF
Corrx: SeriesF, y: SeriesF, length: intКоэффициент корреляции = cov(x,y)/(σx·σy)SeriesF
Percentilesrc: SeriesF, length: int, pct: float, interpolate: boolПерцентиль окна; pct∈[0..100]; interpolate = nearest-rank vs линейная интерполяция (пример: len=12, pct=95, interpolate=True)SeriesF
PercentRanksrc: SeriesF, length: int% значений в окне ≤ текущего = 100·num_leq/lengthSeriesF
Mediansrc: SeriesF, length: intСкользящая медиана (через SortedList)SeriesF
Highestsrc: SeriesF, length: intМаксимум за length баровSeriesF
Lowestsrc: SeriesF, length: intМинимум за length баровSeriesF
SinceHighestsrc: SeriesF, length: intЧисло баров с последнего максимума в окнеSeries[int]
SinceLowestsrc: SeriesF, length: intЧисло баров с последнего минимума в окнеSeries[int]
SinceTruecondition: Series[bool]Число баров с последнего True (−1 если ещё не было)Series[int]
PivotHighLowsrc: SeriesF, left_bars: int, right_bars: intПивотные хай/лоу (локальные экстремумы с подтверждением); внутри HighestLowesttuple (pivot_high, pivot_low)
Mfisrc: SeriesF, length: intMoney Flow Index (пример: hlc3, len=14)SeriesF
Mfv— (без параметров)Money Flow Volume = vol·((c−l)−(h−c))/(h−l)SeriesF
NetVolumesrc: SeriesFНакопленный нетто-объём по знаку Change(src)SeriesF
CumSumsrc: SeriesFКумулятивная сумма по всей историиSeriesF
Sumsrc: SeriesF, length: intСкользящая сумма за length (NaN-aware, через Var)SeriesF
Changesrc: SeriesF, length: int=1Разность src[0]−src[length]SeriesF
FixNansrc: SeriesFЗаменяет NaN на последнее не-NaN значение (forward-fill)SeriesF
NanToZerosrc: SeriesFЗаменяет NaN на 0SeriesF
Trhandle_na: bool=FalseTrue Range = max(h−l,h−c[1],l−c[1])SeriesF

¹ Bb: исходник возвращает (MutSeriesF(lower), MutSeriesF(middle), MutSeriesF(upper)), т.е. порядок (lower, middle, upper); doc-текст к calc описывает «middle, upper, lower» — в коде фактически lower/middle/upper. Это расхождение в самой доке.

² Donchian: в Source code блоке def Donchian(self, length: int) -> SeriesF возвращает только среднюю линию; верх/низ берутся отдельно через Highest/Lowest.

³ Uo: показан только def Main+Uo.new(...); полное тело алгоритма в Source-блоке усечено в доке, но сигнатура (fast_len, middle_len, slow_len) подтверждена из примера.

Mfi: в doc-странице блок «Source code» для Mfi по факту содержит исходник Mfv (ошибка в доке — код Mfi не приведён). Сигнатура Mfi.new(src, length) подтверждена из «Example»; что считает — Money Flow Index. Тело алгоритма Mfi из доки не извлекается.

Честные оговорки по извлечению

Ключевые пути

Инструменты рисования — 25 живых скринов + доки

Каждый инструмент нарисован вживую на DOGE/USD · 1h (активация хоткеем → отрисовка точек → кадр → очистка через «Remove Drawings»). 25 из ~33 сняты в живом терминале; остальные ~8 (Rectangle/Circle/Ellipse/Fib Retracement/Date Range/Measure/Text/Callout) — их хоткеи Option+<буква> перехватывает браузер как ввод спецсимвола, а drawing-тулбар (hover-reveal) не отрисовывается под автоматизацией; они показаны скринами из доков ниже + в таблице параметров.

Живые инструменты (нарисованы на чарте)

Trend Line
Trend Line · live
Horizontal Line
Horizontal Line · live
Horizontal Ray
Horizontal Ray · live
Vertical Line
Vertical Line · live
Cross Line
Cross Line · live
Parallel Channel
Parallel Channel · live
Triangle
Triangle · live
Curve
Curve · live
Arc
Arc · live
Fibonacci Extension
Fibonacci Extension · live
Long & Short
Long & Short · live
Price Range
Price Range · live
XABCD
XABCD · live
Cypher
Cypher · live
Head & Shoulders
Head & Shoulders · live
ABCD
ABCD · live
Triangle Pattern
Triangle Pattern · live
Three Drives
Three Drives · live
Elliott Impulse
Elliott Impulse · live
Elliott Correction
Elliott Correction · live
Pen
Pen · live
Highlighter
Highlighter · live
Path
Path · live
Polygon
Polygon · live
Price Tag
Price Tag · live

Скрины из официальных доков (паттерны, фигуры, Free-form, FRVP)

Cypher (паттерн)
Cypher (паттерн)
Head & Shoulders
Head & Shoulders
ABCD
ABCD
Triangle (паттерн)
Triangle (паттерн)
Three Drive
Three Drive
Elliott Impulse
Elliott Impulse
Elliott Correction
Elliott Correction
Rectangle (фигура)
Rectangle (фигура)
Fixed Range Volume Profile
Fixed Range Volume Profile
Shapes (фигуры: circle/ellipse)
Shapes (фигуры: circle/ellipse)
Free-form (Pen/Path/…)
Free-form (Pen/Path/…)
Compare (VS)
Compare (VS)
Object Tree — группы
Object Tree — группы
Object Tree — управление
Object Tree — управление
Financials — пресеты
Financials — пресеты
AI — API key (BYOK)
AI — API key (BYOK)
Hotkeys
Hotkeys

I have thorough cross-verification. Compiling the final exhaustive answer now.

Источники: docs guide__platform__chart-widget__*.md; код bundles2/BgH94ARf.js (геометрия/типы/Fib-уровни), bundles2/E5A4FP5n.js (UI настроек). Где код расходится с доками — отмечено.

Сводная таблица (~40 инструментов, 7 категорий)

ИнструментКатегорияШорткатТочек (anchors)Ключевые опции/настройки
Quick MeasureMeasuresShift + M2 (start, end)Эфемерный (не остаётся на чарте); Δцены (abs/%/тики), баров (дни/часы), объём
Date & PriceMeasures2Как Quick, но остаётся; Δцена + время, abs + %
Date OnlyMeasuresAlt + D2Только время: счёт баров → недели/дни/часы по ТФ чарта
Price OnlyMeasuresAlt + Shift + M2Только цена: вертикальная Δ, abs + %
Long-Short ToolMeasuresAlt + L3 (entry + TP-уровень + SL-уровень, тянутся)Мульти-TP/SL, risk-ratio, sizing — см. блок ниже
Fixed Range Volume ProfileMeasures2 (start/end диапазона)Гистограмма объёма по ценам: POC, VAH, VAL, Up/Down volume, Value Area up/down, bar width, From/To координаты, шаблоны
Standard LineLinesAlt + T3 (start, end, middle)Кастомный текст на линии (в шаблонах), extension L/R, middle point toggle
ArrowLines3 (как Line)Стрелка на endpoint, направленность
RayLines2 (start + направление)Бесконечно в одну сторону
Trend AngleLines2Показывает угол в градусах (°) у линии, live-обновление (код: degree/°)
Horizontal RayLinesAlt + Shift + H1–2 (price level)Горизонталь вправо до края
Horizontal LineLinesAlt + H1 (price)Фикс. ценовой уровень
Vertical LineLinesAlt + V1 (time)Фикс. момент времени
Cross LineLinesAlt + X1 (точка)Пересечение H+V
Parallel ChannelLinesAlt + Shift + C3 (2 базовых + ширина)2 параллельные границы + опц. middle line, угол/ширина, extend
XABCD PatternPatterns5 (X,A,B,C,D / 4 ноги)Гармоники: Gartley/Butterfly/Crab/Bat (bull/bear)
Cypher PatternPatterns5 (X,A,B,C,D)PRZ в зоне XD, Fib 0.786–0.886; M-(bull)/W-(bear)
Head & ShouldersPatterns~5–7 (L-плечо, голова, R-плечо + neckline)Bear (топ) / Bull-inverse (дно), neckline-пробой
ABCD PatternPatterns4 (A,B,C,D / 3 ноги)AB=CD, Classic (CD=127.2/161.8% BC), Extension (от AB); показывает % ratio
Triangle PatternPatterns4 (A,B,C,D на чередующихся хай/лоу)Symmetrical/Ascending/Descending
Three DrivePatterns5 (3 драйва 1/2/3 + 2 ретрейса A/C)Drives 127.2/161.8%, ретрейсы 61.8/78.6%, симметрия по цене+времени
Elliott ImpulsePatterns6 (точки 1-2-3-4-5 = 5 волн)Motive 5 волн, правила волн 2/3/4; lookback ~600 баров (код)
Elliott CorrectionPatterns4 (A-B-C = 3 волны)Corrective zigzag 5-3-5
CircleShapesAlt + O2 (center + radius)Line color/thickness/style, fill + transparency
RectangleShapesAlt + R2 (углы)Текст внутри (в шаблонах), Extended Top/Bottom/Left/Right, Middle Line (50%), bg+transparency
Triangle (shape)ShapesAlt + G3 (A,B,C)Каждая вершина → context-меню (H/V-линия, H-ray); line+fill
EllipseShapesAlt + Shift + O2 (start + drag)Овал, line+fill+transparency
CurveShapesAlt + K3 (start, end, middle control)Кривая линия, БЕЗ заливки
ArcShapesAlt + Shift + K3 (start, end, control)Сектор С заливкой
PenFree-formAlt + Pfreehand (poly)Только цвет (доп. опции «coming soon»)
HighlighterFree-formAlt + Ifreehand (drag)Цвет; фикс. прозрачность
PathFree-formAlt + Shift + PN точек (клик; dbl-click/Enter завершить)Замкнутая ломаная из прямых; только цвет
PolylineFree-formAlt + NN точекОткрытая ломаная; только цвет
TextTextAlt + Shift + T1 (точка)B/I/U/strike, размер, выравнивание, цвет текста+фон, списки (bullet/number), exact price
CalloutTextAlt + Shift + Y1–2 (текст + указатель)Как Text + указатель 8 направлений (up/down/L/R + 4 угла)
Price TagTextAlt + Shift + G1 (price)Как Callout, маркер с ценой; указатель 8 направлений
Fibonacci RetracementFibonacciAlt + F2 (Price 1, Price 2)Reverse, Prices, Levels, Labels, font, bg fill+opacity, add/remove levels, координаты по барам
Fibonacci ExtensionFibonacciAlt + Shift + F3 (Price 1, 2, 3)Те же опции, что Retracement; проекция за P2
Fib Speed Resistance FanFibonacci2 (anchor + opposite pivot)Диагональные лучи; Reverse, Grid, per-level palette, L/R/Top labels, bg gradient
Fibonacci Time ZoneFibonacci2 (origin + base span 0→1)Вертикали по Fib-числам; per-level colors, enable/disable числа, bg shade

Fibonacci — точные уровни

Retracement / Extension (по доке). Retracement: 0, 23.6, 38.2, 50, 61.8, 78.6, 100%. Extension: 100, 127.2, 138.2, 161.8, 200, 261.8%.

Дефолты в КОДЕ (BgH94ARf.js) — у Retracement, Extension и Fan один общий набор уровней по умолчанию, шире чем в доке:

0, 0.236, 0.382, 0.5, 0.618, 0.786, 1, 1.618, 2.618, 3.618, 4.236

(каждый уровень = объект {label, val, style{fillColor, strokeColor, fillVisible, strokeVisible, lineWidth, lineStyle}}; цвета через CSS-vars --colors-legends-* либо хардкод hex). Уровни добавляются/удаляются/переупорядочиваются юзером.

Speed Resistance Fan (по доке, дефолт лучей): 0, 0.236, 0.382, 0.5, 0.618, 0.786, 1 — каждый луч = наклон (dynamic S/R).

Time Zone — последовательность Фибоначчи, подтверждена в коде:

0, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 (вертикали; per-level цвета и вкл/выкл отдельных чисел).

Patterns — что моделирует каждый

ПаттернГеометрияНазначение
XABCD5 точек / 4 ноги (XA, AB, BC, CD), соседние ноги в противофазеГармоники Gartley, Butterfly, Crab, Bat (bull/bear); зона разворота + цели
Cypher5 точек (X,A,B,C,D)PRZ в районе XD при Fib 0.786–0.886; bull = M-форма (вход D long), bear = W (short)
Head & ShouldersЛевое плечо → голова → правое плечо + necklineРазворот; bear на топах (голова выше плеч), bull-inverse на дне; сигнал = пробой neckline
ABCD4 точки / 3 ноги (AB, BC, CD)AB=CD (симметрия), Classic (CD = 127.2/161.8% BC), Extension (CD от AB); подпись = % CD относит. BC
Triangle (pattern)4 точки на чередующихся хай/лоу (A,C хаи; B,D лоу)Symmetrical (продолжение), Ascending (бычий, плоское сопротивление), Descending (медвежий, плоская поддержка)
Three Drive5 точек: драйвы 1/2/3 по тренду + ретрейсы A/CРазворот; драйвы 127.2/161.8% ретрейсов, ретрейсы 61.8/78.6% (в сильном тренде 38.2/50%), симметрия цена+время
Elliott Impulse5 волн (1-2-3-4-5)Motive по тренду; W2 не >100% W1, W3 за концом W1 и не самая короткая, W4 не входит в зону W1; нотация (1)..(5) и под-волны; lookback ~600 баров
Elliott Correction3 волны (A-B-C)Коррекция против тренда; zigzag 5-3-5, WB короче WA, WC за концом WA

Long-Short Tool — мульти-TP/SL и risk-ratio

Подтверждено в коде (BgH94ARf.js):

Режимы Risk Ratio:

1. Fixed — лочит соотношение (2:1, 3:1), защита от случайных правок (fixedRiskRatio).

2. Flexible — свободная подстройка ratio прямо на чарте/в панели, пересчёт по движению рынка.

Floating real-time PnL, визуализация Risk Ratio, сохранение сетапов в шаблоны.

Утилиты (Magnet / Hide / Sync / Lock / Favorite / Remove All)

УтилитаШорткатЧто делает
Magnet ModeCmd/Ctrl (hold = strong/temp)Regular: снап к значимым хай/лоу. Strong: к каждому OHLC бара. Клик по иконке = standard; hold Cmd/Ctrl = strong/временный
Hide/ShowCmd/Ctrl + H (скрыть все)Временно прячет рисунки без удаления; индивидуально через properties; по группам
Sync— (выбор уровня в настройках)Уровни: Global (все чарты/воркспейсы), Workspace, Channel (связанные чарты), Local (без синка, дефолт)
LockCmd/Ctrl + LБлок перемещения/правок; лок одиночный и групповой
Favorites— (звезда)Сохранение конфигов рисунков (Fib-уровни, цвета, шаблоны паттернов) для быстрого применения к новому рисунку
Remove Allиконка корзины → «Delete all drawings»Удаляет ВСЕ рисунки (включая locked); отменяемо Ctrl + Z

Object Tree (Layers)

Открывается иконкой «Layers». Централизованное управление всеми элементами чарта:

Общие настройки (shared)

Расхождение код↔доки: дефолтный набор Fib-уровней в коде шире документированного (вкл. 1.618/2.618/3.618/4.236 уже в Retracement) — доки показывают «классический» подмножество, код хранит полный расширенный набор для всех трёх Fib-инструментов (кроме Time Zone).

Файлы: docs /Users/stotskii/research/takeprofit-charts/docs/guide__platform__chart-widget__*.md; код /Users/stotskii/research/takeprofit-charts/bundles2/BgH94ARf.js (Fib-массивы, multiTPEnabled/tp:[]/sl:[], timezone-последовательность, имена типов), /Users/stotskii/research/takeprofit-charts/bundles2/E5A4FP5n.js (UI настроек/drawing).

Виджеты — поштучно

I have everything needed. Here is the complete per-widget teardown.


Источники: docs guide__platform__* / guide__getting-started__Widget-Hub.md; код bundles2/{BgH94ARf,68OUmlCg,CXdJCvTF,DnLmMLX5,C2qhB3iH,E5A4FP5n}.js.

Карта виджетов (реестр + proto-сервисы)

Из кода реестр widget-types (частоты упоминаний): Chart, Watchlist, Screener, Financials, IndicatorEditor, Note, MarketDepth, Feed, Strategy(backtest) + брокерские пары LimeOrder/LimePortfolio, J2TOrder/J2TPortfolio, IlotcosOrder/IlotcosPortfolio. Хаб (Widget-Hub.md) перечисляет: Charts, Watchlist, Financials, IDE, Notes, Stock Screener, Backtest, LimeOrder.

Все данные идут через Connect-RPC сервисы takeprofit.* (typeName в BgH94ARf.js):

ДоменСервисМетоды
Котировкиmarketdata.external.quote.v1.QuoteApiQuoteStream, ListQuotes
Свечиmarketdata.external.candle.v1.CandleOffsetApi / ExtrapolationApiQueryCandleOffsets
Фундаментmarketdata.external.fundamental.v2.FundamentalApiGetFundamentalCatalogue, ListCategories, ListModes, ListFundamentals
Ratiomarketdata.external.ratio.v2.CatalogueApi
Скринерscreener.external.v2.StockScreenerApiSearch; CategoryApi: ListCategories, ListCategoryGroups; v1 SearcherApi
Справочникиreference.external.{security,exchange,industry}ListSecurities, ListSecurityClasses, и т.д.
Индикаторыindicator.controller.v2.ControllerApiGetIndicatorStream, GetIndicatorHistory, GetStrategyOrdersAndTradesHistory, GetIndicatorsInfo
Миграция кодаindicator.code_migrator.v1.CodeMigratorApiMigrateCode (Pine→Indie)
Бэктест-отчётindicator.strategy_report_stylist.v1.StrategyReportStylistApi(AI-стилизация отчёта) — найдено в DnLmMLX5.js
Алертыalerts.tpi_alerts.v1.AlertsApi (+Notifications, PopupSender)CreateAlert, …, GetUserAlertLog
Трейдингtrading.trading.v1.TradingApiPlaceOrder, CancelOrder, UpdateOrder, GetEventsStream, StoreApiKey, ListAdaptors, GetAdaptorCapabilities, GetPlaceOrderData(Stream), SetLeverage, SetMarginMode, SwitchSpotMarginTrade, SetPositionMode
Telegramtelegram_connector.v1.TelegramConnectorApi(алерты в TG)

1. Chart

2. Watchlist (68OUmlCg.js)

3. Stock Screener (StockScreenerApi.Search, CategoryApi)

4. Financials (FundamentalApi)

5. Market Depth / Order Book (CXdJCvTF.js)

6. Indie® Code Editor / IDE (ControllerApi, CodeMigratorApi)

7. Strategy / Backtesting (DnLmMLX5.js, GetStrategyOrdersAndTradesHistory, StrategyReportStylistApi)

8. Feed (block-editor read-only render)

9. Notes (block-editor E5A4FP5n.js)

10. Account (C2qhB3iH.js)

11. Брокерские виджеты — Lime / J2T / Ilotcos

Все три — парные третье-сторонние виджеты (Order + Portfolio), интерфейс управляется брокером. В реестре: LimeOrder/LimePortfolio, J2TOrder/J2TPortfolio, IlotcosOrder/IlotcosPortfolio (в Widget-Hub под группой "Third-Party").

БрокерВиджетыЧтоИсточник/auth
Lime (lime-widget-overview.md)LimeOrder (трейдинг) + LimePortfolio (инструменты/портфель)Прямая торговля через Lime Trading (NYC agency broker)OAuth: вход в Lime → "Go to trading" → выбор TakeProfit vs Lime Trader → возврат; интерфейс/правила управляет Lime
J2TJ2TOrder + J2TPortfolioАналогичная пара третье-стороннего брокерачерез TradingApi адаптер
IlotcosIlotcosOrder + IlotcosPortfolioАналогичная парачерез TradingApi адаптер

Бонус — AI Assistant (не в списке задачи, но это сквозной виджет): 4 инструмента (Technical Analysis / Indie Scripts / Platform Guide / Risk & Position Sizing) + Auto; читает контекст attached-виджетов (ticker/TF/range/индикаторы/drawings); генерит chart-action карточки (добавить уровни/трендлайны/Fib/паттерны); ответы только на английском; лимит 5 запросов/мин на юзера на все треды; стриминг с кнопкой Stop.

Ключевые числовые лимиты: Watchlist 1000 символов; Screener ~5000 US-компаний, 80+ колонок, refresh 30s/1m/3m; Financials 10y/12q; Tick 1–49 free / 50–1000 (Binance/Bybit/Pepperstone/Exness); Market Depth 70+ крипто-бирж, кластер min 60s/default 5m; AI 5 req/min.