【MT4】ボリンジャーバンドσ間の値を表示 その1・その2でボリンジャーバンドを使ってボラティリティを表示するインジケーターを作ってみました。
特に追加のオーダーがあったわけではないのですが、自分自身の勉強用に「任意の複数通貨ペア・すべての時間足における最新価格でのボリンジャーバンドσ間の値を一覧表示するインジケーター」を作ってみます。
ポイントはサブウインドウの使い方と、サブウインドウで効率的に一覧表示する方法について研究してみました。
インジケーターの仕様
ユーザーが設定した8種類の通貨ペア×MetaTraderの9種類の時間足、合計72種類の値を表示します(上図)。
それぞれの値、行・列の見出しを含めると90のラベルオブジェクトをサブウインドウを配置することになります。
同じようにサブウインドウへ一覧表示する海外のインジケーターでソースコードが公開されているものがあったのですが、あまりの酷さに参考になりませんでした。
何が酷いかというと、90あるオブジェクトを1つ1つ位置を指定してソースに書いてあったからです。個数を拡張したり、仕様を変更したときにまったく応用がききませんし、バグがあった場合も修正が大変です。
そして何より美しくありません。
自分の中で「コピペをしそうになったら関数化・for化をしろ!」というのをキモに銘じています。繰り返し処理にもかかわらず、関数化・for化できないものは設計が悪いので見直すべきだと思います。
上記の図のように、表形式で規則正しくオブジェクトを並べるので、以下のような命名規則で各オブジェクトの名称を決めてしまいます。
オブジェクトを生成
前章で決めたオブジェクト名で、実際にオブジェクトを作っていきます。
当然ですがfor文で、コピペすることなく効率よく生成します。逆に上図のようなオブジェクト名にしたのは、for文で効率よくつくるためです。
オブジェクトの名称はプログラムの中からのみ参照するもので、実際にインジケーターを使うユーザーは気にするところではないので、このようなプログラマ都合の命名規則でなんら不都合はありません。
オブジェクトを生成するコード例を掲載します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
void InitObject() { string objName; // M1, M5など for (int i = 0; i < 9; i++) { objName = "TF_Index_" + IntegerToString(i); ObjectCreate(0, objName, OBJ_LABEL, 1, 0, 0); } // USDJPYなど for (int i = 0; i < 8; i++) { objName = "Symbol_Index_" + IntegerToString(i); ObjectCreate(0, objName, OBJ_LABEL, 1, 0, 0); } // PIPS値(72個) for (int i = 0; i < 8; i++) { for (int j = 0; j < 9; j++) { objName = "Bora_"+IntegerToString(i) + IntegerToString(j); ObjectCreate(0, objName, OBJ_LABEL, 1, 0, 0); } } } |
上記のコードは、わかりやすくするために書いたモノで、実際はこのような書き方はしていません。一般的にプログラミングの作法としてNGとされている書き方をしているところと、あとはメンテナンスを考慮するとあまりよろしくない部分があるからです。
どこかわかりますか?
マジックナンバーをハードコーディングしない
マジックナンバーとは、プログラムの中で登場する定数などのことをいいます。また、ハードコーディングとは、ソースコード中に定数などをベタ書きすることです。
つまり上述のソースコードで「マジックナンバーをハードコーディングしない」とは、for文の条件式部分に書かれている8や9といった意味不明な数字を直接書くことはNGだということです。
理由の1つ目は、ソースの可読性にあります。この例のように8や9といった数字が突然あらわれても、いったい何を意味するかが不明です。ちなみに今回の例では、8は表示する通貨ペアの数、9は時間足の数です。
理由の2つ目は、ソースのメンテナンス性です。このインジケーターで8つ表示している通貨ペアを4つに減らそうと考えた場合、12行目と18行目の2カ所を書き換える必要があります。
この程度の短いソースコードならすぐに変更箇所を特定することが可能ですが、もっとソースが巨大になった場合は手に負えません。テキストの置換なんか、恐ろしくてできません。
解決策は、#defineを使うことです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#define NUM_OF_TIMEFRAME 9 #define NUM_OF_SYMBOL 8 void InitObject() { string objName; // M1, M5など for (int i = 0; i < NUM_OF_TIMEFRAME; i++) { objName = "TF_Index_" + IntegerToString(i); ObjectCreate(0, objName, OBJ_LABEL, 1, 0, 0); } // USDJPYなど for (int i = 0; i < NUM_OF_SYMBOL; i++) { objName = "Symbol_Index_" + IntegerToString(i); ObjectCreate(0, objName, OBJ_LABEL, 1, 0, 0); } // PIPS値(72個) for (int i = 0; i < NUM_OF_SYMBOL; i++) { for (int j = 0; j < NUM_OF_TIMEFRAME; j++) { objName = "Bora_"+IntegerToString(i) + IntegerToString(j); ObjectCreate(0, objName, OBJ_LABEL, 1, 0, 0); } } } |
こうすることで、意味不明だった定数が何を意味しているかがわかるようになりますし、数値を変えたい場合は1、2行目の#defineで定義している数字を書き換えるだけです。
配列を活用する
今回のように同じような処理を大量にする場合は、先に述べたようにfor文をうまく使うのと同じように、配列・テーブルをうまく使うとソースコードの可読性・メンテナンス性が向上します。
上述のソースコードではObjectCreate関数で作成したテキストラベルオブジェクトは、表示内容を書き換えるために何度もアクセスすることになります。そのときに必要となるのが、オブジェクト名(objName)です。アクセスする度に、オブジェクト名を生成していては非効率ですし、バグの温床にもなりそうです。なので、オブジェクト名称は配列にして保存しておきましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#define NUM_OF_TIMEFRAME 9 #define NUM_OF_SYMBOL 8 string objName_TF [ NUM_OF_TIMEFRAME ]; string objName_Symbol [ NUM_OF_SYMBOL ]; string objName_Pips [ NUM_OF_SYMBOL ] [ NUM_OF_TIMEFRAME ]; void InitObject() { // M1, M5など for (int i = 0; i < NUM_OF_TIMEFRAME; i++) { objName_TF[i] = "TF_Index_" + IntegerToString(i); ObjectCreate(0, objName_TF[i], OBJ_LABEL, 1, 0, 0); } // USDJPYなど for (int i = 0; i < NUM_OF_SYMBOL; i++) { objName_Symbol[i] = "Symbol_Index_" + IntegerToString(i); ObjectCreate(0, objName_Symbol[i], OBJ_LABEL, 1, 0, 0); } // PIPS値(72個) for (int i = 0; i < NUM_OF_SYMBOL; i++) { for (int j = 0; j < NUM_OF_TIMEFRAME; j++) { objName_Pips[i][j] = "Bora_"+IntegerToString(i) + IntegerToString(j); ObjectCreate(0, objName_Pips[i][j], OBJ_LABEL, 1, 0, 0); } } } |
オブジェクト名称を配列化することで、次回以降のアクセスが劇的に簡単になります。
また、前章で修正した#defineによるマジックナンバー化が、配列の定義部分においても可読性向上に一役買っています。
さいごに
今回はソースコードの全文は掲載せずに以上で終了とします。私が実際に稼働させているインジケーターでは、可読性・メンテナンス性向上の観点でもう2〜3の工夫を施しています。
リクエストがあればソースコード、実行ファイルを公開しますが、ぜひみなさんもご自身でコーディングしてみてください。
素晴らしいです!
ソースコードを拝見してみたいです!
きれいなコーディングを心がけています
でも、なかなか思うように作れません( ;∀;)
ソースコードを見せて頂けないでしょうか