Спектра Чартс

Создаем осцилляторы для Spectra Charts в отдельном окне под графиком

Пошаговое руководство, как обучить GPT создавать осцилляторы в нижнем окне Spectra Charts и использовать их в торговле бинарными опционами на Pocket Option.

Создаем визуальный инструмент для Spectra Charts с отрисовкой линий поверх графика

Отрисовка осцилляторов

Это третий пост-инструкция по созданию собственных индикаторов с отрисовкой их на графике. В первых материалах мы уже разобрали:

  • как создавать сигнальные боты с метками BUY и SELL;
  • как делать визуальные инструменты поверх графика — линии тренда, зоны и уровни.

В формате данного материала мы с вами научимся, как правильно создавать осцилляторы, открываемые в нижнем дополнительном окне под графиком в Spectra Charts. Такие индикаторы особенно полезны при торговле бинарными опционами, когда важно видеть не только саму цену, но и её «настроение» — ускорение, замедление, зоны перекупленности и перепроданности.

Что такое осциллятор простыми словами

Осциллятор — это отдельный индикатор под графиком цены, который:

  • колеблется вверх-вниз в определённом диапазоне;
  • помогает оценить силу движения цены;
  • часто показывает моменты, когда рынок «перегрет» и готов к коррекции.

Примеры осцилляторов:

  • Моментум (Momentum) — показывает, насколько быстро растёт или падает цена.
  • Разные вариации MACD, RSI и других классических индикаторов, но в нашем случае в формате кастомных инструментов Spectra Charts.
Важно!
  • Не путайте осциллятор в дополнительном окне с индикаторами, создаваемыми поверх графика, как в предыдущем посте.
  • Осциллятор всегда живёт в отдельной панели под основным графиком, не перекрывает свечи и не мешает чтению цены.

С чего начать

Как и в предыдущих постах, главное — обучение нейросети. Делается это очень легко: достаточно скопировать всего один промпт, и вы получите полностью обученный чат, в котором сможете создавать любые индикаторы-осцилляторы под графиком.

Важно любое обучение производить в новом, полностью пустом чате. Допускается обучение одного чата сразу всем возможностям:
  1. Сигналы (сигнальные боты).
  2. Создание визуальных линий и зон поверх графика.
  3. Создание осцилляторов в нижнем окне.

Главное условие — аккуратно отправить обучающий промпт целиком, без обрезаний и пропусков. Тогда GPT будет понимать структуру кода и сможет дальше генерировать корректные осцилляторы под ваши задачи.


1. Обучающий промпт для создания кода

Этот промпт — основа. Вы даёте его GPT один раз, и после этого чат становится «обученным» на создание осцилляторов для Spectra Charts.

Текст промпта, который вы даёте GPT перед вставкой первого кода:

- Я тебе даю код, тебе нужно изучить его и понять, как правильно рисовать осцилляторы (индикаторы) в дополнительном окне под графиком.
- Так же изучи структуру кода для правильного написания индикаторов.
- Когда будешь писать мне исправления кода, обновления, вносить мелкие правки, всегда отдавай готовый код полностью.
- В ответ скажи мне, что всё понял, и жди от меня дальнейших команд.
- Настройки в параметрах всегда прописывай на моём языке понятными словами.
- Код для обучения:
// =========================== Phantom Wave Oscillator ===========================
// Осциллятор в отдельной панели. Основа — волновая логика (в стиле WaveTrend):
//  AP = источник (по умолчанию HLC3)
//  ESA = EMA(AP, channelLength)
//  DEV = EMA(|AP - ESA|, channelLength)
//  CI  = (AP - ESA) / (0.015 * DEV)
//  PWO = EMA(CI, averageLength) — основная линия
//  SIG = EMA(PWO, signalLength) — сигнальная линия
//  HIST = PWO - SIG — гистограмма
//
// Показ значений на ховере: серии всегда отдают значение (0 до "прогрева").
// Отрисовка: линии + гистограмма + нулевая линия + уровни +L / -L.

