更新日: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" }
]
}
]
}
]