恥は/dev/nullへ by 初心者

プログラミング素人がのろのろと学んだことをつづっています♪

チャートにインディケーターをセットする関数

MQL4でチャートにインディケーターを配置する関数を作るにはどうしたら良いか調べたところ、以下のページを見つけました。

【ネタ元 Source URL】
https://www.mql5.com/en/forum/73391
https://www.forexfactory.com/thread/981735-issues-with-postmessagew-in-mql4

 

今後使う可能性があるので、上記サイトからコピーしてきたコードを貼り付けておきます。(ほんの少し変更していますが、基本的に同じコードです。)

 

コードの冒頭に以下を記述します。これは「RegisterWindowMessageW関数」と「PostMessageA関数」を使えるようにするためです。

#import "user32.dll"
int RegisterWindowMessageW(string MessageName);
bool PostMessageW(int hWnd, int msg, int wParam, uchar &lParam[]);
#import

 

ファイルのどこかで以下の関数を定義します。

void StartCustomIndicator(int hWnd, string indicatorName)
{
    uchar charArray[];
    StringToCharArray(indicatorName, charArray, 0, StringLen(indicatorName));

    int MessageNumber = RegisterWindowMessageW("MetaTrader4_Internal_Message");
    bool uho = PostMessageW(hWnd, MessageNumber, 15, charArray);
    Sleep(10);
}

 

関数の使用例は以下のとおりです。

int hWnd = WindowHandle(Symbol(), 0);
StartCustomIndicator(hWnd, "PaintMarketTime.ex4");
// 第2引数の" "内は19文字以内にする

 コメントで「第2引数の" "内は19文字以内にする」と記述しておきましたが、20文字以上にすると上手くいきませんでした(ex4ファイルをopenできないというエラーが発生しました)。

 

備考

インディケーターで上述の関数を使用したところ、ex4ファイルをopenできませんでした。スクリプトとEAでは動作しました。

 

 

現在日時がサマータイム期間かどうかを調べる関数

使うインディケーターによっては、ロンドンやNYがサマータイム期間かどうかで制御を変える場合があるので、各市場がサマータイム期間なのか調べる関数を書いてみました。

 

サマータイムの開始日時と終了日時

■ロンドン
開始日時は  3月最終日曜日 UTC 1:00
終了日時は 10月最終日曜日 UTC 1:00

■NY
開始日時は  3月第2日曜日 UTC 7:00
終了日時は 11月第1日曜日 UTC 6:00

 

MT4のTimeGMT関数の注意点

MT4のTimeGMT関数について。この関数は、サーバー時間ではなくPCの時間設定を元にGMT時刻を返します。たとえば、PCのタイムゾーンが「(UTC+09:00) 大阪、札幌、東京」になっている場合、PC時刻から見て9時間前を返します。
このため、PCのタイムゾーンや時刻がおかしいと計算に間違いが生じます。

 

サマータイムかどうかを調べる関数(2種類)

引数についてはコードの先頭のコメントのとおりです。たとえば、日本で使う場合の例としては・・・

LondonIsSummerTime(TimeLocal(), 9, false)


この例を実行すると(TimeLocal()は現在のPC時刻なので)現在の日本時間においてロンドンがサマータイム期間ならば true が返って来ます。サマータイム期間でなければ false が返ってきます。

なお、第2引数を9としているので、PCの時計が日本時間になっている必要があります。

