Pine Script でチャートに線を引く

こんにちは。技術部の小池です。

この記事は 面白法人グループ Advent Calendar 2024 の9日目の記事です。

世は貯蓄から投資への時代、投資を始めての株などのチャートを見る機会が増えた方もいるのではないでしょうか。 今回はチャート上で実行できる趣味のプログラミングの話をします。

TradingView 上で開発、実行できる Pine Script というプログラミング言語を使います。TradingView は、株、指数、FX、仮想通貨などの金融市場のチャート分析ができる人気のプラットフォームです。 Pine Script は11月に v6 がリリースされていますが、この記事では v5 で記述していきます。v6 リリースめでたいですね。

取引時間

CFD や FX では多くの銘柄が平日の日本時間早朝から翌日の日本時間早朝まで、あるいは平日24時間取引ができます。その中でも取引が活発になる時間帯*1があります。

  • 東京市場: JST で 08~17時
  • ロンドン市場: JST で 17~01時
  • ニューヨーク市場: JST で 22~04時

東京市場では日本を含むアジアやオセアニアの参加者が、ロンドン市場ではヨーロッパの参加者が、ニューヨーク市場ではアメリカの参加者が活発に取引する時間です。 このとき、それぞれの市場の切り替わりの時間帯は値動きのトレンドが変わりやすいと言われています。

実際のチャートを見てみましょう。以下は適当に選んだ今年の 7/18 ~ 7/30 のドル円の1時間足のチャートです。

ドル円の1時間足チャート

紫の四角が東京市場の時間、青の四角がロンドン市場の時間、オレンジの四角がニューヨーク市場の時間を表しています。

ここでそれぞれの市場の動きを見てみると、東京市場の動きにロンドン市場が追従した場合はニューヨーク市場も追従することが多いです。該当する値動きの箇所を黄色い四角で囲いました。

黄色い四角

ということは東京市場に対してロンドン市場がどう動くのかを見ることで、ニューヨーク市場の動きを予測することができるかもしれません。今日の東京市場の高値と安値に線を引いてみることにしましょう。

Pine Script で東京市場の高値と安値に対して線を引く

完成版のコードを先に貼ります。TradingView 上の Pineエディタ を開いてコピペすれば動きます。

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

isToday(tz = syminfo.timezone) =>
    barYear = year(time, tz)
    barMonth = month(time, tz)
    barDay = dayofmonth(time, tz)
    currentYear = year(timenow, tz)
    currentMonth = month(timenow, tz)
    currentDay = dayofmonth(timenow, tz)
    barYear == currentYear and barMonth == currentMonth and barDay == currentDay

inSession(session, tz = syminfo.timezone) =>
    not na(time(timeframe.period, session, tz))

var string tz = "GMT+9"
var string tokyoSession = "0800-1700"

var float highPrice = 0.0
var float lowPrice = 0.0

var int highPriceTime = 0
var int lowPriceTime = 0

isToday = isToday(tz)
inSession = isToday and inSession(tokyoSession, tz) and timeframe.isintraday
isSessionStart = inSession and not inSession[1]
isSessionEnd = inSession[1] and not inSession

if isSessionStart
    highPrice := high
    lowPrice  := low
    highPriceTime := time
    lowPriceTime := time
else if inSession
    if high > highPrice
        highPrice := high
        highPriceTime := time
    if low < lowPrice
        lowPrice := low
        lowPriceTime := time

isNewDay = isToday and not isToday(tz)[1] 

var line highPriceLine = na
var line lowPriceLine = na

if isNewDay
    line.delete(highPriceLine)
    line.delete(lowPriceLine)
else if isSessionEnd
    highPriceLine := line.new(highPriceTime, highPrice, timenow, highPrice, xloc.bar_time, color = color.white)
    lowPriceLine := line.new(lowPriceTime, lowPrice, timenow, lowPrice, xloc.bar_time, color = color.white)
else if not na(highPriceLine) and not na(lowPriceLine)
    line.set_x2(highPriceLine, timenow)
    line.set_x2(lowPriceLine, timenow)