export const meta = {
  name: "Phantom Wave Oscillator",
  pane: "separate",
  defaultParams: {
    // === Значения (вверху) ===
    source: "HLC3",         // Close | HL2 | HLC3
    channelLength: 10,      // период ESA/DEV
    averageLength: 21,      // период EMA для сглаживания CI
    signalLength: 4,        // период сигнальной EMA
    levelUp: 60,            // верхний уровень
    levelDown: -60,         // нижний уровень

    // === Вид (внизу, сгруппировано) ===
    // Основная линия
    showMain: true,
    mainColor: "#06b6d4",
    mainWidth: 2,

    // Сигнальная линия
    showSignal: true,
    signalColor: "#f59e0b",
    signalWidth: 2,

    // Гистограмма
    showHist: true,
    histUpColor: "#22c55e",
    histDownColor: "#ef4444",
    histWidth: 2,

    // Нулевая линия
    showZero: true,
    zeroColor: "#6b7280",
    zeroWidth: 1,
    zeroStyle: "dashed", // dashed | solid

    // Уровни
    showLevelUp: true,
    levelUpColor: "#a855f7",
    levelUpWidth: 1,
    showLevelDown: true,
    levelDownColor: "#a855f7",
    levelDownWidth: 1
  },
  paramMeta: {
    // === Значения ===
    source: {
      label: "Источник",
      type: "select",
      options: [
        { label: "Close", value: "Close" },
        { label: "HL2 (H+L)/2", value: "HL2" },
        { label: "HLC3 (H+L+C)/3", value: "HLC3" }
      ]
    },
    channelLength: { label: "Период канала (ESA/DEV)", type: "number", min: 2, max: 300 },
    averageLength: { label: "Период сглаживания (EMA)", type: "number", min: 2, max: 300 },
    signalLength:  { label: "Период сигнала (EMA)",     type: "number", min: 1, max: 300 },
    levelUp:       { label: "Уровень +L",               type: "number", min: -2000, max: 2000, step: 1 },
    levelDown:     { label: "Уровень −L",               type: "number", min: -2000, max: 2000, step: 1 },

    // === Вид ===
    showMain:    { label: "Основная линия", type: "boolean" },
    mainColor:   { label: "Цвет основной",  type: "color" },
    mainWidth:   { label: "Толщина основной (px)", type: "number", min: 1, max: 5 },

    showSignal:  { label: "Сигнальная линия", type: "boolean" },
    signalColor: { label: "Цвет сигнальной",   type: "color" },
    signalWidth: { label: "Толщина сигнальной (px)", type: "number", min: 1, max: 5 },

    showHist:     { label: "Гистограмма", type: "boolean" },
    histUpColor:  { label: "Цвет гисто >0", type: "color" },
    histDownColor:{ label: "Цвет гисто <0", type: "color" },
    histWidth:    { label: "Толщина гистограммы (px)", type: "number", min: 1, max: 5 },

    showZero:   { label: "Линия 0", type: "boolean" },
    zeroColor:  { label: "Цвет линии 0", type: "color" },
    zeroWidth:  { label: "Толщина линии 0 (px)", type: "number", min: 1, max: 5 },
    zeroStyle:  { label: "Стиль линии 0", type: "select", options: [
      { label: "Пунктир", value: "dashed" },
      { label: "Сплошная", value: "solid" }
    ]},

    showLevelUp:   { label: "Показывать +L", type: "boolean" },
    levelUpColor:  { label: "Цвет +L",       type: "color" },
    levelUpWidth:  { label: "Толщина +L (px)", type: "number", min: 1, max: 5 },

    showLevelDown: { label: "Показывать −L", type: "boolean" },
    levelDownColor:{ label: "Цвет −L",       type: "color" },
    levelDownWidth:{ label: "Толщина −L (px)", type: "number", min: 1, max: 5 }
  }
};

