🐕

ChatGPTでTradingViewのインジケータのスクリプトを書いてみた

2023/03/26に公開

はじめに

TradingViewは無料で使えるインジケータが沢山ありますが、無料会員の場合、インジケータを3つまでしか設定できません。
TradingViewが提供しているスクリプトを記述することでインジケータを自作できます。
インジケータを自作して、複数のインジケータを1つにまとめてしまえば、3つの枠に沢山のインジケータを表示できるんです。
以前からインジケータを自作したいという思いはありつつも、スクリプト学習の手間を惜しんでなかなか取り組めないでいました。
最近流行りChatGPTを活用してコーディングが出来ることを知ったので、
ChatGPTを活用してよく知らない言語を書いてみようという試みです。

ChatGPTのバージョンは無料版なので、3.5です。

TradingViewとは

FXやっている方にはおなじみかと思いますが、TradingViewというWebサイトがあります。
株式、FX、仮想通貨などの取引を行うためのチャートや分析ツールを提供するWebベースのプラットフォームです。
TradingViewは、使いやすいインターフェースと多様な機能を備えており、個人投資家からプロのトレーダーまで幅広い層に人気があります。

TradingViewの主な機能には、以下のものがあります。

  1. チャート作成:株価、為替レート、仮想通貨などの価格をグラフ化し、時系列で表示することができます。また、複数の指標やテクニカル分析ツールを適用することができます。

  2. アラート:価格やインジケーターが特定の条件に達した場合に、アラートを受け取ることができます。例えば、株価が一定の水準に達した時に通知を受け取ることができます。

  3. スクリーニング:指定した条件に基づいて、銘柄をスクリーニングすることができます。例えば、P/E比が一定の水準以下である株式を検索することができます。

  4. トレーディングアイデアの共有:ユーザーは自分のチャートや分析を共有し、他のユーザーと意見交換することができます。

  5. 経済指標のカレンダー:経済指標の発表スケジュールを表示し、それに基づいて取引の戦略を立てることができます。

https://jp.tradingview.com/

TradingViewのスクリプト言語

TradingViewのスクリプト言語は「PineScript」と言います。

基本的な文法はJavaScriptに似ています。

PineScriptの主な特徴は以下の通りです。

  1. テクニカル指標:PineScriptを使用して、テクニカル指標を作成することができます。例えば、移動平均線、RSI、MACDなどの指標を作成することができます。また、PineScriptを使用して、異なるテクニカル指標を組み合わせて新しい指標を作成することもできます。

  2. ストラテジー:PineScriptを使用して、トレードストラテジーを作成することができます。例えば、テクニカル指標のクロスオーバー、価格パターン、過去のデータからの予測などを利用して、エントリー・イグジットルールを定義することができます。

  3. カスタムインジケーター:PineScriptを使用して、独自のカスタムインジケーターを作成することができます。例えば、あるテクニカル指標の特定のパラメータを変更することができます。

  4. バックテスト:PineScriptを使用して、過去のデータに基づいてストラテジーのバックテストを実行することができます。バックテストは、ストラテジーの有効性を評価するために使用されます。

https://jp.tradingview.com/pine-script-docs/en/v5/Introduction.html

https://jp.tradingview.com/pine-script-reference/v5/

ChatGPTでのスクリプト書き方

とりあえず雑に聞いてみます。
基本の移動平均線でお願いします。

//@version=5
indicator("5 Moving Averages", overlay=true)

// 移動平均線の長さを設定する
ma_length_1 = input.int(10, "MA Length 1", minval=1)
ma_length_2 = input.int(20, "MA Length 2", minval=1)
ma_length_3 = input.int(30, "MA Length 3", minval=1)
ma_length_4 = input.int(40, "MA Length 4", minval=1)
ma_length_5 = input.int(50, "MA Length 5", minval=1)

// 移動平均線を計算する
ma_1 = ta.sma(close, ma_length_1)
ma_2 = ta.sma(close, ma_length_2)
ma_3 = ta.sma(close, ma_length_3)
ma_4 = ta.sma(close, ma_length_4)
ma_5 = ta.sma(close, ma_length_5)

