Каждый индикатор — живой скрин + исходник
Все 80 из 80 встроенных индикаторов отсняты вживую (залогиненный терминал, DOGE/USD · 1h, каждый по отдельности). У 56 из них — полный Indie-исходник + дизайн (цвета/тип plot/format/параметры) под спойлером.
Метод: каждый индикатор добавлялся на чистый чарт по одному, рендерился сервером (R_PYTHON/R_WASM), снимался кадр, затем удалялся — изоляция гарантирована. Остальные 24 (Ichimoku/ZigZag/Pivots и пр.) — сложные мульти-классовые painter-индикаторы, у которых исходник в офиц. доках; скрин есть у всех 80. Инструменты рисования: 25 нарисованы вживую на чарте + скрины из доков для остальных.
Индикаторы (80) — живой скрин + код+дизайн каждого
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]length=9Indie исходник
# 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])window_size=9 · offset=0.85 · sigma=6.0Indie исходник
# 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)length=14Indie исходник
# 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, uplength=14Indie исходник
# 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]adx_len=14 · di_len=14Indie исходник
# 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]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]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)Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
len_rsi=3 · len_up_down=2 · len_roc=100Indie исходник
# 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Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
wma_length=10 · long_roc_length=14 · short_roc_length=11Indie исходник
# 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 curveexchange='NASDAQ' · ticker='GOOG' · source=source.CLOSE · length=20Indie исходник
# 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]length=21 · centered=FalseIndie исходник
# 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)adx_len=14 · di_len=14Indie исходник
# 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]length=20Indie исходник
# 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()length=9 · src=source.CLOSEIndie исходник
# 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]length=14 · divisor=10000Indie исходник
# 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]length=13Indie исходник
# 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]length=20 · percent=10.0 · src=source.CLOSE · exponential=FalseIndie исходник
# 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()length=9 · src=source.CLOSE · offset=0Indie исходник
# 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 supportedconversion_periods=9 · base_periods=26 · lagging_span_2_periods=52 · displacement=26Indie исходник
# 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),
)Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
length=20 · mult=2.0 · src=source.CLOSE · exp=True · bands_style='Average True Range' · atr_length=10Indie исходник
# 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()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]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=9Indie исходник
# 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, siglength=25 · offset=0 · src=source.CLOSEIndie исходник
# 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]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=TrueIndie исходник
# 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)short_len=9 · long_len=21Indie исходник
# 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Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
length=10 · src=source.CLOSEIndie исходник
# 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]length=14 · src=source.HLC3Indie исходник
# 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]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),
)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=200Indie исходник
# 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 nanlength=9 · src=source.CLOSE · offset=0Indie исходник
# 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)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]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 supportedstart=0.02 · increment=0.02 · maximum=0.2Indie исходник
# 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]length_high_left=10 · length_low_left=10 · length_high_right=10 · length_low_right=10 · show_unconfirmed=TrueIndie исходник
# 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,
)higher_tf='Auto' · show_labels=True · show_prices=True · lookahead=True · levels_number=3 · length=10 · offset=0Indie исходник
# 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)Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
rsi_length=14 · src=source.CLOSE · ma_type='SMA' · ma_length=14 · bb_mult=2.0Indie исходник
# 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()Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
length=10 · offset=0 · ma_type='SMA' · ma_length=14 · bb_mult=2.0Indie исходник
# 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(),
)length=9 · src=source.CLOSE · offset=0Indie исходник
# 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 supportedlong_len=20 · short_len=5 · signal_len=5Indie исходник
# 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]long_len=20 · short_len=5 · signal_len=5Indie исходник
# 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]length=7 · src=source.CLOSEIndie исходник
# 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]k_length=14 · k_smoothing=1 · d_smoothing=3Indie исходник
# 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]k_smoothing=3 · d_smoothing=3 · rsi_length=14 · k_length=14 · src=source.CLOSEIndie исходник
# 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]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Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
Сложный мульти-классовый индикатор — исходник в офиц. доках (Code-examples). Здесь: живой рендер на DOGE/USD 1h.
length=18Indie исходник
# 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]long_len=25 · short_len=13 · signal_len=13Indie исходник
# 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]fast_len=7 · middle_len=14 · slow_len=28Indie исходник
# 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]src=source.CLOSE · length=20 · factor=2.0Indie исходник
# 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)
)length=20 · src=source.CLOSE · offset=0Indie исходник
# 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)short_len=5 · long_len=10Indie исходник
# 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)src=source.HLC3 · anchor='Session' · offset=0Indie исходник
# 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),
)length=14Indie исходник
# 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, vipjaw_period=13 · teeth_period=8 · lips_period=5 · jaw_offset=8 · teeth_offset=5 · lips_offset=3Indie исходник
# 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),
)length=14 · src=source.CLOSEIndie исходник
# 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)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)Сложный мульти-классовый индикатор — исходник в офиц. доках (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].
| Алгоритм | Параметры (тип, дефолт) | Что считает | Выход | ||||
|---|---|---|---|---|---|---|---|
| Sma | src: SeriesF, length: int | Simple Moving Average | SeriesF | ||||
| Ema | src: SeriesF, length: int | Exponential MA (alpha=2/(len+1)) | SeriesF | ||||
| Wma | src: SeriesF, length: int | Weighted MA (линейные веса) | SeriesF | ||||
| Vwma | src: SeriesF, length: int | Volume-Weighted MA = SMA(src·vol)/SMA(vol) | SeriesF | ||||
| Rma | src: SeriesF, length: int | Wilder's smoothed MA (SMMA/RMA), seed = SMA | SeriesF | ||||
| Ma | src: SeriesF, length: int, algorithm: str | Диспетчер MA: 'EMA'/'SMA'/'RMA'/'VWMA'/'WMA' | SeriesF | ||||
| Macd | src: 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) | ||||
| Rsi | src: SeriesF, length: int | Relative Strength Index = 100·Rma(up)/(Rma(up)+Rma(down)) | SeriesF | ||||
| Bb | src: SeriesF, length: int, mult: float | Bollinger Bands: SMA ± mult·StdDev (пример: len=20, mult=2.0) | tuple (lower, middle, upper)¹ | ||||
| Atr | length: int, ma_algorithm: str='RMA' | Average True Range = Ma(Tr, length) | SeriesF | ||||
| Adx | adx_len: int, di_len: int | Average Directional Index + ±DI (через Tr, Rma, FixNan) | tuple (minus_di, adx, plus_di) | ||||
| Cci | src: SeriesF, length: int | Commodity Channel Index = (src−SMA)/(0.015·Dev) | SeriesF | ||||
| Stoch | src: SeriesF, low: SeriesF, high: SeriesF, length: int | Стохастик %K = 100·(src−LL)/(HH−LL) | SeriesF | ||||
| Sar | start: float, increment: float, maximum: float | Parabolic SAR (пример: 0.02/0.02/0.2) | SeriesF | ||||
| Supertrend | factor: float, atr_period: int, ma_algorithm: str | Supertrend по hl2 ± factor·ATR (пример: 3.0/10/'SMA') | tuple (super_trend, direction) | ||||
| Vwap | src: SeriesF, anchor: str, std_dev_mult: float | VWAP + полосы ст.откл.; anchor='Session'/'Week'/'Month'/'Year' (док-текст также упоминает day/week/month/year) | tuple (main_line, upper, lower) | ||||
| Donchian | length: int | Donchian middle = (Highest(high)+Lowest(low))/2 | SeriesF² | ||||
| ZigZag | left_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) | ||||
| Uo | fast_len: int, middle_len: int, slow_len: int | Ultimate Oscillator (пример: 7/14/28) | SeriesF³ | ||||
| Tsi | src: SeriesF, long_len: int, short_len: int | True Strength Index = двойное EMA-сглаживание Change (пример: 21/12); внутр. помощник DoubleSmooth | SeriesF | ||||
| Roc | src: SeriesF, length: int | Rate of Change = 100·Change(src,len)/src[len] | SeriesF | ||||
| StdDev | src: SeriesF, length: int | Стандартное отклонение скользящего окна | SeriesF | ||||
| Dev | src: SeriesF, length: int | Среднее абсолютное отклонение от SMA (mean deviation) | SeriesF | ||||
| LinReg | y: SeriesF, length: int, offset: int=0 | Linear Regression (точка на МНК-прямой со сдвигом offset) | SeriesF | ||||
| Corr | x: SeriesF, y: SeriesF, length: int | Коэффициент корреляции = cov(x,y)/(σx·σy) | SeriesF | ||||
| Percentile | src: SeriesF, length: int, pct: float, interpolate: bool | Перцентиль окна; pct∈[0..100]; interpolate = nearest-rank vs линейная интерполяция (пример: len=12, pct=95, interpolate=True) | SeriesF | ||||
| PercentRank | src: SeriesF, length: int | % значений в окне ≤ текущего = 100·num_leq/length | SeriesF | ||||
| Median | src: SeriesF, length: int | Скользящая медиана (через SortedList) | SeriesF | ||||
| Highest | src: SeriesF, length: int | Максимум за length баров | SeriesF | ||||
| Lowest | src: SeriesF, length: int | Минимум за length баров | SeriesF | ||||
| SinceHighest | src: SeriesF, length: int | Число баров с последнего максимума в окне | Series[int] | ||||
| SinceLowest | src: SeriesF, length: int | Число баров с последнего минимума в окне | Series[int] | ||||
| SinceTrue | condition: Series[bool] | Число баров с последнего True (−1 если ещё не было) | Series[int] | ||||
| PivotHighLow | src: SeriesF, left_bars: int, right_bars: int | Пивотные хай/лоу (локальные экстремумы с подтверждением); внутри HighestLowest | tuple (pivot_high, pivot_low) | ||||
| Mfi | src: SeriesF, length: int | Money Flow Index (пример: hlc3, len=14) | SeriesF⁴ | ||||
| Mfv | — (без параметров) | Money Flow Volume = vol·((c−l)−(h−c))/(h−l) | SeriesF | ||||
| NetVolume | src: SeriesF | Накопленный нетто-объём по знаку Change(src) | SeriesF | ||||
| CumSum | src: SeriesF | Кумулятивная сумма по всей истории | SeriesF | ||||
| Sum | src: SeriesF, length: int | Скользящая сумма за length (NaN-aware, через Var) | SeriesF | ||||
| Change | src: SeriesF, length: int=1 | Разность src[0]−src[length] | SeriesF | ||||
| FixNan | src: SeriesF | Заменяет NaN на последнее не-NaN значение (forward-fill) | SeriesF | ||||
| NanToZero | src: SeriesF | Заменяет NaN на 0 | SeriesF | ||||
| Tr | handle_na: bool=False | True 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 из доки не извлекается.
Честные оговорки по извлечению
- Sar появляется в доке дважды (разделы
Sarи внутриUo) с идентичной сигнатурой(start, increment, maximum)— это одна сущность, не две. - Median и Percentile — это полноценные
class … (Algorithm)(не@algorithm-функции), с состоянием черезSortedList; параметрыcalc()извлечены полностью. - Дефолты есть только у:
Atr.ma_algorithm='RMA',Macd.ma_source='EMA'/ma_signal='EMA',Change.length=1,LinReg.offset=0,Tr.handle_na=False,ZigZag.allow_zig_zag_on_one_bar=True. У остальных классов параметры обязательные (без дефолтов в коде) — примерные значения в колонке взяты из блоков «Example» и помечены «пример:». - В задании упоминались
VwapиMedianдважды иDonchianдважды — в доке каждый по одному разу; общее число классов = 44 (не ~46). Перечисленные в задании имена все присутствуют, дополнительных классов вindie.algorithmsнет.
Ключевые пути
/Users/stotskii/research/takeprofit-charts/docs/indie__Code-examples__built-in-indicators.md— 80 встроенных индикаторов (display + полные имена)/Users/stotskii/research/takeprofit-charts/docs/indie__Library-reference__package-indie-algorithms.md— 44 примитиваindie.algorithmsс исходниками/Users/stotskii/research/takeprofit-charts/bundles2/BArdDBzG.js— это CodeMirror/Lezer-парсер Indie-кода (Sentry releasemaster-dae5e30a), не рантайм-stdlib; имена алгоритмов в нём как plain-строки не хранятся (Indie исполняется как компилированный/WASM), поэтому канон сигнатур — из docs, а не из этого бандла.
Инструменты рисования — 25 живых скринов + доки
Каждый инструмент нарисован вживую на DOGE/USD · 1h (активация хоткеем → отрисовка точек → кадр → очистка через «Remove Drawings»). 25 из ~33 сняты в живом терминале; остальные ~8 (Rectangle/Circle/Ellipse/Fib Retracement/Date Range/Measure/Text/Callout) — их хоткеи Option+<буква> перехватывает браузер как ввод спецсимвола, а drawing-тулбар (hover-reveal) не отрисовывается под автоматизацией; они показаны скринами из доков ниже + в таблице параметров.
Живые инструменты (нарисованы на чарте)
Скрины из официальных доков (паттерны, фигуры, Free-form, FRVP)
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 Measure | Measures | Shift + M | 2 (start, end) | Эфемерный (не остаётся на чарте); Δцены (abs/%/тики), баров (дни/часы), объём |
| Date & Price | Measures | — | 2 | Как Quick, но остаётся; Δцена + время, abs + % |
| Date Only | Measures | Alt + D | 2 | Только время: счёт баров → недели/дни/часы по ТФ чарта |
| Price Only | Measures | Alt + Shift + M | 2 | Только цена: вертикальная Δ, abs + % |
| Long-Short Tool | Measures | Alt + L | 3 (entry + TP-уровень + SL-уровень, тянутся) | Мульти-TP/SL, risk-ratio, sizing — см. блок ниже |
| Fixed Range Volume Profile | Measures | — | 2 (start/end диапазона) | Гистограмма объёма по ценам: POC, VAH, VAL, Up/Down volume, Value Area up/down, bar width, From/To координаты, шаблоны |
| Standard Line | Lines | Alt + T | 3 (start, end, middle) | Кастомный текст на линии (в шаблонах), extension L/R, middle point toggle |
| Arrow | Lines | — | 3 (как Line) | Стрелка на endpoint, направленность |
| Ray | Lines | — | 2 (start + направление) | Бесконечно в одну сторону |
| Trend Angle | Lines | — | 2 | Показывает угол в градусах (°) у линии, live-обновление (код: degree/°) |
| Horizontal Ray | Lines | Alt + Shift + H | 1–2 (price level) | Горизонталь вправо до края |
| Horizontal Line | Lines | Alt + H | 1 (price) | Фикс. ценовой уровень |
| Vertical Line | Lines | Alt + V | 1 (time) | Фикс. момент времени |
| Cross Line | Lines | Alt + X | 1 (точка) | Пересечение H+V |
| Parallel Channel | Lines | Alt + Shift + C | 3 (2 базовых + ширина) | 2 параллельные границы + опц. middle line, угол/ширина, extend |
| XABCD Pattern | Patterns | — | 5 (X,A,B,C,D / 4 ноги) | Гармоники: Gartley/Butterfly/Crab/Bat (bull/bear) |
| Cypher Pattern | Patterns | — | 5 (X,A,B,C,D) | PRZ в зоне XD, Fib 0.786–0.886; M-(bull)/W-(bear) |
| Head & Shoulders | Patterns | — | ~5–7 (L-плечо, голова, R-плечо + neckline) | Bear (топ) / Bull-inverse (дно), neckline-пробой |
| ABCD Pattern | Patterns | — | 4 (A,B,C,D / 3 ноги) | AB=CD, Classic (CD=127.2/161.8% BC), Extension (от AB); показывает % ratio |
| Triangle Pattern | Patterns | — | 4 (A,B,C,D на чередующихся хай/лоу) | Symmetrical/Ascending/Descending |
| Three Drive | Patterns | — | 5 (3 драйва 1/2/3 + 2 ретрейса A/C) | Drives 127.2/161.8%, ретрейсы 61.8/78.6%, симметрия по цене+времени |
| Elliott Impulse | Patterns | — | 6 (точки 1-2-3-4-5 = 5 волн) | Motive 5 волн, правила волн 2/3/4; lookback ~600 баров (код) |
| Elliott Correction | Patterns | — | 4 (A-B-C = 3 волны) | Corrective zigzag 5-3-5 |
| Circle | Shapes | Alt + O | 2 (center + radius) | Line color/thickness/style, fill + transparency |
| Rectangle | Shapes | Alt + R | 2 (углы) | Текст внутри (в шаблонах), Extended Top/Bottom/Left/Right, Middle Line (50%), bg+transparency |
| Triangle (shape) | Shapes | Alt + G | 3 (A,B,C) | Каждая вершина → context-меню (H/V-линия, H-ray); line+fill |
| Ellipse | Shapes | Alt + Shift + O | 2 (start + drag) | Овал, line+fill+transparency |
| Curve | Shapes | Alt + K | 3 (start, end, middle control) | Кривая линия, БЕЗ заливки |
| Arc | Shapes | Alt + Shift + K | 3 (start, end, control) | Сектор С заливкой |
| Pen | Free-form | Alt + P | freehand (poly) | Только цвет (доп. опции «coming soon») |
| Highlighter | Free-form | Alt + I | freehand (drag) | Цвет; фикс. прозрачность |
| Path | Free-form | Alt + Shift + P | N точек (клик; dbl-click/Enter завершить) | Замкнутая ломаная из прямых; только цвет |
| Polyline | Free-form | Alt + N | N точек | Открытая ломаная; только цвет |
| Text | Text | Alt + Shift + T | 1 (точка) | B/I/U/strike, размер, выравнивание, цвет текста+фон, списки (bullet/number), exact price |
| Callout | Text | Alt + Shift + Y | 1–2 (текст + указатель) | Как Text + указатель 8 направлений (up/down/L/R + 4 угла) |
| Price Tag | Text | Alt + Shift + G | 1 (price) | Как Callout, маркер с ценой; указатель 8 направлений |
| Fibonacci Retracement | Fibonacci | Alt + F | 2 (Price 1, Price 2) | Reverse, Prices, Levels, Labels, font, bg fill+opacity, add/remove levels, координаты по барам |
| Fibonacci Extension | Fibonacci | Alt + Shift + F | 3 (Price 1, 2, 3) | Те же опции, что Retracement; проекция за P2 |
| Fib Speed Resistance Fan | Fibonacci | — | 2 (anchor + opposite pivot) | Диагональные лучи; Reverse, Grid, per-level palette, L/R/Top labels, bg gradient |
| Fibonacci Time Zone | Fibonacci | — | 2 (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 — что моделирует каждый
| Паттерн | Геометрия | Назначение |
|---|---|---|
| XABCD | 5 точек / 4 ноги (XA, AB, BC, CD), соседние ноги в противофазе | Гармоники Gartley, Butterfly, Crab, Bat (bull/bear); зона разворота + цели |
| Cypher | 5 точек (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 |
| ABCD | 4 точки / 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 Drive | 5 точек: драйвы 1/2/3 по тренду + ретрейсы A/C | Разворот; драйвы 127.2/161.8% ретрейсов, ретрейсы 61.8/78.6% (в сильном тренде 38.2/50%), симметрия цена+время |
| Elliott Impulse | 5 волн (1-2-3-4-5) | Motive по тренду; W2 не >100% W1, W3 за концом W1 и не самая короткая, W4 не входит в зону W1; нотация (1)..(5) и под-волны; lookback ~600 баров |
| Elliott Correction | 3 волны (A-B-C) | Коррекция против тренда; zigzag 5-3-5, WB короче WA, WC за концом WA |
Long-Short Tool — мульти-TP/SL и risk-ratio
Подтверждено в коде (BgH94ARf.js):
- Флаги
multiTPEnabled/multiSLEnabled— поддержка нескольких уровней. - Массивы
tp:[]иsl:[], каждый уровень ={qty, profitPrice, price, enabled}(фикс. длинаIa) — цена + объём на каждый шаг → частичное закрытие позиции. riskRatio(встречается 18×; дефолты0.5/1/1.5/2), отдельныйfixedRiskRatioдля Fixed-режима.- Sizing:
accountSize,lotSize(account-based: размер позиции от депозита и % риска). - Стили:
tp/slpriceLine + triggerLine,bgVisible, кастомные цвета entry/exit/зон, маркеры на каждый TP/SL.
Режимы 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 Mode | Cmd/Ctrl (hold = strong/temp) | Regular: снап к значимым хай/лоу. Strong: к каждому OHLC бара. Клик по иконке = standard; hold Cmd/Ctrl = strong/временный |
| Hide/Show | Cmd/Ctrl + H (скрыть все) | Временно прячет рисунки без удаления; индивидуально через properties; по группам |
| Sync | — (выбор уровня в настройках) | Уровни: Global (все чарты/воркспейсы), Workspace, Channel (связанные чарты), Local (без синка, дефолт) |
| Lock | Cmd/Ctrl + L | Блок перемещения/правок; лок одиночный и групповой |
| Favorites | — (звезда) | Сохранение конфигов рисунков (Fib-уровни, цвета, шаблоны паттернов) для быстрого применения к новому рисунку |
| Remove All | иконка корзины → «Delete all drawings» | Удаляет ВСЕ рисунки (включая locked); отменяемо Ctrl + Z |
Object Tree (Layers)
Открывается иконкой «Layers». Централизованное управление всеми элементами чарта:
- Группировка: выбор через Ctrl/Shift → Group icon → переименование групп.
- Управление группой (right-click): Visibility (скрыть/показать), Lock, Ordering (Bring to Front / Send to Back / Forward / Backward), Duplication (со всеми настройками), Rename, Synchronization (global/workspace/channel/local).
- Bulk-операции: мультивыбор → массовое удаление, применение настроек, раскладка по группам.
- Visibility control: переключение аналитических слоёв/сценариев, снижение визуального шума при сохранении работы.
Общие настройки (shared)
- Line tools: цвет/стиль (solid/dashed/dotted)/толщина, background, extend L/R, Middle Point toggle, sync-уровни, Lock, текст на линии (Standard Line).
- Measurement tools: line color/style/thickness, background, шаблоны, sync, Lock.
- Shapes: line color/thickness/style, fill color + transparency, enable/disable fill (у Curve заливки нет).
- Все рисунки: шаблоны (Templates) + Reset to Default, Favorites, sync, Lock, visibility.
Расхождение код↔доки: дефолтный набор 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.QuoteApi | QuoteStream, ListQuotes |
| Свечи | marketdata.external.candle.v1.CandleOffsetApi / ExtrapolationApi | QueryCandleOffsets |
| Фундамент | marketdata.external.fundamental.v2.FundamentalApi | GetFundamentalCatalogue, ListCategories, ListModes, ListFundamentals |
| Ratio | marketdata.external.ratio.v2.CatalogueApi | — |
| Скринер | screener.external.v2.StockScreenerApi | Search; CategoryApi: ListCategories, ListCategoryGroups; v1 SearcherApi |
| Справочники | reference.external.{security,exchange,industry} | ListSecurities, ListSecurityClasses, и т.д. |
| Индикаторы | indicator.controller.v2.ControllerApi | GetIndicatorStream, GetIndicatorHistory, GetStrategyOrdersAndTradesHistory, GetIndicatorsInfo |
| Миграция кода | indicator.code_migrator.v1.CodeMigratorApi | MigrateCode (Pine→Indie) |
| Бэктест-отчёт | indicator.strategy_report_stylist.v1.StrategyReportStylistApi | (AI-стилизация отчёта) — найдено в DnLmMLX5.js |
| Алерты | alerts.tpi_alerts.v1.AlertsApi (+Notifications, PopupSender) | CreateAlert, …, GetUserAlertLog |
| Трейдинг | trading.trading.v1.TradingApi | PlaceOrder, CancelOrder, UpdateOrder, GetEventsStream, StoreApiKey, ListAdaptors, GetAdaptorCapabilities, GetPlaceOrderData(Stream), SetLeverage, SetMarginMode, SwitchSpotMarginTrade, SetPositionMode |
| Telegram | telegram_connector.v1.TelegramConnectorApi | (алерты в TG) |
1. Chart
- Назначение: неогранич. число независимых графиков; линковка для синхро-анализа.
- Контролы (top panel): TF-селектор, тип графика (candles/line/bars),
f-иконка (индикаторы+маркетплейс),VS(сравнение инструментов), Object tree, Undo/Redo, three-dot (Settings/Duplicate/Delete), Share, Maximize, Linking. - Данные:
QuoteApi.QuoteStream/ListQuotes+CandleOffsetApi.QueryCandleOffsets; индикаторы черезControllerApi.GetIndicatorStream. - Настройки (5–6 категорий,
Chart-settings.md): ① Style (тип; цвета body/border/wick/last-visible-price/last-price/volume; toggle видимости каждого; продвинутый color-picker с HEX/preset/gradient); ② Canvas (фон, текст, grid, crosshair solid/dashed/dotted, margins right/top/bottom); ③ Security Label (logo/имя/биржа/статус; OHLC/change/volume); ④ Scales (Regular/Logarithmic/Percent/Index-to-100; last-price label/overlapping; Extended Hours; timezone); ⑤ Alerts (alert lines/inactive/ticker labels); ⑥ Templates (сохранение конфигурации). Use-as-default / Reset. - Под-фичи: Comparison/
VS(несколько инструментов цветными линиями, percentage-режим по умолчанию vs absolute, eye/bin); Drawing toolbar (8 категорий: Measures, Lines, Patterns, Shapes, Drawings, Fibonacci, Text + utility Magnet/Hide/Sync/Lock/Favorite); bottom timeline (range 1d→full history, scale-режимы, timezone); Sharing (публикация в комьюнити, embed). - TF (
Chart-timeframes.md): in-house time-series движок, всё строится on-the-fly из raw quote; сек 1/3/5/10/15/30/45, мин 1/3/5/15/30/45, час 1/2/3/4, день/неделя/месяц + кастомные TF. Tick-бары: 1–49 ticks бесплатно на всех потоках; 50–1000 (шаг 50) — только Binance/Bybit/Pepperstone/Exness. Favorites (звезда). - Лимиты: unlimited charts; глубина истории = max доступная по символу (daily+ глубже, intraday/tick могут ограничиваться провайдером/планом).
2. Watchlist (68OUmlCg.js)
- Назначение: мониторинг наборов инструментов в реал-тайме.
- Данные:
QuoteApiстримы котировок; добавление через Screener-линковку и хоткей Alt+W (активный символ чарта). - View-режимы (
Watchlist-display-options.md): Lite (default: имя/цена/изменение, чекбоксы, сортировка); Table (полное имя, цена, change abs+%, High/Low, extended hours); Heatmap (группировка по volume(default)/типу(equity/crypto/funds)/exchange/currency; красно-зелёная шкала от open; zoom колесом/±, hover-детали). - Навигация: dropdown с Recent / My Watchlists / Built-in; меню Rename/Duplicate/Delete (built-in только дублируются).
- Под-фичи: мульти-селект (Shift+Click, Ctrl/Cmd+A); сортировка синхронится между Lite/Table.
- Лимиты: unlimited виджетов, до 1000 инструментов на watchlist. Sharing — "coming soon".
3. Stock Screener (StockScreenerApi.Search, CategoryApi)
- Назначение: отбор акций по фундаменту/технике, >80–100 параметров.
- Данные:
screener.external.v2.StockScreenerApi.Search(params:conditions, fields, limit, properties, skip, sort); категории —CategoryApi.ListCategories/ListCategoryGroups; heatmap-конфигje.SCREENER_HEATMAP. - Покрытие: US-рынок (~5000 компаний), включая pre/post-market в фильтрах и колонках.
- Периоды (
Stock-screener-overview.md): TTM, MRQ, Fiscal Year, YoY growth, CAGR 3Y/5Y. - Фильтры: range-слайдеры, операторы below/enter/above; "Apply Changes". Источники наборов: Built-in / модификация / дубль / blank.
- View: Table (кастом-колонки, per-column фильтр), Lite, Heatmap (
groupByenum в коде:INDUSTRY_SECTOR_NAME(default),EXCHANGE_ALIAS,QUOTE_ASSET_CODE; Market Structure/Business Categories/Performance). - Колонки: дефолт = Price, Change 1d%, Volume 1d, Market cap, P/E TTM, Div Yield FY, Perf 1Y, Sector; 80+ параметров, кастом-columnsets (New/Quick/Edit, Save as).
- Сортировка: Primary (Sort by) + Secondary (клик по заголовку).
- Под-фичи: auto-refresh 30s / 1m / 3m / off; интеграция (клик по строке обновляет linked Chart/Financials); Sharing custom-скринеров (Title 5–120, Desc ≤500, cover ≤2MB; на
/@user/screeners; реф-ссылка/монетизация; редактирование только через support). - Лимит: Select-All берёт только подгруженные строки.
4. Financials (FundamentalApi)
- Назначение: структурированный фундамент компании; линкуется с Screener/Watchlist.
- Данные:
marketdata.external.fundamental.v2.FundamentalApi—GetFundamentalCatalogue, ListCategories, ListModes, ListFundamentals; ratio черезCatalogueApi. - Отчёты: Income Statement, Balance Sheet, Cash Flow, Statistics (P/E, P/S, P/B…).
- Периоды: Annual (Fiscal Year), Quarterly, TTM. Глубина: 10 лет годовых / 12 кварталов (quarterly не для всех).
- Display: единицы M/B; валюта USD (others "coming soon"); точность .0/.00; Swap Chart↔Table; collapsible-divider.
- Table-фичи: Common Size (%), %YoY (на таблицу или метрику); добавление метрик на график (Line/Area/Column/Stacked/Percent + Separated Axis, hide/show).
- Presets (9 built-in): Valuation, Dividends & Buyback, Profit Margin, Expense Breakdown, Return Analysis, ROE Analysis, Solvency, Financial Health, Asset Conversion Cycle + кастомные.
- Sharing: 6 тем, выбор колонок/метрик, тип графика bar/column/line, Enable Copying, ссылка/embed/Post-it, Save-as-image.
5. Market Depth / Order Book (CXdJCvTF.js)
- Назначение: реал-тайм ликвидность и order-flow для крипты, 70+ бирж.
- Данные: свой
orderbookStream+tradesStream(r.orderbook.getStream(...),.subscribe(), авто-reconnect()); WS-источник по выбранной бирже. - Элементы: Price ladder, Bids(зел)/Asks(крас), liquidity bars, volume labels, spread, cumulative depth curve, trade clusters.
- Режимы: Depth (resting-ликвидность, bid/ask ladders, walls) и Flow (+executed trades, агрегированные prints, кластеры-«свечи»: зел=агрессивные покупки, крас=продажи).
- Настройки (код-поля): General — Depth/Flow, Bid/Ask Ratio, Show Empty Rows,
priceStep(×10 и т.п., шаговый ±-контрол в хедере), Font Size, Auto-centered; Volume — Volume Bars, Volume Scale (Linear),cumulative; Trades(Flow) — Display Type,aggregation/aggregationGap, Volume Filter; Clusters(Flow) —clusterTimeframe(min 60000ms, default 300000ms=5m),clustersNumber,clusterWidth, Skip Empty Clusters. Reset to Default; centering-кнопка. - Лимит: только крипто-инструменты.
6. Indie® Code Editor / IDE (ControllerApi, CodeMigratorApi)
- Назначение: создание/правка индикаторов и стратегий на языке Indie®; публикация в Marketplace (комиссия/рефералы).
- Данные/исполнение:
ControllerApi.GetIndicatorStream/GetIndicatorHistory/GetIndicatorsInfo; Pine→Indie конвертацияCodeMigratorApi.MigrateCode. - Контролы: dropdown (Built-in / Marketplace / свои) → выбор создаёт Fork; имя редактируемое; three-dot (Fork/Delete); Publish; version-history (автосейв после каждого изменения); Indie-docs; Find&Replace; Duplicate.
- Bottom panel: Add to Chart (в выбранный активный чарт), Console (логи/ошибки
IndieError). - Indicator Settings (на чарте,
indicator-settings.md): gear рядом с именем (осцилляторы — снизу); Inputs (Length, Source, StdDev/Offset/Multiplier) + Outputs (цвета/стили линий, толщина-слайдер, toggle видимости каждого output, напр. BB: Lower/Basis/Upper/Background); Reset Settings. - Под-фичи: несколько IDE одновременно; linked-charts имеют независимые наборы индикаторов (добавлять в каждый отдельно). Доступ к Source Code open-source индикатора авто-создаёт форк.
7. Strategy / Backtesting (DnLmMLX5.js, GetStrategyOrdersAndTradesHistory, StrategyReportStylistApi)
- Назначение: тест стратегий на истории; связка Charts+IDE.
- Данные: прогон через
ControllerApi.GetStrategyOrdersAndTradesHistory; отчёт стилизуетсяStrategyReportStylistApi(AI). - Built-in стратегии: ADX Breakout, MACD, Moving Average Cross, RSI, Price Channel, Consecutive Up/Down, Inside/Outside Bar.
- Custom: Create New Strategy → Indie editor (strategy framework,
docs/indie/Strategies). - Settings (inputs): Initial Capital, Commission (value+type), Leverage, Risk-free rate, Order size + unit, Market order type, Intrabar execution rules + логика стратегии — мгновенный re-run.
- Date range: Start (Start-Available огранич. планом / From-Custom) + End (Now=forward-testing / Until-Custom), dual-calendar.
- Метрики: Core (Total P&L, Win Rate, Profit Factor, Total/Wins/Losses, Avg Trade, Expectancy, Risk-Reward); Capital&Returns (Initial/Final, Realized/Unrealized, Annualized/Buy&Hold/Avg-Monthly, Min/Max Equity); Risk (Max/Avg/Number Drawdown, MaxDD Duration, Sharpe); Volatility&Recovery (Sortino, Calmar, Daily StdDev, VaR 95%/99%, Annualized Vol, Avg/Max Recovery Time); Trade Quality (Best/Worst Net P&L, Commission-to-Profit).
- Логи: Trades tab (ID, Long/Short, entry/exit, duration, P&L, cumulative, drawdown/run-up); Orders tab (ID, Market/Limit/Stop, price, qty, exposure, fee).
- Визуал: equity curve, drawdown chart, position activity, trade distribution. Side-by-side сравнение через 2 виджета.
- Лимит: глубина истории Start-Available ограничена планом.
8. Feed (block-editor read-only render)
- Назначение: лента комьюнити-постов внутри workspace.
- Контент: title + intro-текст; клик → полный пост открывается в новой вкладке (layout не меняется).
- Интеракции без выхода: Like, Report.
- Лимиты: показывает все посты; фильтр (по подпискам/персонализация) — "coming soon".
9. Notes (block-editor E5A4FP5n.js)
- Назначение: приватный репозиторий идей/анализа; привязка к тикеру обязательна.
- Редактор: общий с постами block-editor (heading/blockquote/code-block — найдено в коде; H1-H2, bullet/numbered lists, image/video embed, X/Twitter-embed, highlights, callouts).
- Хранение: заметки авто-видны во всех workspace; крипто — отдельные заметки на пару per-exchange (BTC/USDT уникальна на каждой бирже).
- Управление: "All notes" (заголовки/preview/timestamp); Delete (удаляет из всех workspace).
- Приватность: private by default; sharing — "coming soon".
10. Account (C2qhB3iH.js)
- Назначение: не отдельный workspace-виджет, а управление профилем + подключение брокеров/бирж (API-ключи для трейдинга).
- Данные:
TradingApi—StoreApiKey/UpdateApiKey/ListAllApiKeyInfos/DeleteApiKey,ListAdaptors,GetAdaptorCapabilities. В коде:tradingProvider.apiKeys(add/remove),membershipModalState(подписка/membership-gating подключения),setMarginMode,getStream({securityId, apiKeyName}). - Под-фичи трейдинга (capabilities): order types MARKET/LIMIT/STOP/STOP_LIMIT/TRAILING, TIF GTC/IOC/FOK/DAY, SetLeverage, SetMarginMode, SetPositionMode, SwitchSpotMarginTrade. Биржи: Binance/Bybit/Gate/OKX и др. через адаптеры.
- Профиль/безопасность (docs getting-started): Profile-settings, Registration, MFA/2FA (
mfa.md), Workspaces.
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 |
| J2T | J2TOrder + J2TPortfolio | Аналогичная пара третье-стороннего брокера | через TradingApi адаптер |
| Ilotcos | IlotcosOrder + IlotcosPortfolio | Аналогичная пара | через TradingApi адаптер |
- Общее: Order = размещение/управление ордерами; Portfolio = просмотр позиций/доступных инструментов. Документация по правилам — на стороне брокера (для Lime — lime.co/faq). Order-типы и capabilities определяются
GetAdaptorCapabilitiesконкретного адаптера. - В layout-шаблонах присутствуют только Lime-комбинации (LimePortfolio+LimeOrder+Chart+Watchlist и т.п.); J2T/Ilotcos в шаблонах не предзаданы.
Бонус — 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.