export function init(ctx) {
  const { chart, addSeries, LineSeries, HistogramSeries, paneIndex, params } = ctx;

  // ==== Серии (общая шкала) ====
  const scaleId = "right";
  const main = addSeries(LineSeries, { priceScaleId: scaleId, lineWidth: 2, priceLineVisible: false, lastValueVisible: false }, paneIndex);
  const signal = addSeries(LineSeries, { priceScaleId: scaleId, lineWidth: 2, priceLineVisible: false, lastValueVisible: false }, paneIndex);
  const hist = addSeries(HistogramSeries, { priceScaleId: scaleId, base: 0, priceLineVisible: false }, paneIndex);
  const zero = addSeries(LineSeries, { priceScaleId: scaleId, lineWidth: 1, priceLineVisible: false, lastValueVisible: false }, paneIndex);
  const lvlUp = addSeries(LineSeries, { priceScaleId: scaleId, lineWidth: 1, priceLineVisible: false, lastValueVisible: false }, paneIndex);
  const lvlDn = addSeries(LineSeries, { priceScaleId: scaleId, lineWidth: 1, priceLineVisible: false, lastValueVisible: false }, paneIndex);

  // ==== Utils ====
  const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
  const clampW = (v, d=1) => { const n=Number(v); return Math.max(1, Math.min(5, Number.isFinite(n)? Math.round(n): d)); };

  function pickPrecision(sample){
    if(!Number.isFinite(sample) || sample===0) return 5;
    const v = Math.abs(sample);
    if (v>=100) return 2;
    if (v>=10)  return 3;
    if (v>=1)   return 4;
    if (v>=0.1) return 5;
    return 6;
  }

  function EMA(src, len){
    const n=src.length, out=new Array(n).fill(undefined);
    if(!n || len<=0) return out;
    const a = 2/(len+1);
    let v = src[0];
    out[0] = v;
    for(let i=1;i<n;i++){ v = a*src[i] + (1-a)*v; out[i]=v; }
    return out;
  }

  // Основной расчёт
  function calcPWO(candles, srcType, chLen, avgLen, sigLen){
    const n=candles.length; if(!n) return { t:[], pwo:[], sig:[], hist:[] };
    const t = candles.map(c=>c.time);
    let src;
    switch(srcType){
      case "Close": src = candles.map(c=>c.close); break;
      case "HL2":   src = candles.map(c=>(c.high+c.low)/2); break;
      default:      src = candles.map(c=>(c.high+c.low+c.close)/3); // HLC3
    }

    // ESA & DEV
    const esa = EMA(src, chLen);
    const absDiff = src.map((v,i)=> Math.abs(v - (esa[i] ?? v)));
    const dev = EMA(absDiff, chLen).map(v=> (v && v!==0) ? v : 1e-12);

    // CI → PWO → SIG
    const ci = src.map((v,i)=> (v - (esa[i] ?? v)) / (0.015 * dev[i]));
    const pwo = EMA(ci, avgLen);
    const sig = EMA(pwo.map(v=> Number.isFinite(v)? v : 0), sigLen);

    // HIST
    const hist = pwo.map((v,i)=> (Number.isFinite(v) && Number.isFinite(sig[i])) ? (v - sig[i]) : 0);

    // Заполняем нулями до «прогрева», чтобы были значения на ховере
    const safe = arr => arr.map(v => Number.isFinite(v) ? v : 0);

    return { t, pwo: safe(pwo), sig: safe(sig), hist: safe(hist) };
  }

  function update(candles){
    if(!candles || candles.length===0){
      main.setData([]); signal.setData([]); hist.setData([]); zero.setData([]); lvlUp.setData([]); lvlDn.setData([]);
      return [];
    }

    // === Параметры ===
    const srcType = (params.source === "Close" || params.source === "HL2" || params.source === "HLC3") ? params.source : "HLC3";
    const chLen   = Math.max(2, +params.channelLength || 10);
    const avgLen  = Math.max(2, +params.averageLength || 21);
    const sigLen  = Math.max(1, +params.signalLength  || 4);
    const Lup     = Number(params.levelUp ?? 60);
    const Ldn     = Number(params.levelDown ?? -60);

    const { t, pwo, sig, hist: H } = calcPWO(candles, srcType, chLen, avgLen, sigLen);

    // Формат шкалы
    const last = pwo.length ? pwo[pwo.length-1] : 0;
    const prec = pickPrecision(last);
    const minMove = Math.pow(10, -prec);
    for (const s of [main, signal, hist, zero, lvlUp, lvlDn]) {
      s.applyOptions?.({ priceFormat: { type: "price", precision: prec, minMove } });
    }

    // Данные
    const mainData   = t.map((time,i)=> ({ time, value: pwo[i] }));
    const signalData = t.map((time,i)=> ({ time, value: sig[i] }));
    const zeroData   = t.map(time => ({ time, value: 0 }));
    const upData     = t.map(time => ({ time, value: Lup }));
    const dnData     = t.map(time => ({ time, value: Ldn }));

    const histUpColor   = params.histUpColor   || "#22c55e";
    const histDownColor = params.histDownColor || "#ef4444";
    const histData = t.map((time,i)=> ({
      time,
      value: H[i],
      color: (H[i] >= 0) ? histUpColor : histDownColor
    }));

    // Применяем опции/видимость
    main.applyOptions({
      visible: params.showMain !== false,
      color: params.mainColor || "#06b6d4",
      lineWidth: clampW(params.mainWidth, 2)
    });
    main.setData(params.showMain !== false ? mainData : []);

    signal.applyOptions({
      visible: params.showSignal !== false,
      color: params.signalColor || "#f59e0b",
      lineWidth: clampW(params.signalWidth, 2)
    });
    signal.setData(params.showSignal !== false ? signalData : []);

    hist.applyOptions({
      visible: params.showHist !== false,
      color: histUpColor,
      lineWidth: clampW(params.histWidth, 2)
    });
    hist.setData(params.showHist !== false ? histData : []);

    zero.applyOptions({
      visible: params.showZero !== false,
      color: params.zeroColor || "#6b7280",
      lineWidth: clampW(params.zeroWidth, 1),
      lineStyle: (params.zeroStyle === "solid") ? 0 : 1 // 0=Solid, 1=Dashed
    });
    zero.setData(params.showZero !== false ? zeroData : []);

    lvlUp.applyOptions({
      visible: params.showLevelUp !== false,
      color: params.levelUpColor || "#a855f7",
      lineWidth: clampW(params.levelUpWidth, 1)
    });
    lvlUp.setData(params.showLevelUp !== false ? upData : []);

    lvlDn.applyOptions({
      visible: params.showLevelDown !== false,
      color: params.levelDownColor || "#a855f7",
      lineWidth: clampW(params.levelDownWidth, 1)
    });
    lvlDn.setData(params.showLevelDown !== false ? dnData : []);

    return []; // маркеров нет
  }

  function destroy(){
    try { chart.removeSeries(main); } catch(_){}
    try { chart.removeSeries(signal); } catch(_){}
    try { chart.removeSeries(hist); } catch(_){}
    try { chart.removeSeries(zero); } catch(_){}
    try { chart.removeSeries(lvlUp); } catch(_){}
    try { chart.removeSeries(lvlDn); } catch(_){}
  }

  return { update, destroy };
}