// 移動平均線をプロットする
plot(ma_1, color=color.yellow, linewidth=1)
plot(ma_2, color=color.blue, linewidth=1)
plot(ma_3, color=color.green, linewidth=1)
plot(ma_4, color=color.red, linewidth=1)
plot(ma_5, color=color.orange, linewidth=1)

// 凡例を表示する
legend_label_1 = "MA " + str.tostring(ma_length_1)
legend_label_2 = "MA " + str.tostring(ma_length_2)
legend_label_3 = "MA " + str.tostring(ma_length_3)
legend_label_4 = "MA " + str.tostring(ma_length_4)
legend_label_5 = "MA " + str.tostring(ma_length_5)
plotchar(false, "", "", location.bottom, size = size.tiny)
plotchar(false, "", "", location.bottom, size = size.tiny, text = legend_label_1, color = color.yellow)
plotchar(false, "", "", location.bottom, size = size.tiny, text = legend_label_2, color = color.blue)
plotchar(false, "", "", location.bottom, size = size.tiny, text = legend_label_3, color = color.green)
plotchar(false, "", "", location.bottom, size = size.tiny, text = legend_label_4, color = color.red)
plotchar(false, "", "", location.bottom, size = size.tiny, text = legend_label_5, color = color.orange)

いい感じですね。

出てきたスクリプトの結果を表示するには、TradingViewの「Pineエディタ」に貼り付けます。

  1. 画面下部の「Pineエディタ」をクリックするとエディタが表示されます。
  2. 右にある「開く」でメニューを表示します。
  3. 新規作成の「インジケータ」をクリック
  4. ここにコードを張り付けます。元々書いてあるコードは消してしまってよいです。
  5. 保存ボタンをクリック
  6. 名前を付けるように促されるので、名前を決めて、保存ボタンクリック

これで表示される、と思いきやエラーです。

画像の下にある赤字がエラーです。
ドラッグして上下に枠を広げられるので広げて確認します。

Cannot call 'plotchar' with argument 'text'='legend_label_1'. An argument of 'simple string' type was used but a 'const string' is expected.

plotcharに指定した、text=legend_label_1がよくないようです。

話が違うので、ChatGPTに聞いてみます。

//@version=5
indicator("5 Moving Averages", overlay=true)

// 移動平均線の長さを設定する
ma_length_1 = input.int(10, "MA Length 1", minval=1)
ma_length_2 = input.int(20, "MA Length 2", minval=1)
ma_length_3 = input.int(30, "MA Length 3", minval=1)
ma_length_4 = input.int(40, "MA Length 4", minval=1)
ma_length_5 = input.int(50, "MA Length 5", minval=1)

// 移動平均線を計算する
ma_1 = ta.sma(close, ma_length_1)
ma_2 = ta.sma(close, ma_length_2)
ma_3 = ta.sma(close, ma_length_3)
ma_4 = ta.sma(close, ma_length_4)
ma_5 = ta.sma(close, ma_length_5)

// 移動平均線をプロットする
plot(ma_1, color=color.yellow, linewidth=1)
plot(ma_2, color=color.blue, linewidth=1)
plot(ma_3, color=color.green, linewidth=1)
plot(ma_4, color=color.red, linewidth=1)
plot(ma_5, color=color.orange, linewidth=1)

// 凡例を表示する
var label1 = "MA " + str.tostring(ma_length_1)
var label2 = "MA " + str.tostring(ma_length_2)
var label3 = "MA " + str.tostring(ma_length_3)
var label4 = "MA " + str.tostring(ma_length_4)
var label5 = "MA " + str.tostring(ma_length_5)
plotchar(false, "", "", location.bottom, size = size.tiny)
plotchar(false, "", "", location.bottom, size = size.tiny, text = label1, color = color.yellow)
plotchar(false, "", "", location.bottom, size = size.tiny, text = label2, color = color.blue)
plotchar(false, "", "", location.bottom, size = size.tiny, text = label3, color = color.green)
plotchar(false, "", "", location.bottom, size = size.tiny, text = label4, color = color.red)
plotchar(false, "", "", location.bottom, size = size.tiny, text = label5, color = color.orange)

