更新日:2023/10/17

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

[
    {
        // 単純移動平均
        type: "i",
        name: "sample.SMA",
        onInit() {
            const { lineColor, lineWidth, lineDash } = this.params;
            this.smaBuffer = this.addLineBuffer({
                lineColor,
                lineWidth,
                lineDash
            });
            this.shortName = "SMA";
        },
        onChartChange({ index, times, opens, highs, lows, closes, spreads }) {
            const period = this.params.period,
                smaBuffer = this.smaBuffer;
            const ohlc = this.params.ohlc[0].toLowerCase();
            const buffer = ohlc === 'o' ? opens :
                ohlc === 'h' ? highs :
                    ohlc === 'l' ? lows :
                        ohlc === 'c' ? closes : null
            buffer.sma(smaBuffer, period, index)
        },
        getDisplayData(index) {
            return [
                { rate: this.smaBuffer.get(index) }
            ]
        },
        params: [
            { period: 25 },
            { lineColor: "red" },
            { lineWidth: 2 },
            { lineDash: [3, 3] },
            { ohlc: 'close' }
        ]
    },
    {
        // 指数移動平均
        type: "i",
        name: "sample.EMA",
        onInit() {
            const { lineColor, lineWidth, lineDash } = this.params;
            this.emaBuffer = this.addLineBuffer({
                lineColor,
                lineWidth,
                lineDash
            });
            this.shortName = "EMA";
        },
        onChartChange({ index, times, opens, highs, lows, closes, spreads }) {
            const period = this.params.period,
                emaBuffer = this.emaBuffer
            const ohlc = this.params.ohlc[0].toLowerCase();
            const buffer = ohlc === 'o' ? opens :
                ohlc === 'h' ? highs :
                    ohlc === 'l' ? lows :
                        ohlc === 'c' ? closes : null
            buffer.ema(emaBuffer, period, index);
        },
        getDisplayData(index) {
            const emaBuffer = this.emaBuffer;
            return [
                { rate: emaBuffer.get(index) }
            ]
        },
        params: [
            { period: 25 },
            { lineColor: "green" },
            { lineWidth: 2 },
            { lineDash: [3, 3] },
            { ohlc: 'close' }
        ]
    },
    {
        // 平均足
        type: "c",
        name: "sample.HeikinAshi",
        onInit({ addCandleStickBuffer }) {
            const { initialHeight, upColor, downColor } = this.params;
            this.initialHeight = initialHeight;
            const [openBuffer, highBuffer, lowBuffer, closeBuffer] = addCandleStickBuffer([
                upColor,
                sameColor,
                downColor
            ]);
            this.openBuffer = openBuffer;
            this.highBuffer = highBuffer;
            this.lowBuffer = lowBuffer;
            this.closeBuffer = closeBuffer;
            this.shortName = "heikin"
            this.isShowDefaultScaleLine = true;
        },
        onChartChange({ index, times, opens, highs, lows, closes, spreads }) {
            const preOpen = opens.get(index - 1),
                preClose = closes.get(index - 1);
            this.openBuffer.set(index, (preOpen + preClose) / 2);
            this.highBuffer.set(index, highs.get(index));
            this.lowBuffer.set(index, lows.get(index));
            this.closeBuffer.set(index, (opens.get(index) + highs.get(index) + lows.get(index) + closes.get(index)) / 4);
        },
        getDisplayData(index) {
            return [
                { rate: this.openBuffer.get(index) },
                { rate: this.closeBuffer.get(index) }
            ]
        },
        params: [
            { upColor: "blue" },
            { sameColor: "black" },
            { downColor: "red" },
            { initialHeight: "25%" }
        ]
    },
    {
        // MACD
        type: "c",
        name: "sample.MACD",
        onInit() {
            const { initialHeight, osciColor, macdColor, signalColor } = this.params;
            this.initialHeight = initialHeight;
            this.osciBuffer = this.addHistgramBuffer({ backgroundColor: osciColor, baseValue:0 });
            this.fastEmaBuffer = this.addNoopBuffer();
            this.slowEmaBuffer = this.addNoopBuffer();
            this.macdBuffer = this.addLineBuffer({ lineColor: macdColor, lineWidth: 2 });
            this.textBuffer = this.addLineBuffer({ lineColor: signalColor, lineWidth: 2 });
            this.shortName = "MACD";
            this.digit = 0;
            this.showScaleLine(0, "darkgray")
        },
        getDisplayData(index) {
            return [
                { rate: this.macdBuffer.get(index) },
                { rate: this.textBuffer.get(index) },
                { rate: this.osciBuffer.get(index) }
            ]
        },
        onChartChange({ index, times, opens, highs, lows, closes, spreads }) {
            const fastEmaBuffer = this.fastEmaBuffer,
                slowEmaBuffer = this.slowEmaBuffer,
                macdBuffer = this.macdBuffer,
                textBuffer = this.textBuffer,
                osciBuffer = this.osciBuffer,
                { 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(textBuffer, signalSmaPeriod, index, slowEmaPeriod)
            osciBuffer.set(index, macdVal - signalVal);
        },
        params: [
            { fastEmaPeriod: 12 },
            { slowEmaPeriod: 26 },
            { signalSmaPeriod: 9 },
            { macdColor: "green" },
            { signalColor: "cyan" },
            { osciColor: "gray" },
            { initialHeight: "25%" }
        ]
    },
    {
        // ストキャスティクス
        type: "c",
        name: "sample.Stochastics",
        initialHeight: "25%",
        onInit() {
            const { initialHeight, colors: [kColor, dColor, sdColor] } = this.params;
            this.initialHeight = initialHeight;
            this.kBuffer = this.addLineBuffer({ lineColor: kColor });
            this.dBuffer = this.addLineBuffer({ lineColor: dColor });
            this.sdBuffer = this.addLineBuffer({ lineColor: sdColor });
            this.numeratorBuffer = addNoopBuffer();
            this.denominatorBuffer = addNoopBuffer();
            this.shortName = "stochastics"
            this.showScaleLine(80, "darkgray")
                .showScaleLine(20, "darkgray")
            this.digit = 0;
        },
        getMin() { return 0 },
        getMax() { return 100 },
        onChartChange({ index, times, opens, highs, lows, closes, spreads }) {
            const kBuffer = this.kBuffer,
                dBuffer = this.dBuffer,
                sdBuffer = this.sdBuffer,
                numeratorBuffer = this.numeratorBuffer,
                denominatorBuffer = this.denominatorBuffer,
                [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);
        },
        getDisplayData(index) {
            return [
                { rate: this.kBuffer.get(index) },
                { rate: this.dBuffer.get(index) },
                { rate: this.sdBuffer.get(index) },
            ]
        },
        params: {
            periods: [14, 3, 3],
            colors: ["aqua", "green", "gold"],
            initialHeight: "25%"
        }
    },
    {
        // RSI
        type: "c",
        name: "sample.RSI",
        onInit() {
            const { initialHeight, lineColor } = this.params
            this.initialHeight = initialHeight;
            this.diffBuffer = this.addNoopBuffer();
            this.lineBuffer = this.addLineBuffer({ lineColor });
            this.showScaleLine(70, "darkgray")
                .showScaleLine(30, "darkgray")
            this.digit = 0
        },
        getMin() { return 0 },
        getMax() { return 100 },
        onChartChange({ index, times, opens, highs, lows, closes, spreads }) {
            const diffBuffer = this.diffBuffer,
                lineBuffer = this.lineBuffer;
            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))))
            lineBuffer.set(index, rsi)
        },
        getDisplayData(index) {
            return [
                { rate: this.lineBuffer.get(index) },
            ]
        },
        params: {
            period: 14,
            lineColor: "purple",
            initialHeight: "25%"
        }
    },
    {
        // GMMA
        type: "i",
        name: "sample.GMMA",
        onInit() {
            this.shortName = 'GMMA';
            const params = this.params;
            this.shortEmas = params.shortPeriods.map(function (period) {
                return this.addItemIndicator('sample.EMA', { period: period, lineColor: params.shortColor, lineDash: [], lineWidth: 1 })
            }, this);
            this.longEmas = params.longPeriods.map(function (period) {
                return this.addItemIndicator('sample.EMA', { period: period, lineColor: params.longColor, lineDash: [], lineWidth: 1 })
            }, this);
        },
        params: [
            { shortPeriods: [3, 5, 8, 10, 12, 15] },
            { longPeriods: [30, 35, 40, 45, 50, 60] },
            { shortColor: 'cyan' },
            { longColor: 'pink' }
        ],
        getDisplayData(index) {
            return this.shortEmas.flatMap(ema => ema.getDisplayData(index))
                .concat(this.longEmas.flatMap(ema => ema.getDisplayData(index)))
        }
    },
    {
        // 移動平均シグナル
        type: "i",
        name: "sample.sample.SmaCrossSignal",
        onInit() {
            const params = this.params;
            const origParams = Indicator.getParameter("sample.SMA")
            const fastParams = { ...origParams, ...params.fast }
            const slowParams = { ...origParams, ...params.slow }
            this.fastSMA = this.addItemIndicator("sample.SMA", fastParams)
            this.slowSMA = this.addItemIndicator("sample.SMA", slowParams)
            this.smaCrossSignalBuffer = this.addTextBuffer({
                arrow: false,
                backgroundColor: 'white',
                borderColor: 'black',
                borderWidth: 0,
                color: 'black',
                fontFamily: 'Font Awesome 6 Free', /* 現状WebフォントはFont Awesome 6 Freeのみ利用可能です, */
                fontSize: { min: 20, max: 30 },
                position: 'center'
            })
            this.entryPointTextBuffer = this.addTextBuffer({
                arrow: true,
                backgroundColor: 'white',
                borderColor: 'black',
                color: 'red',
                fontSize: { min: 20, max: 30 },
                padding: 5
            })
        },
        onChartChange({ index, times, opens, highs, lows, closes, spreads, isBarChanged }) {
            const fastBuffer = this.fastSMA.smaBuffer,
                slowBuffer = this.slowSMA.smaBuffer,
                smaCrossSignalBuffer = this.smaCrossSignalBuffer,
                entryPointTextBuffer = this.entryPointTextBuffer
            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" }
                ]
            }
        ]
    }
]