Что происходит после отправки этого блока:

  • GPT изучает каркас кода и «понимает», как правильно:
    • подключать осциллятор в отдельное окно под графиком;
    • описывать параметры с русскими названиями;
    • обновлять и очищать индикатор;
  • Далее вы можете просить его создавать новые варианты осцилляторов на основе этого шаблона.

2. После обучения: ставим задачи GPT

После того как вы отправили в чат обучающий промпт и получили от модели подтверждение (что всё понято и она ждёт команд), можно начинать формулировать собственные задачи по созданию визуальных инструментов под ваши нужды.

Примеры:
  • Создай классический индикатор Моментум.
  • Создай связку индикаторов Моментум и MACD.
  • Любые другие вариации.

Развернём эти примеры чуть подробнее:

  1. Простой Моментум
    Вы можете написать:
    «Создай классический индикатор Моментум в нижнем окне под графиком. Добавь параметр периода, отображай линию моментума и выделяй нулевую линию для удобства.»
  2. Связка Моментум + MACD
    Запрос может выглядеть так:
    «Создай осциллятор, где в одной панели будут линия моментума и гистограмма и одновременно будет отображаться индикатор MACD. Оформи параметры на русском языке, чтобы я мог менять период моментума и настройки MACD.»
  3. Свои вариации
    Здесь всё ограничено только вашими идеями. Главное — описывайте:
    • какие линии вы хотите видеть;
    • какие уровни нужны;
    • нужно ли что-то подсвечивать цветом;
    • как использовать индикатор в торговле бинарными опционами (фильтрация сигналов, поиск разворотов и т. д.).