//--------------------------------------------------------------------------
// ロンドンがサマータイム期間か調べる関数
//
// [説明]
//   引数として指定したローカル日時が英国のサマータイム期間ならtrueを返す。
//   サマータイム期間でない場合はfalseを返す。
// [引数]
//   第1引数:ローカル日時
//            【引数の例】TimeLocal()
//   第2引数:ローカル時間とUTCとの時差
//            【引数の例】日本時間は「UTC +9:00」なので、第2引数は 9
//   第3引数:サマータイムの開始日時と終了日時をエキスパートに表示する
//            【引数の例】表示するならtrue、表示しないならfalse
//--------------------------------------------------------------------------
bool LondonIsSummerTime(datetime localTime, int offset, bool printDate)
{
    // 現在のローカル時刻をUTCに変換したもの
    datetime currentUTC = localTime - (offset * 3600);  // 1時間は3600秒
    
    // 現在の「年」を文字列で表したもの
    string thisYear = IntegerToString(TimeYear(currentUTC));

    //--------------------------------------------------------
    // 3月最後の日曜日の1:00(UTC時刻)を求める
    //--------------------------------------------------------
    // 今年の3月31日
    datetime March31 = StringToTime(thisYear + ".03.31 01:00");
    // サマータイムUTC 1:00 に始まるので文字列の末尾は「01:00」

    // 3月31日の曜日
    int dayOfMarch31 = TimeDayOfWeek(March31);
    // TimeDayOfWeekの戻り値は0~6。0が日曜日、6が土曜日を表す。

    // 3月31日の曜日が日曜日から何秒ズレているか
    //   [備考] 1日は86400秒
    int diffFromSun = 0;

    if(dayOfMarch31) diffFromSun = dayOfMarch31 * 86400;
    // dayOfMarch31 == 0 なら 3月31日が3月最後の日曜日

    // 3月最後の日曜日のUTC 1:00
    datetime MarchLastSun = March31 - diffFromSun;

    // チェック用
    if(printDate) printf("ロンドンサマータイム開始日時(UTC時刻): " + TimeToStr(MarchLastSun));

    //--------------------------------------------------------
    // 10月最後の日曜日の1:00(UTC時刻)を求める
    //--------------------------------------------------------
    // 今年の10月31日
    datetime Oct31 = StringToTime(thisYear + ".10.31 01:00");
    // サマータイムUTC 1:00 に終わるので文字列の末尾は「01:00」

    // 10月31日の曜日
    int dayOfOct31 = TimeDayOfWeek(Oct31);

    // 10月31日の曜日が日曜日から何秒ズレているか
    if(dayOfOct31) diffFromSun = dayOfOct31 * 86400;
    else diffFromSun = 0; // 3月の計算でこの変数を使っているので上書きが必要
    // dayOfOct31 == 0 なら 10月31日が10月最後の日曜日

    // 10月最後の日曜日のUTC 1:00
    datetime OctLastSun = Oct31 - diffFromSun;

    // チェック用
    if(printDate) printf("ロンドンサマータイム終了日時(UTC時刻): " + TimeToStr(OctLastSun));

    //--------------------------------------------------------
    // 戻り値
    //--------------------------------------------------------
    bool res = false;

    // ロンドンがサマータイム中ならresにtrueを代入
    if(currentUTC >= MarchLastSun && currentUTC < OctLastSun){
        res = true;
    }

    return res;
}