ちなみにこれも動きません。
マニュアル調べてtextはconstでないといけないという事が分かりましたので、教えてみます。

plotcharの説明
https://jp.tradingview.com/pine-script-reference/v5/#fun_plotchar

const=定数じゃないといけないので、変数は入れられないという事のようです。
varじゃダメってことですね。

//@version=5
indicator("5 Moving Averages", overlay=true)

// 移動平均線の長さを設定する
ma_length_1 = input.int(10, "MA Length 1", minval=1)
ma_length_2 = input.int(20, "MA Length 2", minval=1)
ma_length_3 = input.int(30, "MA Length 3", minval=1)
ma_length_4 = input.int(40, "MA Length 4", minval=1)
ma_length_5 = input.int(50, "MA Length 5", minval=1)

// 移動平均線を計算する
ma_1 = ta.sma(close, ma_length_1)
ma_2 = ta.sma(close, ma_length_2)
ma_3 = ta.sma(close, ma_length_3)
ma_4 = ta.sma(close, ma_length_4)
ma_5 = ta.sma(close, ma_length_5)

// 移動平均線をプロットする
plot(ma_1, color=color.yellow, linewidth=1)
plot(ma_2, color=color.blue, linewidth=1)
plot(ma_3, color=color.green, linewidth=1)
plot(ma_4, color=color.red, linewidth=1)
plot(ma_5, color=color.orange, linewidth=1)

// 凡例を表示する
plotchar(false, "", "", location.bottom, size = size.tiny)
plotchar(false, "", "MA " + str.tostring(ma_length_1), location.bottom, size = size.tiny, color = color.yellow)
plotchar(false, "", "MA " + str.tostring(ma_length_2), location.bottom, size = size.tiny, color = color.blue)
plotchar(false, "", "MA " + str.tostring(ma_length_3), location.bottom, size = size.tiny, color = color.green)
plotchar(false, "", "MA " + str.tostring(ma_length_4), location.bottom, size = size.tiny, color = color.red)
plotchar(false, "", "MA " + str.tostring(ma_length_5), location.bottom, size = size.tiny, color = color.orange)

textの引数が消えた!
そして今度は第2引数が同様のエラーにw

Cannot call 'plotchar' with argument 'char'='call 'operator +' (simple string)'. An argument of 'simple string' type was used but a 'input string' is expected.

その辺修正して保存してみて、エラーが出ていなければ成功です。

修正版がこちら

//@version=5
indicator("5 Moving Averages", overlay=true)

// 移動平均線の長さを設定する
ma_length_1 = input.int(10, "MA Length 1", minval=1)
ma_length_2 = input.int(20, "MA Length 2", minval=1)
ma_length_3 = input.int(30, "MA Length 3", minval=1)
ma_length_4 = input.int(40, "MA Length 4", minval=1)
ma_length_5 = input.int(50, "MA Length 5", minval=1)

// 移動平均線を計算する
ma_1 = ta.sma(close, ma_length_1)
ma_2 = ta.sma(close, ma_length_2)
ma_3 = ta.sma(close, ma_length_3)
ma_4 = ta.sma(close, ma_length_4)
ma_5 = ta.sma(close, ma_length_5)

// 移動平均線をプロットする
plot(ma_1, color=color.blue, linewidth=2)
plot(ma_2, color=color.orange, linewidth=2)
plot(ma_3, color=color.yellow, linewidth=2)
plot(ma_4, color=color.red, linewidth=2)
plot(ma_5, color=color.orange, linewidth=2)

// 凡例を表示する
plotchar(false, "", "❄", location.bottom, size = size.tiny, color = color.yellow)
plotchar(false, "", "❄", location.bottom, size = size.tiny, text = "MA2", color = color.blue)
plotchar(false, "", "❄", location.bottom, size = size.tiny, text = "MA3", color = color.green)
plotchar(false, "", "❄", location.bottom, size = size.tiny, text = "MA4", color = color.red)
plotchar(false, "", "❄", location.bottom, size = size.tiny, text = "MA5", color = color.orange)

チャート上にも線が表示されています。

インジケータの表示と編集

作成したスクリプトは他のインジケータ同様、数値を変更して期間の変更が可能です。