Чем проще и конкретнее вы сформулируете задачу, тем точнее GPT создаст осциллятор. Не бойтесь писать своими словами, без «умных» терминов. Для нейросети главное — понятное условие.

Пример отображения осцилляторов на графике

Чтобы было проще представить конечный результат, вот пример того, как могут выглядеть осцилляторы в Spectra Charts:

В одном окне под графиком можно:

  • разместить одну или несколько линий;
  • добавить уровни (например, зоны перекупленности и перепроданности);
  • визуально отделить «спокойные» участки рынка от участков с сильным импульсом.

Для трейдера это превращается в удобный прибор: вы видите не только голый график, но и понятные подсказки, когда стоит быть осторожнее, а когда наоборот — искать точки входа.


Как правильно составлять промпт для нейросети

Нужно понимать одну важную вещь: Создать можно почти всё что угодно, но результат напрямую зависит от того, насколько правильно вы сформулируете задачу.

  • Если промпт написан расплывчато, GPT будет додумывать детали и может переписать логику по своему пониманию.
  • Очень сложные и нестандартные конструкции нейросеть иногда реализует некорректно. Для таких инструментов может понадобиться более глубокое обучение на дополнительных примерах, которые выходят за рамки этого обучающего поста.

Как повысить качество результата

Чтобы увеличить шансы получить рабочий и понятный индикатор, придерживайтесь простых правил:

Опишите цель, а не только формулу

  • Не ограничивайтесь фразой «сделай осциллятор».
  • Попробуй лучше так: «Мне нужен индикатор, который показывает ускорение цены за последние N свечей и помогает отсеивать слабые движения».

Сразу уточните визуальное оформление

Напишите, где должен находиться индикатор и как он должен выглядеть:

  • в каком окне он должен отображаться (в нижнем дополнительном окне под графиком);
  • какие нужны линии и цвета;
  • нужны ли уровни, подписи, дополнительные зоны.

Просите полный код целиком

  • Так проще вставить индикатор в Spectra Charts и протестировать его без ручной склейки обрывков.
  • Формулировка может быть такой: «Отдай полный код индикатора целиком, чтобы я мог сразу вставить его в Spectra Charts».

Храните удачные версии кода

  • Если GPT выдал рабочий и удобный индикатор, сохраните его отдельно.
  • Затем можно просить: «Возьми вот этот код и доработай его по таким-то правилам», вместо того чтобы каждый раз начинать с нуля.

Возможные проблемы и важные нюансы

В конце важно проговорить типичные ситуации, с которыми вы можете столкнуться, и сразу понимать, как их корректно решать через нейросеть.

1. Неполное удаление осцилляторов

Иногда неправильно написанные осцилляторы могут не до конца удаляться с графика. Визуальные элементы остаются на экране, и для полного сброса приходится обновлять страницу.

Чтобы такого не происходило, об этом нужно прямо сказать нейросети и попросить доработать код очистки.

Рекомендуемая формулировка для GPT (только если такие ошибки проявились):

«Сделай так, чтобы при отключении индикатора все линии, объекты и данные полностью очищались. Ничего не должно оставаться на графике, чтобы не приходилось обновлять страницу».

2. Ошибки в логике или отрисовке

Иногда индикатор может вести себя странно:

  • вообще не появляется;
  • рисуется не в том месте;
  • показывает непонятные или слишком большие значения.

В этом случае:

  1. Проверьте, не был ли код обрезан при копировании и вставке.
  2. Если Spectra Charts показывает ошибку, скопируйте её текст целиком.
  3. Отправьте эту ошибку GPT с понятной просьбой, например:

«Разберись по этой ошибке и исправь код индикатора. Отдай полностью исправленную версию».

Так вы даёте нейросети конкретную точку опоры, и она сможет не просто переписать индикатор заново, а именно устранить причину сбоя.


Это был третий обучающий материал по созданию визуальных инструментов с помощью Спектра Чартс и если у вас возникают какие-либо вопросы или трудности с реализацией, вы всегда можете задать свой вопрос в нашу поддержку через бот: https://t.me/pocketservice111 или в нашем партнерском чате «Много Денег Trade»