Pine Script はその性質上使用者が少ない言語なためか はてなブログ や GitHub でもシンタックスハイライトはありません!生成 AI によるコーディング支援も受けにくく、生成 AI 以前の時代のようにすべて自力でコードを書く体験ができます。

Pine Script のコード

コードを上から簡単に解説していきます。

isToday(tz = syminfo.timezone) =>
    barYear = year(time, tz)
    barMonth = month(time, tz)
    barDay = dayofmonth(time, tz)
    currentYear = year(timenow, tz)
    currentMonth = month(timenow, tz)
    currentDay = dayofmonth(timenow, tz)
    barYear == currentYear and barMonth == currentMonth and barDay == currentDay

inSession(session, tz = syminfo.timezone) =>
    not na(time(timeframe.period, session, tz))

isToday() と inSession() はユーザ定義の関数です。 isToday 関数は第1引数でタイムゾーンを表す tz という変数を受け取っていて、現在のバーが今日のものかどうかを返しています。

Pine Script の実行モデルの面白いところは過去の履歴のバーごとに上記のコードが実行されることです。チャートは以下のように連続したバーにって構成されていますが、矢印のようにバーの数だけ実行されます。

バーごとに実行される

そのため、現在のバーが今日のバーかどうかを判定する必要があるわけですね。 取引時間内の場合は一番右のバーはリアルタイムに値が更新されます。このときは値が更新されるたびにコードが実行されます。頻繁に実行されるため迂闊に計算量を増やしてしまうとすぐに重くなってしまいます。パフォーマンスチューニングのスキルが試されるところです。

timetimenow は Pine Script の組み込み変数です。time バーの時間を、timenow は現在の時間を保持しています。

inSession 関数は関数内で na というキーワードがあります。 これは Pine Script 上で値が利用できないことを意味するキーワードです。引数として東京時間を表す文字列を受け取っていて、not 東京時間ではない = 東京時間である という条件処理の結果を返しています。

その下には変数の宣言や初期化コードがありますね。

var string tz = "GMT+9"
isSessionStart = inSession and not inSession[1]
isSessionEnd = inSession[1] and not inSession

Pine Script では var をつけて宣言すると初回バーの実行時のみ初期化されます。

それに対して isSessionStart などの変数はバーごとに実行されるたびに初期化されます。 バーごとに東京時間かどうかを判定する必要があるのでバーごとに初期化が必要になりますね。 そしてもうひとつ、配列操作のような inSession[1] という記述が出てきました。

Pine Script では [] は history-referencing operator と呼ばれていて、バーの履歴を参照する演算子です。inSession[1] は 1つ前のバーが東京時間かどうかを判定しています。つまり、現在のバーが inSession (東京時間)で、1つ前のバー inSession[1] が東京時間ではない場合に東京時間が開始したバーであることがわかります。

history-referencing operator で過去のバーを参照できる

時系列データを配列のようにアクセスできるというのも面白いですね。

あとはバーごとに東京時間の高値と安値を求めていき、isSessionEnd になったら line.new() という関数呼び出しで線を引くという実装になっています。

東京時間を判定する考え方

また、取引時間中はバーがリアルタイムに更新されていきます。バーが更新されるたびに線の座標を更新し、日付が変わったら線の削除を行っています。

実際に線が引かれているチャートが以下です。

12/6 のドル円の1時間足チャート

おわりに

今回はチャートに線を引くという簡単なコードでしたが、Pine Script ではチャートの時系列データを扱えるため高度な分析や売買戦略を実装することができます。売買のシグナルを外部サービスに通知して自動売買を行うことも可能です。

市場に資金を入れている以上リスクはありますが、寝てる間にもお金を稼いでくれる可能性のあるコードと思うと、とても夢が広がりますね。

カヤックではいろんなコードを書くエンジニアを募集しています

hubspot.kayac.com

*1:サマータイムかどうかによって時間帯は変わります