最初から変更しておきたい場合は、スクリプトの下記の部分の数値を変更して保存しておくことも出来ます。

// 移動平均線の長さを設定する
ma_length_1 = input.int(10, "MA Length 1", minval=1)
ma_length_2 = input.int(20, "MA Length 2", minval=1)
ma_length_3 = input.int(30, "MA Length 3", minval=1)
ma_length_4 = input.int(40, "MA Length 4", minval=1)
ma_length_5 = input.int(50, "MA Length 5", minval=1)

さらに改良してみる

色の指定をしたいので、聞いてみます。

実装例

// 色定義
var deep_pink = color.rgb(255, 20, 147)
var bisque = color.rgb(255, 228, 196)

plot(ma_1, color=bisque, linewidth=2)
plot(ma_2, color=deep_pink, linewidth=2)

ボリンジャーバンドの追加

これだけだと、元からあるインジケータ使うのとあまり違いがありません。
他のインジケータも増やしてインジケータ枠を確保したいです。

と言うことで次はボリンジャーバンドをお願いします。

//@version=5
indicator("Bollinger Bands", overlay=true)

// ボリンジャーバンドのパラメータを設定する
length = input.int(20, "Length", minval=1)
mult = input.float(2.0, "Multiplier", minval=0.001, maxval=50)

// ボリンジャーバンドを計算する
basis = ta.sma(close, length)
dev = mult * ta.stdev(close, length)
upper = basis + dev
lower = basis - dev

// ボリンジャーバンドをプロットする
plot(basis, color=color.yellow, linewidth=1)
plot(upper, color=color.blue, linewidth=1)
plot(lower, color=color.green, linewidth=1)

// ボリンジャーバンドの中心線と上下バンドの名前を設定する
plotchar(false, "", "", location.bottom, size = size.tiny)
plotchar(false, "", "Basis", location.bottom, size = size.tiny, color = color.yellow)
plotchar(false, "", "Upper", location.bottom, size = size.tiny, color = color.blue)
plotchar(false, "", "Lower", location.bottom, size = size.tiny, color = color.green)

こちらはエラーなくそのまま表示できそうです。
1つのインジケータに表示したいので、
先ほどの移動平均線のスクリプトの下に追加していきます。
最初のindicatorの部分は不要なので、その下からコピーして貼り付けます。

//@version=5
indicator("Bollinger Bands", overlay=true)
//@version=5
indicator("5 Moving Averages", overlay=true)

// 移動平均線の長さを設定する
ma_length_1 = input.int(10, "MA Length 1", minval=1)
ma_length_2 = input.int(20, "MA Length 2", minval=1)

// ~ 中略 ~

color.green)
plotchar(false, "", "❄", location.bottom, size = size.tiny, text = "MA4", color = color.red)
plotchar(false, "", "❄", location.bottom, size = size.tiny, text = "MA5", color = color.orange)


// ボリンジャーバンドのパラメータを設定する
length = input.int(20, "Length", minval=1)
mult = input.float(2.0, "Multiplier", minval=0.001, maxval=50)

// ~ 中略 ~

plotchar(false, "", "Upper", location.bottom, size = size.tiny, color = color.blue)
plotchar(false, "", "Lower", location.bottom, size = size.tiny, color = color.green)


という事でインジケータ1つで移動平均線とボリンジャーバンドを表示する事が出来ました。

MACDの追加

オシレータ系もやってみます。

//@version=5
indicator("MACD", overlay=true)

// MACDのパラメータを設定する
fast_length = input.int(12, "Fast Length", minval=1)
slow_length = input.int(26, "Slow Length", minval=1)
signal_length = input.int(9, "Signal Length", minval=1)

// MACDを計算する
fast_ma = ta.ema(close, fast_length)
slow_ma = ta.ema(close, slow_length)
macd = fast_ma - slow_ma
signal = ta.ema(macd, signal_length)

// MACDをプロットする
plot(macd, color=color.blue, linewidth=1)
plot(signal, color=color.red, linewidth=1)

