恥は/dev/nullへ by 初心者

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

統計言語「R」をUbuntu 20.04.1 LTSにインストール

タイトルのまんまですが、統計処理に使うRという言語をインストールしました。

同じ作業をしないとも限らないので、備忘録として残しておくことにしました。

 

Ubuntuのバージョン確認

最初にUbuntuのバージョンを確認します。

catコマンドを使って、

cat /etc/os-release

もしくは

cat /etc/lsb-release

で確認できます。

 

catコマンドの出力結果は以下のとおりでした。

 f:id:philojuan:20201104023735p:plain

 この結果からUbuntuのバージョンは「20.04.1 LTS」で、コードネームは「focal」だと確認できます。

 

GPGキーの登録

ネットを調べたところ、GPGキーの登録が必要なようです。

以下のコマンドを実行します。 

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9

 

リポジトリの追加

次はaptコマンド用のリポジトリの追加です。

add-apt-repositoryコマンドで統計数理研究所のRリポジトリを追加します。

sudo add-apt-repository 'deb https://cran.ism.ac.jp/bin/linux/ubuntu focal-cran40/'

 このコマンドの末尾が「focal-cran40」となっていることに御注意ください。先にUbuntuのバージョンを確認しておいたのは、この部分(ディレクトリ名)に何を指定するか決めるためです。

もしバージョン「18.04」、コードネーム「bionic」だった場合、「focal-cran40」ではなく「bionic-cran40」となります。

 

念のため、リポジトリがsources.listファイルに追加されたか確認しておきます。

tailコマンドを使いましょう。

tail -n 4 /etc/apt/sources.list

 これは sources.listファイルのラスト4行を表示せよという意味です。 

 

実行結果は以下のとおりです。ちゃんとリポジトリが追加されていますね(下から2行目)。

f:id:philojuan:20201104024348p:plain

 

Rのインストール

あとは、以下のコマンドを実行するだけです。

sudo apt update
sudo apt install r-base

 インストールはこれで終了です。

 

Rが起動するか確認

念のため、Rが起動するか確認してみましょう。

ターミナルで「R」と入力したら、無事にRの画面が表示されました。

f:id:philojuan:20201104025659p:plain

 

Ubuntuのバージョン確認

Ubuntuのバージョン確認なんて1年に数えるくらいしか行わないので、どのファイルを見ればいいのか忘れてしまい、毎回ネット検索をしています。

 そんなわけで備忘録を残すことにしました。

 

/etc/os-release

を見ればOK。

 

ちなみに、今回バージョン確認をしたのは、統計分析用のRをインストールするためです。Rのリポジトリを開いた折、Ubuntuのバージョンごとにファイルが置かれていたのを見て「我が家のUbuntuのバージョンって何だったっけ?」となった次第です。

 

「os-release」の中を見たらバージョン18のLTSだと判明。

そのままでもRをインストールすることはできたのですが、既にUbuntuの最新バージョンは20になっているので、Ubuntuのバージョンアップを行いました。

 

バージョンアップにかかった時間は22分くらい。これといって問題無く終わり、今はバージョン「20.04.1 LTS (Focal Fossa)」になりました^^。

 

/etc/os-releaseの本体

os-releaseって普通のテキストファイルだよね・・・、と思いつつ、何となく気になったので lsコマンドで調べてみました。

$ ls -l /etc/os-release 
lrwxrwxrwx 1 root root 21 8月 17 22:31 /etc/os-release -> ../usr/lib/os-release

この結果からお分かりのとおり、/usr/lib/os-releaseへの シンボリックリンクになっていました。というわけで、本体は /usr/lib ディレクトリにあるようです。

知らなかった・・・^^;。

 

ヒストリカルデータのインポートとデータ生成(MT4)

EAのバックテストをするために自分が使用している業者のヒストリカルデータをインポートしたのですが、手順を忘れてしまいそうなので、メモ代わりに作業内容を残しておくことにしました。

 

通常は1分足のヒストリカルデータが配布されていると思うので、それを前提にこの記事は書かれています。

 

ヒストリカルデータのインポート

f:id:philojuan:20201102082941p:plain

「ツール」→「ヒストリーセンター」を開きます。

通貨ペアを選択し、「1分足(M1)」をダブルクリックします。

「インポート」ボタンをクリックします。

 

f:id:philojuan:20201102083106p:plain

「ファイル名」枠の右隣にある「参照」ボタンを使って、インポートするファイルを選択します。

 

f:id:philojuan:20201102083230p:plain

選択したファイルのデータが画面に表示されたら「OK」ボタンをクリック。
(これを取り込むファイルの数だけ繰り返します。)

 

これでヒストリカルデータの取り込みは完了です。

 

1分足以外のヒストリカルデータを生成する

現時点では、1分足のデータしか存在しません(5分足や15分足など、他の時間軸のデータがありません)。このため、5分足や1時間足などでバックテストをしようにもできない状況です。そこで、1分足以外のヒストリカルデータを生成します。

 