//--------------------------------------------------------------------------
// NYがサマータイム期間か調べる関数
//
// [説明]
//   引数として指定したローカル日時が、NYのサマータイム期間ならtrueを返す。
//   サマータイム期間でない場合はfalseを返す。
// [引数]
//   第1引数:ローカル日時
//            【引数の例】TimeLocal()
//   第2引数:ローカル時間とUTCとの時差
//            【引数の例】日本時間は「UTC +9:00」なので、第2引数は 9
//   第3引数:サマータイムの開始日時と終了日時をエキスパートに表示する
//            【引数の例】表示するならtrue、表示しないならfalse
//--------------------------------------------------------------------------
bool NewYorkIsSummerTime(datetime localTime, int offset, bool printDate)
{
    // 現在のローカル時刻をUTCに変換したもの
    datetime currentUTC = localTime - (offset * 3600);  // 1時間は3600秒
    
    // 現在の「年」を文字列で表したもの
    string thisYear = IntegerToString(TimeYear(currentUTC));

    //--------------------------------------------------------
    // 3月第2日曜日の7:00(UTC時刻)を求める
    //--------------------------------------------------------
    // 今年の3月1日
    datetime MarchFirst = StringToTime(thisYear + ".03.01 07:00");
    // NYのサマータイムUTC 7:00 に始まるので文字列の末尾は「07:00」

    // 3月1日の曜日
    int dayOfMarchFirst = TimeDayOfWeek(MarchFirst);
    // TimeDayOfWeekの戻り値は0~6。0が日曜日、6が土曜日を表す。

    // 3月1日が第2日曜日から何秒ズレているかをdiffFromSunに代入
    //   [備考] 1日は86400秒
    int diffFromSun = 0;
    if(dayOfMarchFirst) diffFromSun = (14 - dayOfMarchFirst) * 86400;
    else diffFromSun = 7 * 86400;
    // [備考] dayOfMarchFirst == 0 の場合は、3月1日が第1日曜日
    //        なので、else行には(14ではなく)7を使う。

    // 3月第2日曜日のUTC 7:00
    datetime MarchSecondSun = MarchFirst + diffFromSun;

    // チェック用
    if(printDate) printf("NYサマータイム開始日時(UTC時刻): " + TimeToStr(MarchSecondSun));

    //--------------------------------------------------------
    // 11月第1日曜日の6:00(UTC時刻)を求める
    //--------------------------------------------------------
    // 今年の11月1日
    datetime NovFirst = StringToTime(thisYear + ".11.01 06:00");
    // NYのサマータイムUTC 6:00 に終わるので文字列の末尾は「06:00」

    // 11月1日の曜日
    int dayOfNovFirst = TimeDayOfWeek(NovFirst);

    // 11月1日が第1日曜日から何秒ズレているかをdiffFromSunに代入
    diffFromSun = 0; // 3月第2日曜日の処理にこの変数を使っているのでリセット
    if(dayOfNovFirst) diffFromSun = (7 - dayOfNovFirst) * 86400;
    // [備考] dayOfNovFirst == 0 なら 11月1日が第1日曜日

    // 11月第1日曜日のUTC 6:00
    datetime NovFirstSun = NovFirst + diffFromSun;

    // チェック用
    if(printDate) printf("NYサマータイム終了日時(UTC時刻): " + TimeToStr(NovFirstSun));

    //--------------------------------------------------------
    // 戻り値
    //--------------------------------------------------------
    bool res = false;

    // NYがサマータイム中ならresにtrueを代入
    if(currentUTC >= MarchSecondSun && currentUTC < NovFirstSun){
        res = true;
    }

    return res;
}

開いているチャートの数をカウントする

やっていることは大したことないのですが、たまに他のチャートの情報を取ってくることがあるので、開いているチャートの数に応じた動作をするスクリプトをブログに貼り付けておくことにしました。(ここからコピペして使おうと思います)。

 

以下のコードは開いているチャートの数を数えます。

#property copyright "Copyright 2020. PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict

void OnStart()
{
    int  counter = 1;  // チャートの数
    // [備考] 1つもチャートが無い状態では、EAもインジもスクリプトも実行できない
    //        ので必ず1つはチャートを開いている。

    // 2つ目のチャートのIDをnextChartに代入
    // [備考] 2つ目のチャートが無い場合は、-1が代入される
    long nextChart = ChartNext(ChartFirst());

    while(nextChart != -1){
        counter++;
        nextChart = ChartNext(nextChart);
    }

    printf("チャートの個数は: %d", counter);

}

 

for文の例

実際に使う場合は、for文でループさせる気がします。

void OnStart()
{
    int n = 1;
    for(long nextchart = ChartNext(ChartFirst()); nextchart != -1; nextchart = ChartNext(nextchart)){
        printf("%d回目", n);
        n++;
    }
    // このコードを実行すると「チャートの数 - 1」回だけループすることが分かる。
    // チャートの数だけループするわけではない点に注意。
}

 

