更新日:2024/8/24

JSTraderカスタムインジケーターサンプル


export default [
  (): ItemIndicator<
    {
      period: number;
      lineColor: string;
      lineWidth: number;
      lineDash: number[];
      ohlc?: "open" | "high" | "low" | "close";
    },
    { smaBuffer: IndicatorBuffer }
  > => ({
    type: "i",
    name: "sample.SMA",
    onInit() {
      const { lineColor, lineWidth, lineDash } = this.params;
      this.smaBuffer = this.addLineBuffer({
        lineColor,
        lineWidth,
        lineDash,
      });
      this.shortName = "SMA";
      this.onChartChange = ({ index, opens, highs, lows, closes }) => {
        const period = this.params.period;
        const ohlc = this.params.ohlc;
        const buffer =
          ohlc === "open"
            ? opens
            : ohlc === "high"
              ? highs
              : ohlc === "low"
                ? lows
                : closes;
        buffer.sma(this.smaBuffer, period, index);
      };
      this.getDisplayData = (index) => {
        return { rate: this.smaBuffer.get(index) };
      };
    },
    params: {
      period: 25,
      lineColor: "red",
      lineWidth: 2,
      lineDash: [3, 3],
      ohlc: "close",
    },
  }),
  (): ItemIndicator<
    {
      period: number;
      lineColor: string;
      lineWidth: number;
      lineDash: number[];
      ohlc?: "open" | "high" | "low" | "close";
    },
    { emaBuffer: IndicatorBuffer }
  > => ({
    type: "i",
    name: "sample.EMA",
    onInit() {
      const { lineColor, lineWidth, lineDash } = this.params;
      const emaBuffer = (this.emaBuffer = this.addLineBuffer({
        lineColor,
        lineWidth,
        lineDash,
      }));
      this.shortName = "EMA";
      this.onChartChange = ({
        index,
        times,
        opens,
        highs,
        lows,
        closes,
        spreads,
      }) => {
        const period = this.params.period;
        const ohlc = this.params.ohlc;
        const buffer =
          ohlc === "open"
            ? opens
            : ohlc === "high"
              ? highs
              : ohlc === "low"
                ? lows
                : closes;
        buffer.ema(emaBuffer, period, index);
      };
      this.getDisplayData = (index) => {
        return { ema: emaBuffer.get(index) } as const;
      };
    },
    params: {
      period: 25,
      lineColor: "green",
      lineWidth: 2,
      lineDash: [3, 3],
      ohlc: "close",
    },
  }),
  (): ContainerIndicator<
    {
      initialHeight: number | string;
      upColor: string;
      sameColor: string;
      downColor: string;
    },
    {
      candleStickBuffer: [
        IndicatorBuffer,
        IndicatorBuffer,
        IndicatorBuffer,
        IndicatorBuffer,
      ];
    }
  > => ({
    type: "c",
    name: "sample.HeikinAshi",
    onInit() {
      const { initialHeight, upColor, sameColor, downColor } = this.params;
      this.initialHeight = initialHeight;
      const [openBuffer, highBuffer, lowBuffer, closeBuffer] =
        (this.candleStickBuffer = this.addCandleStickBuffer([
          upColor,
          sameColor,
          downColor,
        ]));
      this.shortName = "heikin";
      this.isShowDefaultRoundNumber = true;
      this.onChartChange = ({
        index,
        times,
        opens,
        highs,
        lows,
        closes,
        spreads,
      }) => {
        const preOpen = opens.get(index - 1),
          preClose = closes.get(index - 1);
        openBuffer.set(index, (preOpen + preClose) / 2);
        highBuffer.set(index, highs.get(index));
        lowBuffer.set(index, lows.get(index));
        closeBuffer.set(
          index,
          (opens.get(index) +
            highs.get(index) +
            lows.get(index) +
            closes.get(index)) /
            4,
        );
      };
      this.getDisplayData = (index) => {
        return {
          open: openBuffer.get(index),
          close: closeBuffer.get(index),
        };
      };
    },
    params: {
      upColor: "blue",
      sameColor: "black",
      downColor: "red",
      initialHeight: "25%",
    },
  }),
  (): ContainerIndicator<
    {
      fastEmaPeriod: number;
      slowEmaPeriod: number;
      signalSmaPeriod: number;
      macdColor: string;
      signalColor: string;
      osciColor: string;
      initialHeight: string | number;
    },
    {
      macdBuffer: IndicatorBuffer;
      signalBuffer: IndicatorBuffer;
      osciBuffer: IndicatorBuffer;
    }
  > => ({
    type: "c",
    name: "sample.MACD",
    onInit() {
      const { initialHeight, osciColor, macdColor, signalColor } = this.params;
      this.initialHeight = initialHeight;
      const osciBuffer = (this.osciBuffer = this.addHistgramBuffer({
        backgroundColor: osciColor,
        baseValue: 0,
      }));
      const fastEmaBuffer = this.addWorkBuffer();
      const slowEmaBuffer = this.addWorkBuffer();
      const macdBuffer = (this.macdBuffer = this.addLineBuffer({
        lineColor: macdColor,
        lineWidth: 2,
      }));
      const signalBuffer = (this.signalBuffer = this.addLineBuffer({
        lineColor: signalColor,
        lineWidth: 2,
      }));
      this.shortName = "MACD";
      this.digit = 0;
      this.addPercentLine(0, "darkgray");
      this.onChartChange = ({
        index,
        times,
        opens,
        highs,
        lows,
        closes,
        spreads,
      }) => {
        const { fastEmaPeriod, slowEmaPeriod, signalSmaPeriod } = this.params;
        closes.ema(fastEmaBuffer, fastEmaPeriod, index);
        closes.ema(slowEmaBuffer, slowEmaPeriod, index);
        const macdVal = fastEmaBuffer.get(index) - slowEmaBuffer.get(index);
        macdBuffer.set(index, macdVal);
        const signalVal = macdBuffer.ema(
          signalBuffer,
          signalSmaPeriod,
          index,
          slowEmaPeriod,
        );
        osciBuffer.set(index, macdVal - signalVal);
      };
      this.getDisplayData = (index) => {
        return {
          macd: macdBuffer.get(index),
          signal: signalBuffer.get(index),
          osci: osciBuffer.get(index),
        };
      };
    },
    params: {
      fastEmaPeriod: 12,
      slowEmaPeriod: 26,
      signalSmaPeriod: 9,
      macdColor: "green",
      signalColor: "cyan",
      osciColor: "gray",
      initialHeight: "25%",
    },
  }),
  (): ContainerIndicator<
    {
      periods: number[];
      colors: string[];
      initialHeight: number | string;
    },
    {
      kBuffer: IndicatorBuffer;
      dBuffer: IndicatorBuffer;
      sdBuffer: IndicatorBuffer;
    }
  > => ({
    type: "c",
    name: "sample.Stochastics",
    onInit() {
      const {
        initialHeight,
        colors: [kColor, dColor, sdColor],
      } = this.params;
      this.initialHeight = initialHeight;
      const kBuffer = (this.kBuffer = this.addLineBuffer({
        lineColor: kColor,
      }));
      const dBuffer = (this.dBuffer = this.addLineBuffer({
        lineColor: dColor,
      }));
      const sdBuffer = (this.sdBuffer = this.addLineBuffer({
        lineColor: sdColor,
      }));
      const numeratorBuffer = this.addWorkBuffer();
      const denominatorBuffer = this.addWorkBuffer();
      this.shortName = "stochastics";
      this.addPercentLine(80, "darkgray");
      this.addPercentLine(20, "darkgray");
      this.digit = 0;
      this.onChartChange = ({
        index,
        times,
        opens,
        highs,
        lows,
        closes,
        spreads,
      }) => {
        const [kPeriod, dPeriod, sdPeriod] = this.params.periods;
        const lowest = lows.min(index - kPeriod + 1, index),
          highest = highs.max(index - kPeriod + 1, index),
          close = closes.get(index),
          num = close - lowest,
          denom = highest - lowest;
        numeratorBuffer.set(index, num);
        denominatorBuffer.set(index, denom);
        const val = (num / denom) * 100;
        kBuffer.set(index, val);
        const sumNumerator = numeratorBuffer.sum(index - dPeriod + 1, index),
          sumDenominator = denominatorBuffer.sum(index - dPeriod + 1, index);
        dBuffer.set(index, (sumNumerator / sumDenominator) * 100);
        const avg = dBuffer.avg(index - sdPeriod + 1, index);
        sdBuffer.set(index, avg);
      };
      this.getDisplayData = (index) => {
        return {
          k: kBuffer.get(index),
          d: dBuffer.get(index),
          sd: sdBuffer.get(index),
        };
      };
      this.getMin = () => {
        return 0;
      };
      this.getMax = () => {
        return 100;
      };
    },
    params: {
      periods: [14, 3, 3],
      colors: ["aqua", "green", "gold"],
      initialHeight: "25%",
    },
  }),
  (): ContainerIndicator<
    {
      period: number;
      lineColor: string;
      initialHeight: number | string;
    },
    { rsiBuffer: IndicatorBuffer }
  > => ({
    type: "c",
    name: "sample.RSI",
    onInit() {
      const { initialHeight, lineColor } = this.params;
      this.initialHeight = initialHeight;
      const diffBuffer = this.addWorkBuffer();
      const rsiBuffer = (this.rsiBuffer = this.addLineBuffer({ lineColor }));
      this.addPercentLine(70, "darkgray");
      this.addPercentLine(30, "darkgray");
      this.digit = 0;
      this.onChartChange = ({
        index,
        times,
        opens,
        highs,
        lows,
        closes,
        spreads,
      }) => {
        const period = this.params.period;
        const diff = closes.get(index) - closes.get(index - 1);
        diffBuffer.set(index, diff);
        let sumGain = 0,
          sumLoss = 0;
        for (let i = index - period + 1; i <= index; i++) {
          const diff = diffBuffer.get(i);
          if (diff > 0) {
            sumGain += diff;
          } else if (diff <= 0) {
            sumLoss -= diff;
          } else {
            (sumGain = NaN), (sumLoss = NaN);
            break;
          }
        }
        const avgGain = sumGain / period,
          avgLoss = Math.abs(sumLoss) / period;
        const rsi =
          avgLoss === 0
            ? 100
            : avgGain === 0
              ? 0
              : 100 - 100 / (1 + avgGain / avgLoss);
        rsiBuffer.set(index, rsi);
      };
      this.getDisplayData = (index) => {
        return { rate: rsiBuffer.get(index) };
      };
      this.getMin = () => {
        return 0;
      };
      this.getMax = () => {
        return 100;
      };
    },
    params: {
      period: 14,
      lineColor: "purple",
      initialHeight: "25%",
    },
  }),
  (): ItemIndicator<
    {
      shortPeriods: number[];
      longPeriods: number[];
      shortColor: string;
      longColor: string;
    },
    {
      shortEmaBuffers: IndicatorBuffer[];
      longEmaBuffers: IndicatorBuffer[];
    }
  > => ({
    type: "i",
    name: "sample.GMMA",
    onInit() {
      this.shortName = "GMMA";
      const params = this.params;
      const _this = this;
      const shortEmas = params.shortPeriods.map(function (period) {
        return _this.addItemIndicator("sample.EMA", {
          period: period,
          lineColor: params.shortColor,
          lineDash: [],
          lineWidth: 1,
        });
      });
      const longEmas = params.longPeriods.map(function (period) {
        return _this.addItemIndicator("sample.EMA", {
          period: period,
          lineColor: params.longColor,
          lineDash: [],
          lineWidth: 1,
        });
      });
      this.shortEmaBuffers = shortEmas.map((x) => x.emaBuffer);
      this.longEmaBuffers = longEmas.map((x) => x.emaBuffer);
      this.getDisplayData = (index) => {
        return [...shortEmas, ...longEmas].reduce(
          (acc, ema) => {
            acc[ema.params.period] = Object.values(
              ema.getDisplayData(index),
            )[0];
            return acc;
          },
          {} as Record<string, any>,
        );
      };
    },
    params: {
      shortPeriods: [3, 5, 8, 10, 12, 15],
      longPeriods: [30, 35, 40, 45, 50, 60],
      shortColor: "cyan",
      longColor: "pink",
    },
  }),
  (): ItemIndicator<
    {
      fast: { period: number; lineColor: string };
      slow: { period: number; lineColor: string };
    },
    {}
  > => ({
    type: "i",
    name: "sample.sample.SmaCrossSignal",
    onInit() {
      this.zIndex = 1;
      const params = this.params;
      const origParams = IndicatorUtil.getParameter("sample.SMA");
      const fastParams = { ...origParams, ...params.fast, ...{ zIndex: 0 } };
      const slowParams = { ...origParams, ...params.slow, ...{ zIndex: 0 } };
      const fastSMA = this.addItemIndicator("sample.SMA", fastParams);
      const slowSMA = this.addItemIndicator("sample.SMA", slowParams);
      const smaCrossSignalBuffer = this.addTextBuffer({
        arrow: false,
        backgroundColor: "white",
        borderColor: "black",
        borderWidth: 0,
        color: "black",
        fontFamily:
          "Font Awesome 6 Free" /*  Currently the only available web font is Font Awesome 6 Free */,
        fontSize: { min: 20, max: 30 },
        position: "center",
      });
      const entryPointTextBuffer = this.addTextBuffer({
        arrow: true,
        backgroundColor: "white",
        borderColor: "black",
        color: "red",
        fontSize: { min: 20, max: 30 },
        padding: 5,
      });
      this.onChartChange = ({
        index,
        times,
        opens,
        highs,
        lows,
        closes,
        spreads,
        isBarChanged,
      }) => {
        const fastBuffer = fastSMA.smaBuffer,
          slowBuffer = slowSMA.smaBuffer;
        if (isBarChanged) {
          if (
            fastBuffer.get(index - 2) <= slowBuffer.get(index - 2) &&
            fastBuffer.get(index - 1) > slowBuffer.get(index - 1)
          ) {
            smaCrossSignalBuffer.set(index - 1, lows.get(index - 1), {
              position: "bottom",
              text: 0xf0aa /* circle-arrow-up */,
            });
            entryPointTextBuffer.set(index, opens.get(index), {
              position: "left",
              text: "Buy",
            });
          }
          if (
            fastBuffer.get(index - 2) >= slowBuffer.get(index - 2) &&
            fastBuffer.get(index - 1) < slowBuffer.get(index - 1)
          ) {
            smaCrossSignalBuffer.set(index - 1, highs.get(index - 1), {
              position: "top",
              text: 0xf0ab /* circle-arrow-down */,
            });
            entryPointTextBuffer.set(index, opens.get(index), {
              position: "left",
              text: "Sell",
            });
          }
        }
      };
    },
    params: {
      fast: { period: 10, lineColor: "lime" },
      slow: { period: 30, lineColor: "cyan" },
    },
  }),
]