データを生成するには、通常、MT4の「スクリプト」の中にある「Period Coverter」を使いますが、Period Converter All Timeframesというスクリプトを使えば、5分足から月足のデータを一度に生成できるので便利です。

<Period Converter All Timeframesのある場所>
https://www.mql5.com/ja/code/19839

 

Period Converter All Timeframesを実行する前に、「ツール」→「オプション」→「チャート」の中にある次の2項目をチェックしておく必要があります。

・ヒストリー内の最大バー数
・チャートの最大バー数

これらの数値が小さいとデータの生成が上手く行かないことがあるからです。
ネット上の情報を見ていると、2147483647にしている人が多いようでした(MT4のint型の最大値?)。

 

この数値を変更した後、一度MT4を再起動します。
再起動しないと、変更後の数値が反映しないようです。

 

MT4を再起動したら、

(1)データを作成したい通貨ペアのチャートを開き
(2)チャートの時間軸をM1(1分足)にして
(3)Period Converter All Timeframesスクリプトを実行します。

 

f:id:philojuan:20210922023255p:plain

デフォルトでは日足から月足までのデータを生成しない設定になっています。もし、これらも生成したい場合はfalseとなっている部分をtrueに変更する必要があります。


スクリプトを実行し、データの生成が終わったら、MT4を再起動します。

「ヒストリーセンター」を開くと、1分足以外のヒストリカルデータが生成されていることを確認できるはずです。

 

データの生成に失敗する場合は、データ生成が失敗している時間足のヒストリカルデータを消去して(←間違っても、1分足のデータは消去しないこと)、もう一度Period Converter All Timeframesスクリプトを実行します。

 

ヒストリカルデータの場所については、MT4に付属しているMQL4 ReferenceのFileOpenHistory関数の項目を読むと以下のように書かれています。

history directory (terminal_directory\history\server_name) or in its subfolders

 

 チャートの最大バー数の注意点

ネット情報によると、「チャートの最大バー数」を2147483647にしたままで複数のチャートを開くとMT4が凄く重くなったり固まったりするケースがあるそうです。

自分の環境ではそういうことは起きていないのですが、もしそのような状況になった場合は、もっと少ない数値にした方が良さそうです。

 

大雑把に言うと、5分足を1年分表示する場合、10万本くらいあれば足ります。

よって、過去10年分のデータを5分足チャートに表示する場合、100万本くらいを「チャートの最大バー数」にセットしておくことになります。

 

一応、5分足1年分で10万本くらいと述べた理由を書いておきます。
1年間は525600分です。これを5で割ると105120ですから、1年分の5分足を表示する場合、チャートの本数は105120本です。ただし、相場が動いていない時間(土日など)が存在するので、実際には105120本より少なくなります。


というわけで、1年分の5分足をチャートに表示するのなら、「チャートの最大バー数」に105120を指定しておけば足ります。

robocopyのコマンドオプション

昨年12月からWindowsを使い始めましたが、オンラインストレージにしかバックアップをしていなかったので、外付けHDDにもバックアップすることにしました。

バックアップ用のバッチファイルを作成しようとネットを調べていたら、Windowsにはrobocopyという便利なコマンドがあると知りました。

使ってみたら便利だったので、自分が使うかもしれないコマンドオプションだけメモとして残しておくことにしました。

 

<使っている>
/MIR      ミラーリング
/R:3      ファイルコピーに失敗した場合の再試行回数(この例だと3回)
/W:3      再試行する時の待ち時間(この例だと3秒待つ)
/LOG:     ログの出力先となるファイルを指定(上書き)
/NDL      ディレクトリ名をログに記録しない
/NP       バックアップの進行状況を表示しない

<たまに使うかも>
/L        バックアップのシミュレーションを行う
/XD       指定されたディレクトリを除外する
/XF       指定されたファイルを除外する(/R:0を使って飛ばしてもOK)

<ひょっとすると使うかも>
/TEE      ログをファイルだけでなくコンソール画面にも出力する
/LOG+:    ログの出力先となるファイルを指定(追記)
/UNILOG:  UNICODEでログを出力(上書き)  → 試しに使ったら、UTF-16LEになった。
/UNILOG+: UNICODEでログを出力(追記)
/NFL      ファイル名をログに記録しない

個人的に「/R:」オプションは必須だと思います。再試行回数のデフォルト値が100万回なので、回数を指定しないと危険です(汗)。

 

自分の使用例(ディレクトリ)

rem Pictures Directory
robocopy C:\Users\name\Pictures\ D:\Backup\Pictures\ /MIR /R:3 /W:3 /NP /NDL /LOG:D:\Backup\robocopy_Pictures.log

 

バッチファイルの拡張子 

ネットによると、バッチファイルの拡張子は「.cmd」で良いみたいです(たとえば「roboco.cmd」など)。めちゃくちゃ古い記憶ではMS-DOSのバッチファイルの拡張子は「.bat」だったような気がするのですが、きっと気のせいでしょう。

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

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」回だけループすることが分かる。
    // チャートの数だけループするわけではない点に注意。
}