Vimでコードの折り畳み and インデントの可視化

Vimの設定の話を少々。

MQLのコードは基本的にVimで書いているのですが、Visual Studioのようにコードの折り畳みができたら便利だなと思いました。そこで調べてみたら、遥か昔からそういう機能が実装されているようです。

 

コードの折り畳み

折り畳みに関する設定内容を .vimrcに記述しました。

set foldmethod=indent
set foldlevel=8
set foldcolumn=2

1行目の記述はインデント単位で折り畳みを行うという意味です。
2行目については、foldlevelに5を指定すると、5番目に深いインデント以下(例:5番目、6番目、7番目・・・)が折り畳まれた状態でファイルが開きます。自分の場合、ファイルを開いた時点では折り畳まれていない方が嬉しいので、わざと 8 を指定しています。
3行目では、画面の左端の何列を折り畳み表示に使うかを指定しています。ネットを見ると3くらいを指定している例を何回か目にしましたが、インデントの深さが分かるだけなので、1や2でも問題ない気がしました。

 

折り畳みについては色々なコマンドが用意されていますが、自分が良く使いそうなものは以下の3つあたりでしょうか。

zc 折り畳み(カーソル位置)
zo 展開(カーソル位置)
zR 展開(ファイル全体)

 

インデントの可視化

ついでにインデントを可視化できると便利だなと思ったので、indentLineというプラグインを導入しました。プラグインを所定のディレクトリに置いてから、.vimrcに以下の記述をしました。

let g:indentLine_color_term = 111
let g:indentLine_color_gui = '#708090'
let g:indentLine_char = '¦'


以上の設定を終えた結果、以下のようになりました。


インデントが可視化された状態

f:id:philojuan:20200901024748p:plain

 

zcコマンドでコードの一部を折り畳んだ状態

f:id:philojuan:20200901024802p:plain

 

余談

世界中で使われているエディタだけあって、「こういうことがやりたいな」と思ってネットを調べると、十中八九その機能を誰かが既に作ってくれています。改めて、Vimって凄いなあと思います。

ちなみに、今でこそこんな風にVimを称賛しているものの、学生時代のレポートはEmacsで書いていました(汗)。その頃は、Vimの操作方法を面倒くさいと思っていたのです。

しかし、慣れてしまうと超便利です。今では日本語の文章もVimで書いています。

 

パラメーターで任意の時間軸を選択し、MTFの移動平均線を表示する

以前はたまにしかMTF(MultiTimeFrame)の移動平均線をチャートに表示していなかったので、必要になったら、その都度、移動平均線を描画するインディケーターを作っていました。

 

しかし、最近MTF移動平均線をチャートに入れてあれこれ調べることが増えました。こうなると、その都度作るのが面倒になってきたため、パラメーターで任意の時間軸(MTF)を選ぶ方式のコードを書きました。

 

f:id:philojuan:20200829031524p:plain

 

コード

//----------------------------------------------------------------------------
// FileName : MyMA_Multi.mq4
// Feature  : MTFのMAを表示するインディケーター
//----------------------------------------------------------------------------

#property copyright "Copyright 2020. PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property indicator_chart_window
#property strict

#property indicator_buffers 1

enum OnOff {OFF, ON};

input ENUM_TIMEFRAMES maTF     = PERIOD_CURRENT;  // MAの時間軸(MultiTimeFrame)
input int maPeriod             = 20;              // MAの期間
input ENUM_MA_METHOD method    = MODE_SMA;        // 計算方法(SMA, EMA等)
input ENUM_APPLIED_PRICE price = PRICE_CLOSE;     // 適用価格(デフォルトは終値)
input int maWidth              = 2;               // MAの太さ
input color maColor            = clrLightPink;    // MAの色
input OnOff repaint            = ON;              // リペイント