// MACDの中心線とシグナルの名前を設定する
plotchar(false, "", "", location.bottom, size = size.tiny)
plotchar(false, "", "MACD", location.bottom, size = size.tiny, color = color.blue)
plotchar(false, "", "Signal", location.bottom, size = size.tiny, color = color.red)

// MACDとシグナルの交差点をプロットする
plotshape(crossover(macd, signal), style=shape.triangleup, location=location.bottom, color=color.green, size=size.small)
plotshape(crossunder(macd, signal), style=shape.triangledown, location=location.top, color=color.red, size=size.small)

例によってエラー出るので聞いてみます。
だんだんエラーが出ても気にならなくなってきます。

Could not find function or function reference 'crossover'というエラーが出ます

ChatGPT

crossover関数は、Pineスクリプトで使用するために事前に定義する必要があります。
crossover関数は、2つのシリーズがクロスオーバーした場合にtrueを返し、クロスアンダーした場合にfalseを返します。
同様に、crossunder関数は、2つのシリーズがクロスアンダーした場合にtrueを返し、クロスオーバーした場合にfalseを返します。

以下は、crossover関数とcrossunder関数を定義する方法の例です。
//@version=5
indicator("Example")

// crossover関数を定義する
crossover(x, y) =>
    x[1] < y[1] and x > y

// crossunder関数を定義する
crossunder(x, y) =>
    x[1] > y[1] and x < y

// サンプルの使用方法
a = sma(close, 10)
b = sma(close, 20)

// aとbのクロスオーバーとクロスアンダーを検出する
crossover_ab = crossover(a, b)
crossunder_ab = crossunder(a, b)

// 結果をプロットする
plot(a)
plot(b)

plotshape(crossover_ab, style=shape.triangleup, location=location.bottom, color=color.green, size=size.small)
plotshape(crossunder_ab, style=shape.triangledown, location=location.top, color=color.red, size=size.small)
このスクリプトでは、最初にcrossover関数とcrossunder関数を定義しています。
次に、sma関数を使用して、2つの移動平均線aとbを計算しています。
その後、crossover関数とcrossunder関数を使用して、aとbのクロスオーバーとクロスアンダーを検出し、
plotshape関数を使用して、それぞれ三角形でプロットしています。

crossover関数とcrossunder関数を定義した後、MACDのスクリプトに戻り、
crossover関数とcrossunder関数を使用してMACDとシグナルのクロスオーバーとクロスアンダーを検出できます。

なるほど!crossoverは自作関数だったんですね。斬新
追記:crossoverta.crossoverとすれば自作関数なしで同様の表記が可能です。

出力されたコードを、よしなにマージしたのが以下

//@version=5
indicator("MACD", timeframe="", timeframe_gaps=true)

//--------------------
// MACD
//--------------------

// MACDのパラメータを設定する
fast_length = input.int(12, "Fast Length", group="MACD", minval=1)
slow_length = input.int(26, "Slow Length", group="MACD", minval=1)
signal_length = input.int(9, "Signal Length", group="MACD", minval=1)

