Pine Scriptのコードを読んでいると、`type`で定義した型に対して、

`.`(ドット)で関数を呼んでいる書き方を見かけることがあります。

「MQLの構造体(struct)と同じようなものだろう」と、思ったことはありませんか。

実は、構造体にはできないことが、Pine Scriptのtypeにはできます。

既刊struct-mql4-mql5.mdと、

mql5-pointer-object-cobject.mdでは、

MQLの構造体とオブジェクトポインタを解説しました。

この記事では、Pine Scriptのtype(ユーザー定義型)とmethodを、

この2つの既刊記事と対比しながら解説します。

この記事で分かること
・typeキーワードでのユーザー定義型(UDT)の宣言方法
・methodキーワードでの関数の紐付け方法
・MQLの構造体(データのみ)との違い
・Pine Scriptのオブジェクトが常に参照型として振る舞う理由

type・methodの基本とMQLの対応

まず、それぞれの基本的な書式を確認します。

Pine Scriptのtype・methodの書式

type StreakCounter
    int count = 0
    int direction = 0

method update(StreakCounter s, float diff) =>
    newDirection = diff > 0 ? 1 : diff < 0 ? -1 : 0
    s

typeでフィールドを持つ型を定義し、methodでその型専用の関数を定義します。

MQLではstruct(構造体)と関数を別々に書く

struct StreakCounter
  {
   int count;
   int direction;
  };

void UpdateStreak(StreakCounter &s, double diff)
  {
   int newDirection = diff > 0 ? 1 : (diff < 0 ? -1 : 0);
  }

既刊struct-mql4-mql5.mdのとおり、

MQLの構造体はデータの集まりでしかなく、

更新用の関数(UpdateStreak)は構造体の外に、

別の独立した関数として書く必要があります。

最大の違い: データだけの構造体 vs 関数まで持てる型

ここが、MQLユーザーが最初に戸惑うポイントです。

Pine Scriptはtypeとmethodがセットで完結する

補足
・methodはそのtypeの第1パラメータに紐付く専用関数
・呼び出しはstreak.update(diff)のようにドット記法で書ける
・MQL5のクラス(CTrade等のメンバー関数)に近い感覚

MQLで同じことをするにはクラス(class)が必要

MQLで「データ+専用関数」をひとまとめにするには、structではなくclassを使い、

メンバー関数として定義する必要があります。

既刊ctrade-mql5.mdで解説したCTradeクラスも、

この仕組みで発注用の関数をまとめています。

structとclassは見た目が近くても、

MQLでは明確に別の仕組みとして教える必要がありました。

もう一つの違い: 参照渡しを&で書かなくていい理由

ここが、この記事で最も伝えたいポイントです。

MQLの構造体は&を付けないと更新が反映されない

MQLユーザーが驚くポイント
・既刊pass-by-reference-value-mql4-mql5.mdのとおり
・構造体を関数に渡すと既定では値のコピーが渡る
・関数内で書き換えても、呼び出し元の構造体は変わらない
・&を付けて参照渡しにしないと更新が反映されない

Pine Scriptのtypeインスタンスは常に参照型

Pine Scriptのtypeで作ったオブジェクトは、

既刊mql5-pointer-object-cobject.mdの、

オブジェクトポインタと同じように、

常に「参照」として扱われます。

methodに渡したオブジェクトのフィールドを書き換えると、

呼び出し元の変数にもそのまま反映されるため、

MQLのように&を書く必要がありません。

ただし、フィールドへの直接書き込み(obj.field := 値)は、

method内のチェック処理を経由しない点に注意が必要です。

varを付けないと毎バー作り直される

既刊var/varipの記事と同じ注意点が、typeにも当てはまります。

var無しだと毎バー新しいインスタンスになる

既刊pinescript-var-varip-mql-comparison.mdで、

解説したとおり、

var宣言のない変数はバーごとに再評価されます。

StreakCounter.new()も例外ではなく、varを付けずに毎回呼ぶと、