int chartTF = 0;             // チャートのTimeFrameを格納する変数
int BarsToRecalculate = 0;   // リペイントの対象となるローソク足の本数

// MA用バッファ
double MultiMA[];

int OnInit()
{
    // ラベル名を作成
    string label = ChangeTimeFrameExp(maTF) + "_" + IntegerToString(maPeriod)
                 + "_" + ChangeMAmodeExp(method);

    SetIndexStyle (0, DRAW_LINE, STYLE_SOLID, maWidth, maColor);
    SetIndexBuffer(0, MultiMA);
    SetIndexLabel (0, label);

    chartTF = Period();

    // MAをリペイントするーソク足の本数を計算
    //  → MAの時間軸(分) ÷ チャートの時間軸(分) = リペイント本数
    if(repaint && (maTF > chartTF))
    {
        if((maTF % chartTF) == 0)
            BarsToRecalculate = maTF / chartTF;
        else
            BarsToRecalculate = maTF / chartTF + 1;
    }

    return(0);
}

int start()
{
    int counted_bars = IndicatorCounted();

    if(!counted_bars)  // counted_barsが 0 のとき
    {
        // 計算対象から除外するローソク足の本数を計算
        if((maTF * maPeriod % chartTF) == 0)
            counted_bars = maTF * maPeriod / chartTF;
        else
            counted_bars = maTF * maPeriod / chartTF + 1;
    }
    else if(repaint) // counted_barsが 0 ではなく、かつ、repaintがONのとき
    {
        // リペイントするローソク足の本数を counted_bars から差し引く。
        counted_bars = counted_bars - BarsToRecalculate;
    }

    // limitは計算対象となるローソク足の本数
    int limit = Bars - counted_bars;

    for(int i = limit - 1; i >= 0; i--)
    {
        MultiMA[i] = EMPTY_VALUE;
        int shift_Multi = iBarShift(NULL, maTF, iTime(NULL, 0, i), false);

        MultiMA[i] = iMA(NULL, maTF, maPeriod, 0, method, price, shift_Multi);
    }

    return(0);
}

// MAの計算方法の表現を「SMA」等に変換する関数
//  → 「MODE_SMA」等は整数値。これを「SMA」等の文字列にして返す。
string ChangeMAmodeExp(ENUM_MA_METHOD original)
{
    string res = "";
    switch(original)
    {
        case 0:          // MODE_SMA
            res = "SMA";
            break;

        case 1:          // MODE_EMA
            res = "EMA";
            break;

        case 2:          // MODE_SMMA
            res = "SMMA";
            break;

        case 3:          // MODE_LWMA
            res = "LWMA";
            break;

        default:
            res = "ERROR";
            break;
    }

    return res;
}

// 時間軸の表現を「M1」等に変換する関数
//  → 「PERIOD_M5」等は整数値。これを「M5」等の文字列にして返す。
string ChangeTimeFrameExp(ENUM_TIMEFRAMES tf)
{
    string res = "";   // 戻り値を格納する変数

    // 通常は、引数で受け取った時間軸を使用
    int frame  = tf;

    // 引数が PERIOD_CURRENTの場合、チャートの時間軸を使用
    if(!tf) frame = Period();

    // 変換処理
    if(frame < 60)
        res = "M" + IntegerToString(frame);
    else if(frame < 1440)  // D1は1440分
        res = "H" + IntegerToString(frame / 60);
    else if(frame == 1440)
        res = "D1";
    else if(frame == 10080)
        res = "W1";
    else if(frame == 43200)
        res = "MN1";
    else
        res = "ERROR";

    return res;
}

 

 

3市場の現地時刻を表示するインディケーター

【追記】

初めてこの記事をアップした当時はロンドンやニューヨークがサマータイム期間かどうかをパラメーターで指定する仕様になっていましたが、その後、サマータイム期間かどうかを調べる関数を作成したので、このインディケーターにもその関数を組み込みました。

この結果、サマータイム期間かどうかを指定するパラメーターは除去しました。