// Plot colors
col_macd = input(#2962FF, "MACD Line  ", group="MACD Color Settings", inline="MACD")
col_signal = input(#FF6D00, "Signal Line  ", group="MACD Color Settings", inline="Signal")
col_grow_above = input(#26A69A, "Above   Grow", group="MACD Histogram", inline="Above")
col_fall_above = input(#B2DFDB, "Fall", group="MACD Histogram", inline="Above")
col_grow_below = input(#FFCDD2, "Below Grow", group="MACD Histogram", inline="Below")
col_fall_below = input(#FF5252, "Fall", group="MACD Histogram", inline="Below")

// MACDを計算する
fast_ma = ta.ema(close, fast_length)
slow_ma = ta.ema(close, slow_length)
// RCIと同じ場所に表示するため、MACDの値を100倍している
macd = (fast_ma - slow_ma) * 100
signal = ta.ema(macd, signal_length)
hist = macd - signal

// MACDをプロットする
hline(0, "Zero Line", color=color.new(#787B86, 50))
plot(hist, title="Histogram", style=plot.style_columns, color=(hist>=0 ? (hist[1] < hist ? col_grow_above : col_fall_above) : (hist[1] < hist ? col_grow_below : col_fall_below)))
plot(macd, title="macd", color=color.blue, linewidth=1)
plot(signal, title="signal", color=color.red, linewidth=1)

// MACDの中心線とシグナルの名前を設定する
plotchar(false, "", "", location.bottom, size = size.tiny)
plotchar(false, "", "MACD", location.bottom, size = size.tiny, color = color.blue)
plotchar(false, "", "Signal", location.bottom, size = size.tiny, color = color.red)

// crossover関数を定義する
crossover(x, y) =>
    x[1] < y[1] and x > y

// crossunder関数を定義する
crossunder(x, y) =>
    x[1] > y[1] and x < y

// MACDとシグナルの交差点をプロットする
plotshape(crossover(macd, signal), title="ゴールデンクロス", style=shape.triangleup, location=location.bottom, color=color.green, size=size.small)
plotshape(crossunder(macd, signal), title="デッドクロス", style=shape.triangledown, location=location.top, color=color.red, size=size.small)

indicator("MACD", overlay=true)overlay=trueは、設定してしまうと他のインジケータと混ざってよくわからない表示になるので、外しました。
なので、移動平均線とボリンジャーバンドとは別で保存しました。
オシレータ系はこちらにまとめて行けば節約できそうです。

こんな感じです。
通常のMACDに加えてゴールデンクロスやデッドクロスしている箇所に▲のマークがついていますね。

まとめ

  • ChatGPTを活用してPineScriptのインジケータを作成できました。
  • 実際の作成手順から詰まるポイントや解決方法について解説しました。
  • ChatGPTのコードは動かないこともある。エラーメッセージだけで判断できない部分はあり、過去の経験やマニュアルの理解はある程度必要

参考文献

https://www.youtube.com/watch?v=ZaPeRJ3rIBY
動画内では、インジケータだけでなく、ストラテジやバックテストについても触れているので、この辺も取り入れてみたいです。

NG集

//@version=5
indicator("月次Pivot", overlay=true)

// Pivotを計算するための入力値
h = high
l = low
c = close

// 現在の日付の年月を取得
current_month = year(time)*100 + month(time)

// 直近5か月分のPivotを計算する
for i = 0 to 4
    // iヶ月前の年月を計算
    month_offset = -i
    year_offset = month_offset < 0 ? 1 : 0
    target_month = year(time[month_offset])*100 + month(time[month_offset])
    
    // iヶ月前の高値、安値、終値を取得
    ph = security(syminfo.ticker, "M", high[month_offset])
    pl = security(syminfo.ticker, "M", low[month_offset])
    pc = security(syminfo.ticker, "M", close[month_offset])
    
    // Pivotを計算
    pivot = (ph + pl + pc) / 3
    r1 = 2*pivot - pl
    r2 = pivot + (ph - pl)
    r3 = ph + 2*(pivot - pl)
    s1 = 2*pivot - ph
    s2 = pivot - (ph - pl)
    s3 = pl - 2*(ph - pivot)
    
    // Pivotが表示すべき月の場合にだけ、プロットする
    if target_month == current_month
        plot(pivot, color=color.blue, linewidth=2, style=plot.style_circles, title="Pivot")
        plot(r1, color=color.green, linewidth=2, style=plot.style_circles, title="R1")
        plot(r2, color=color.green, linewidth=2, style=plot.style_circles, title="R2")
        plot(r3, color=color.green, linewidth=2, style=plot.style_circles, title="R3")
        plot(s1, color=color.red, linewidth=2, style=plot.style_circles, title="S1")
        plot(s2, color=color.red, linewidth=2, style=plot.style_circles, title="S2")
        plot(s3, color=color.red, linewidth=2, style=plot.style_circles, title="S3")

結局解決せず。。。

マニュアル

v5からrequest.securityになったようです。
https://jp.tradingview.com/pine-script-reference/v5/#fun_request{dot}security

マニュアルがv3からしかなかったので、security関数がいつから使えたかは不明ですが、v3、v4にはsecurity関数が存在します。

該当箇所をrequest.securityにしただけではエラーのままなので、もう少し調べないと分からないですねぇ。

Discussion