count・directionの蓄積が毎バーリセットされてしまいます。

MQLの構造体はグローバル変数と同じように値を保持する

既刊struct-mql4-mql5.mdのとおり、

MQLの構造体はOnInit()で宣言すれば、

EA・インジケーターが動いている間、値を保持し続けます。

Pine Scriptのような「再生成」の心配はありません。

実際に動くサンプルコード: 連続本数を数えるStreakCounter

直近の値動きが何本連続で同じ方向かを数える設計です。

//@version=6
indicator("StreakCounter Sample", overlay = false)

type StreakCounter
    int count = 0
    int direction = 0

method update(StreakCounter s, float diff) =>
    newDirection = diff > 0 ? 1 : diff < 0 ? -1 : 0
    if newDirection == s.direction and newDirection != 0
        s.count := s.count + 1
    else
        s.count := 1
        s.direction := newDirection
    s

var StreakCounter streak = StreakCounter.new()
streak.update(close - close[1])

plot(streak.count, title = "連続本数", color = color.blue)

var宣言したstreakを、methodの中でフィールドごと書き換えています。

呼び出し元の変数を直接書き換えなくても、参照型であるおかげで変化がそのまま反映されます。

なお、終値が前バーと全く同じ(diffが0)ときは、

newDirectionが0になり、countは1にリセットされる仕様です。

「方向なし」を連続とは数えない設計だと理解しておいてください。

TradingViewでの貼り付け・実行手順

実際にTradingViewで動かすまでの手順です。

  1. TradingViewのチャート画面下部にある「Pineエディタ」タブを開く
  2. 新規スクリプトを作成し、上のコードを貼り付ける
  3. 「保存」してから「チャートに追加」をクリックする
  4. 別ウィンドウに連続本数(count)のラインが表示されることを確認する
  5. var StreakCounterのvarを外して再度試し、ラインが1以上に増えなくなることも確認する

よくあるエラーと注意点

実装でつまずきやすいポイントをまとめます。

method内でobj.field=値と書いてエラーになる

注意
・フィールドの再代入は=ではなく:=を使う
・=はtype宣言時のデフォルト値指定にのみ使う
・varと同じ「宣言は=、再代入は:=」というPine Script共通のルール

MQLのclassとstructを混同する

既刊struct-mql4-mql5.mdのstructは、

単純な型のみを持つ場合に「単純構造体」として相互コピーが可能ですが、

classはメンバー関数・継承・ポインタが絡むため、

同じ感覚でコピーできない場面が増えます。

よくある質問

type・methodについて、よく疑問に挙がる点に回答します。

実践アドバイス
・バーをまたいで保持したいオブジェクトには必ずvarを付ける
・フィールドの再代入は:=
・継続利用するオブジェクトはmethod経由で更新する

1つのtypeに複数のmethodを定義できますか?

できます。

update用・リセット用など、

目的ごとに複数のmethodを同じtypeに対して定義できます。

MQL5のクラスのように継承はできますか?

Pine Scriptのtypeには、MQL5のクラスのような継承の仕組みはありません。

既刊mql5-pointer-object-cobject.mdの、

CObject継承のような構造を、

そのまま移植することはできない点に注意が必要です。

配列の中にtypeのインスタンスを入れられますか?

入れられます。

既刊の配列関数の記事、

pinescript-array-functions-mql-comparison.md、

のarray関数と組み合わせ、

StreakCounterを複数まとめて配列で管理することもできます。

【Pine Script入門】type(ユーザー定義型)とmethodをMQLユーザー向けに解説してみた(構造体・クラスとの比較):まとめ

Pine Scriptのtypeは、MQLの構造体のようにフィールドを持てるだけでなく、

methodで専用の関数まで紐付けられる点が大きな違いです。

さらに、typeのインスタンスは常に参照型として振る舞うため、

MQLのように&を付けて参照渡しを明示する必要がありません。

ただし、var宣言を忘れると毎バー作り直されてしまう点は、

既刊var/varipの記事と同じ注意が必要です。

関連記事