【追記 ここまで】 


 

ネット上に3市場の現在時刻を表示するインディケーターがありました。

裁量トレードをやっている時に便利そうだなと思った一方で、フォント、表示色、文字サイズなどを自分好みにしたかったので、自分で作ってみました。

 

スクリーンショット

f:id:philojuan:20201102183654p:plain

 

ロンドンのサマータイム期間は10月最後の日曜日までですが、ニューヨークのサマータイム期間は11月最初の日曜日までです。このため、ニューヨークだけサマータイムという期間が10月の末に存在します。

その期間は以下のようになります(実際の画像)。

f:id:philojuan:20201103091625p:plain

 

 

コード

//------------------------------------------------------------------
//  FileName : ShowMarketTimes.mq4
//------------------------------------------------------------------
#property copyright "Copyright 2020, PHILOJUAN"
#property link      "https://uhoho.hatenablog.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

input color mtColor = C'227, 2, 132'; // 表示色
input int fontsize  = 12;             // フォントサイズ
input int ctXdis    = 440;            // 表示位置(X軸)
input int ctYdis    = 2;              // 表示位置(Y軸)

// 時差用変数
datetime LondonOffset, NYOffset;

// 都市名
string LonLabel = "LON ";
string NYLabel  = "NY ";

// サマータイム期間
bool LondonSummerTime = false;
bool NYSummerTime     = false;

int OnInit()
{
    EventSetTimer(1);

    // ロンドンがサマータイムならtrueを代入
    LondonSummerTime = LondonIsSummerTime(TimeLocal(), 9, false);

    // NYがサマータイムならtrueを代入
    // 引数2と引数3は省略(デフォルト値が使われる)
    NYSummerTime     = NewYorkIsSummerTime(TimeLocal(), 9, false);

    // ロンドンがサマータイムの時
    if(LondonSummerTime)
    {
        LondonOffset = 28800;   // 時差を28800秒にする
        LonLabel  = "LON_Sum "; // 都市名を LON_Sum にする
    }
    // ロンドンがサマータイムではない時
    else
    {
        LondonOffset = 32400; // 時差を32400秒にする
        LonLabel  = "LON ";   // 都市名を LON にする(パラメーター変更時等を考慮)
    }

    // ニューヨークがサマータイムの時
    if(NYSummerTime)
    {
        NYOffset = 46800;     // 時差を46800秒にする
        NYLabel  = "NY_Sum "; // 都市名を NY_Sum にする
    }
    // ニューヨークがサマータイムではない時
    else
    {
        NYOffset = 50400;    // 通常の時差は50400秒
        NYLabel  = "NY ";    // 都市名を NY にする(パラメーター変更時等を考慮)
    }

    return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    return(rates_total);
}

void OnTimer()
{
    // 3市場の時刻
    datetime TokyoTime  = TimeLocal(); // 東京時刻を基準とする
    datetime LondonTime = TokyoTime - LondonOffset;
    datetime NYTime     = TokyoTime - NYOffset;

    string marketTimes = "TKY "   + IntegerToString(TimeHour(TokyoTime))  + ":" + IntegerToString(TimeMinute(TokyoTime), 2, '0')  + "   "
                         + LonLabel + IntegerToString(TimeHour(LondonTime)) + ":" + IntegerToString(TimeMinute(LondonTime), 2, '0') + "   "
                         + NYLabel  + IntegerToString(TimeHour(NYTime))     + ":" + IntegerToString(TimeMinute(NYTime), 2, '0');

    ObjectDelete(0, "ShowMarketTimes");
    MakeLabel("ShowMarketTimes", ctXdis, ctYdis, 20, 25);
    ObjectSetText("ShowMarketTimes", marketTimes, fontsize, "Arial Rounded MT Bold", mtColor);

}

void OnDeinit(const int reason)
{
    EventKillTimer();
    ObjectDelete(0, "ShowMarketTimes");
}

//--------------------------------------------------------------------------
// ラベル作成関数
//--------------------------------------------------------------------------
void MakeLabel(string objName,   // ラベルオブジェクトの名前
               int xDistance,    // ラベル配置位置の起点(X軸)
               int yDistance,    // ラベル配置位置の起点(Y軸)
               int xSize,        // ラベルのサイズ(横)
               int ySize         // ラベルのサイズ(縦)
              )
{
    ObjectCreate(objName, OBJ_LABEL, 0, 0, 0);
    ObjectSet(objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
    ObjectSet(objName, OBJPROP_XDISTANCE, xDistance);
    ObjectSet(objName, OBJPROP_YDISTANCE, yDistance);
    ObjectSet(objName, OBJPROP_XSIZE, xSize);
    ObjectSet(objName, OBJPROP_YSIZE, ySize);
    ObjectSet(objName, OBJPROP_SELECTABLE, false);
    ObjectSet(objName, OBJPROP_HIDDEN, true);
}

//--------------------------------------------------------------------------
// ロンドンがサマータイム期間か調べる関数
//--------------------------------------------------------------------------
// [説明]
//   引数として指定したローカル日時が英国のサマータイム期間ならtrueを返す。
//   サマータイム期間でない場合はfalseを返す。
// [引数]
//   第1引数:ローカル日時
//            【引数の例】TimeLocal()
//   第2引数:ローカル時間とUTCとの時差
//            【引数の例】日本時間は「UTC +9:00」なので、第2引数は 9
//   第3引数:サマータイムの開始日時と終了日時をエキスパートに表示する
//            【引数の例】表示するならtrue、表示しないならfalse
//--------------------------------------------------------------------------
bool LondonIsSummerTime(datetime localTime, int offset, bool printDate)
{
    datetime currentUTC = localTime - (offset * 3600);  // 1時間は3600秒
    
    string thisYear = IntegerToString(TimeYear(currentUTC));

    datetime March31 = StringToTime(thisYear + ".03.31 01:00");

    int dayOfMarch31 = TimeDayOfWeek(March31);

    int diffFromSun = 0;

    if(dayOfMarch31) diffFromSun = dayOfMarch31 * 86400;

    datetime MarchLastSun = March31 - diffFromSun;

    // チェック用
    if(printDate) printf("ロンドンサマータイム開始日時(UTC時刻): " + TimeToStr(MarchLastSun));
    
    // エラーチェック用コード
    if(TimeDayOfWeek(MarchLastSun)) Alert("ERROR: MarchLastSun is not Sunday!");

    datetime Oct31 = StringToTime(thisYear + ".10.31 01:00");

    int dayOfOct31 = TimeDayOfWeek(Oct31);

    if(dayOfOct31) diffFromSun = dayOfOct31 * 86400;
    else diffFromSun = 0; // 3月の計算でこの変数を使っているので上書きが必要

    datetime OctLastSun = Oct31 - diffFromSun;

    // チェック用
    if(printDate) printf("ロンドンサマータイム終了日時(UTC時刻): " + TimeToStr(OctLastSun));

    // エラーチェック用コード
    if(TimeDayOfWeek(OctLastSun)) Alert("ERROR: OctLastSun is not Sunday!");

    bool res = false;

    if(currentUTC >= MarchLastSun && currentUTC < OctLastSun){
        res = true;
    }

    return res;
}

//--------------------------------------------------------------------------
// NYがサマータイム期間か調べる関数
//--------------------------------------------------------------------------
// [説明]
//   引数として指定したローカル日時が、NYのサマータイム期間ならtrueを返す。
//   サマータイム期間でない場合はfalseを返す。
// [引数]
//   第1引数:ローカル日時
//            【引数の例】TimeLocal()
//   第2引数:ローカル時間とUTCとの時差
//            【引数の例】日本時間は「UTC +9:00」なので、第2引数は 9
//   第3引数:サマータイムの開始日時と終了日時をエキスパートに表示する
//            【引数の例】表示するならtrue、表示しないならfalse
//--------------------------------------------------------------------------
bool NewYorkIsSummerTime(datetime localTime, int offset, bool printDate)
{
    datetime currentUTC = localTime - (offset * 3600);  // 1時間は3600秒
    
    string thisYear = IntegerToString(TimeYear(currentUTC));

    datetime MarchFirst = StringToTime(thisYear + ".03.01 07:00");

    int dayOfMarchFirst = TimeDayOfWeek(MarchFirst);

    int diffFromSun = 0;
    
    if(dayOfMarchFirst) diffFromSun = (14 - dayOfMarchFirst) * 86400;
    else diffFromSun = 7 * 86400;

    datetime MarchSecondSun = MarchFirst + diffFromSun;

    // チェック用
    if(printDate) printf("NYサマータイム開始日時(UTC時刻): " + TimeToStr(MarchSecondSun));
    
    // エラーチェック用コード
    if(TimeDayOfWeek(MarchSecondSun)) Alert("ERROR: MarchSecondSun is not Sunday!");

    datetime NovFirst = StringToTime(thisYear + ".11.01 06:00");

    int dayOfNovFirst = TimeDayOfWeek(NovFirst);

    diffFromSun = 0; // 3月第2日曜日の処理にこの変数を使っているのでリセット
    if(dayOfNovFirst) diffFromSun = (7 - dayOfNovFirst) * 86400;

    datetime NovFirstSun = NovFirst + diffFromSun;

    // チェック用
    if(printDate) printf("NYサマータイム終了日時(UTC時刻): " + TimeToStr(NovFirstSun));

    // エラーチェック用コード
    if(TimeDayOfWeek(NovFirstSun)) Alert("ERROR: NovFirstSun is not Sunday!");

    bool res = false;

    if(currentUTC >= MarchSecondSun && currentUTC < NovFirstSun){
        res = true;
    }

    return res;
}

 

ArrayResizeで要素数を増やした場合、配列に格納した値に影響は生じるか? (ついでに、--i の失敗談)

タイトル通りの疑問が浮かんだので実験してみました。

必要に応じて配列の要素数を増やそうと思っているのですが、ArrayResizeで配列の要素数増加させた場合、それ以前に存在した要素の値に影響があるのか調べました。

 

実験に使用したスクリプト

#property copyright "Copyright 2020. PHILOJUAN"
#property version   "1.00"
#property strict

void OnStart()
{
    int hoge[];
    ArrayResize(hoge, 1);
    hoge[0] = 999;

    for(int i = 2; i < 11; i++){
        ArrayResize(hoge, i);
        hoge[i - 1] = i - 1;
    }
    
    for(int j = 0; j <= 9; j++){
        printf("hoge[%d]: %d", j, hoge[j]);
    }
}

 

MT4のエキスパートに出力されたもの

 hoge[9]: 9
 hoge[8]: 8
 hoge[7]: 7
 hoge[6]: 6
 hoge[5]: 5
 hoge[4]: 4
 hoge[3]: 3
 hoge[2]: 2
 hoge[1]: 1
 hoge[0]: 999

 

上記の結果を見る限り、素数を増加させる前に格納した値には影響が無いと分かったので、安心してArrayResizeを使いたいと思います^^。

 

 失敗したこと

 hoge[i - 1] = i - 1;

の行で、「i - 1」の代わりに

 --i

 を使ってしまい、ループが終了しなくなるというミスをしました(汗)。

ここで i の値を1つ減らしてしまうと、for文の「i++」と相まって、 i の値は「1減って、1増えて」を繰り返すので、いつまで経ってもfor文が終わらないことになります。

 

備考

 i - 1

という式は、i の値を変化させません。しかし、

 --i

は、以下の代入式と同じなので i の値を変化させてしまいます。

i = i - 1