barus's diary

とても真面目なblogですにゃ

【BitFlyer】C#でビットコインを自動売買する。その③(ビットコインの時系列データをCSVに加工しMT4に読み込ませる)

 ビットコイン

 

 投資するには、買う材料(指標)が必要になってくる・・。証券会社が提供しているツールで、MetaTrader 4(以下MT4)以外はハッキリいって貧弱である。MT4の利点は・・色々あると思うが、一つには多くの証券会社が採用している点にある。そのため、一度EA(エキスパートアドバイザー)自動売買プログラムを作成してしまえば、証券会社の垣根を越えてEAを使い回し出来る点がいい。仮に、EAを作成出来なくても、トップレベルの使いやすさにあると思う。むろん、それぞれの証券会社で提供しているオリジナルツールにも利点があるかもしれないが、自由度の高さでMT4に勝るものはないと思う。また、多くのFX向けの解説書・雑誌では、このMT4を利用していることを前提に説明していることが多い。

 

と・・、力説するまでもないですかね。( ゚Д゚)?

 

と、いうわけで。MT4。これを活用しない手はない。そこで、今回は時系列データをCSVに加工しMT4に読み込ませる方法を紹介する。

 

 

前回の続き。「BitFlyer」のAPIから時系列データを取得しようとしたが、「BitFlyer」では時系列データを取得するAPIを公開しておらず(2018年1月現在)、仕方なく Public Market REST API - Cryptowatchのサイトよりビットコインの時系列データを取得した。

 

取得したデータ

{"result":{"3600":[[1514991600,1735702,1748659,1731190,1733627,423.71158,736988400],[1514995200,1733884,1744000,1733416,1742072,250.66791,435888420],

(省略)

[1515283200,1987000,1997999,1986687,1991979,186.93735,372412800],[1515286800,1991974,1992000,1975000,1988000,236.68404,469937250]]},"allowance":{"cost":3274104,"remaining":7931517348}}

 

取得したデータは、左から

[ CloseTime, OpenPrice, HighPrice, LowPrice, ClosePrice, ???, Volume ]

時間、開始値、最高値、最安値、終了値、???、出来高

となっていると思われる。(???の列は、よくわからない。)

 

今回は、MT4に読み込ませて、表示させる方法を紹介しようと思う。MT4にインポートさせるためには、取得したデータをそのままでは使えないので、MT4用に加工する必要がある。なので、取得したデータを以下のように加工した。

f:id:hatakeka:20180107120429g:plain

前回と変わらない!。と思う方は注意深く見て欲しい。 前回は単純に取得したのを表示していたが、今回は1行目のUNIX TIMESTAMPを日付に変換し、開始値、最高値、最安値、終了値、出来高という順にカンマ区切りにしてある。(CSV形式)

 

直ぐに試してみたい方は、Vectorの以下からダウンロードできる。

www.vector.co.jp

 

 

 

加工したCSVファイルを、MT4にインポートし、ビットコインの1時間足を表示させてみた結果が以下のような感じとなった。

f:id:hatakeka:20180107100259p:plain

 

1日足を取得すると以下のになった。もの凄く値上がりしているのが分かる。

f:id:hatakeka:20180107130750p:plain

 

 

これにより、MT4の優秀なIndicatorを使用することが出来るようになる。(/・ω・)/

 

 

f:id:hatakeka:20180107210132p:plain

上記ビットコインの1H足を、標準的な指標MACDボリンジャーバンド一目均衡表で表示したもの。

 

 

 

 

bitFlyer ビットコインを始めるなら安心・安全な取引所で

 ↑BitFlyerでアカウントを作成する場合こちらよりどうぞ。

 

 

MT4スプレッド2

  ↑Metrader 4(MT4)が使えるOanda証券会社。

実際登録してみないと分からないことだが、時系列データを数年前から取得できる。

他の証券会社だと時系列データ取得の制限があったりする。まあ、無料だし登録していても損はない。

 

 

MT4に読み込ませるCSV作成

前回の画面は以下のように作りました。

f:id:hatakeka:20180105204353p:plain

これに、保存ボタンを追加した。

f:id:hatakeka:20180107105827p:plain

 

今回は、以下のように

Convets.cs 修正

Form1.cs 修正

FileClass.cs 追加

とする。他のファイルは同じ。

f:id:hatakeka:20180107110301p:plain

 

大きな変更点は、textbox1に値を返すときに、MT4用にconvertCSV_MT4()関数を用意しただけである。

textBox1.Text = conver.convertCSV_MT4(tes.testcall("https://api.cryptowat.ch", url).ToString()); 

 

Convets.cs ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace C_BitCoin
{
    class ConvertsEctx
    {
        private static readonly DateTime UNIX_EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

        //指定時間をUnixTimeに変換する.
        public long ToUnixTime(DateTime dt)
        {
            double nowTicks = (dt.ToUniversalTime() - UNIX_EPOCH).TotalSeconds;
            return (long)nowTicks;
        }

        // UnixTimeからDateTimeに変換.
        public DateTime ToDataTime(long unixTime)
        {
            return UNIX_EPOCH.AddSeconds(unixTime).ToLocalTime();

        }

        //現在の時間をUnixTimeに変換する
        public long Now()
        {
            return (ToUnixTime(DateTime.UtcNow));
        }

        //プレーンファイルをMT4用に加工する
        public string convertCSV_MT4(string buff)
        {
            string rlt = "";

            int p1 = buff.IndexOf(":[[");
            int p2 = buff.IndexOf("]},", p1 + 3);

            buff = buff.Substring(p1, p2 - p1 + 3);

            string[] lines = buff.Split(']');

            if (lines.Length == 0) return rlt;

            rlt += subline(lines[0].Substring(3));
            rlt += "\r\n";

            for (int i = 1; lines.Length > i; i++)
            {
                if (lines[i].Length < 3) break;
                Console.WriteLine("i=" + i + "   " + lines[i]);
                rlt += subline(lines[i].Substring(2));
                rlt += "\r\n";
            }

            return rlt;
        }

        //戦闘のUNIXタイムをローカル時間に変換
        private string subline(string lines)
        {
            string rlt = "";

            //[ CloseTime, OpenPrice, HighPrice, LowPrice, ClosePrice, ???, Volume ]
            int p1 = lines.IndexOf(",");
            int p2 = lines.Length;
            if (p1 == -1 || p2 < p1) return rlt;
            // CloseTime 時間
            rlt = ToDataTime(Convert.ToInt64(lines.Substring(0, p1))).ToString();
            //2018/01/07 09:48 を 2018.01.07 09:48 に変換する
            rlt = toMT4DataTime(rlt);
            rlt += ",";
            //???部分はいらないので省く
            string[] buf = lines.Substring(p1 + 1, p2 - p1 - 1).Split(',');
            rlt += buf[0];  //OpenPrice
            rlt += ",";
            rlt += buf[1];  //HighPrice
            rlt += ",";
            rlt += buf[2];  //LowPrice
            rlt += ",";
            rlt += buf[3];  //ClosePrice
            rlt += ",";
            rlt += buf[5];  //Volume
            return rlt;
        }

        //2018/01/07 09:48 を 2018.01.07 09:48 に変換する
        private string toMT4DataTime(string buf)
        {
            string rlt = "";
            rlt = buf.Replace('/', '.');

            return rlt;
        }

    }
}

Form1.cs ソースコード

 

private void button1_Click(object sender, EventArgs e)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;



namespace C_BitCoin
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Submit tes = new Submit();
            ConvertsEctx conver = new ConvertsEctx();

            string url = getUrlPath_cryptowat();
            if (url.Equals("err")) return;

            textBox1.Text = conver.convertCSV_MT4(tes.testcall("https://api.cryptowat.ch", url).ToString());


        }

        private string getUrlPath_cryptowat()
        {
            ErrorClass err = new ErrorClass();
            ConvertsEctx conver = new ConvertsEctx();
            string url_base = "/markets/bitflyer/btcjpy/ohlc?periods=";

            if (comboBox1.Text.Length == 0) { err.errmes("時間間隔を入力してください。"); return "err"; }
            if (textBox2.Text.Length == 0) { err.errmes("日付を入力してください。"); return "err"; }

            url_base += getPeriods(comboBox1.Text);

            url_base += "&after=";

            url_base += conver.ToUnixTime(DateTime.Parse(textBox2.Text)).ToString();

            return url_base;
        }

        //-------------------------------------
        //コンボボックスの時間を秒数に変換
        //-------------------------------------
        private string getPeriods(string txt)
        {
            string rlt = "";
            if (txt.Equals("1min")) rlt = "60";
            if (txt.Equals("5min")) rlt = "300";
            else if (txt.Equals("15min")) rlt = "900";
            else if (txt.Equals("30min")) rlt = "1800";
            else if (txt.Equals("1H")) rlt = "3600";
            else if (txt.Equals("4H")) rlt = "14400";
            else if (txt.Equals("1D")) rlt = "86400";
            else if (txt.Equals("1W")) rlt = "604800";

            return rlt;
        }

        //-------------------------------------
        //カレンダー表示
        //-------------------------------------
        private void button2_Click(object sender, EventArgs e)
        {
            monthCalendar1.Visible = (monthCalendar1.Visible == true) ? false : true;
        }

        //-------------------------------------
        //カレンダーの日付をtextbox2にセットする
        //-------------------------------------
        private void monthCalendar1_DateChanged(object sender, DateRangeEventArgs e)
        {
            string str = monthCalendar1.SelectionRange.End.ToString();
            string[] str2 = str.Split(' ');
            textBox2.Text = str2[0];
        }

        private void button3_Click(object sender, EventArgs e)
        {
            FileClass fc = new FileClass();
            // カレントディレクトリの取得
            string stCurrentDir = System.Environment.CurrentDirectory;

            string filepath = fc.DialogBox_FileSave(stCurrentDir);

            if(filepath.Length > 0)
            {
                fc.write(filepath, textBox1.Text, false);//上書き保存
            }

        }
    }


}

FileClass.cs ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Windows.Forms; //SaveFile

namespace C_BitCoin
{
    class FileClass
    {


        public string DialogBox_FileSave(string path)
        {

            //SaveFileDialogクラスのインスタンスを作成
            SaveFileDialog sfd = new SaveFileDialog();

            //はじめのファイル名を指定する
            //はじめに「ファイル名」で表示される文字列を指定する
            sfd.FileName = "新しいファイル.csv";
            //はじめに表示されるフォルダを指定する
            sfd.InitialDirectory = @path;
            //[ファイルの種類]に表示される選択肢を指定する
            //指定しない(空の文字列)の時は、現在のディレクトリが表示される
            sfd.Filter = "HTMLファイル(*.html;*.htm)|*.html;*.htm|すべてのファイル(*.*)|*.*";
            //[ファイルの種類]ではじめに選択されるものを指定する
            //2番目の「すべてのファイル」が選択されているようにする
            sfd.FilterIndex = 2;
            //タイトルを設定する
            sfd.Title = "保存先のファイルを選択してください";
            //ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
            sfd.RestoreDirectory = true;
            //既に存在するファイル名を指定したとき警告する
            //デフォルトでTrueなので指定する必要はない
            sfd.OverwritePrompt = true;
            //存在しないパスが指定されたとき警告を表示する
            //デフォルトでTrueなので指定する必要はない
            sfd.CheckPathExists = true;

            //ダイアログを表示する
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                //OKボタンがクリックされたとき、選択されたファイル名を表示する
                //Console.WriteLine(sfd.FileName);
                return sfd.FileName;
            }

            return "";
        }


        //追記 type=true, 上書き=false
        public void write(string filepath, string buff, bool savetype)
        {
            // StreamWriter でファイルを初期化
            System.IO.StreamWriter sw = new System.IO.StreamWriter(@filepath, savetype, System.Text.Encoding.GetEncoding("shift_jis"));

            // hogehoge.txtに書き込まれている行末に、追加で書き出す
            sw.Write(buff);

            // 閉じる (オブジェクトの破棄)
            sw.Close();
        }

    }
}

 

 

 

 

以上、

使い方は、取得したい「日付」ボタンを押して日付を入力し、「間隔」の時系列データの足を決定後、「時系列取得」ボタンを押す。

f:id:hatakeka:20180107120429g:plain

 

次にMT4の設定をする。

 

 

 

MT4の設定

 MT4をオフラインにする。サーバーは使わないところを選択し(デモサーバが妥当)、ログインIDとパスワードを適当に入力しておく。

f:id:hatakeka:20180107114530p:plain

 

historyフォルダにある。「*.hst」ファイルを削除する。

Windows10の場合、Windows7や8と違ってhistoryフォルダは

以下のような感じのフォルダにある。

 

C:\Users\「ユーザー名」\AppData\Roaming\MetaQuotes\Terminal\「数字と英語のランダム文字列」\history

 

フォルダの場所が分かりにくいので、以下の方法で見つけるとよい。

メニュウのメタエディターをクリックする。

f:id:hatakeka:20180107085858p:plain

メタエディターのFileメニュウ>Openを開く。

f:id:hatakeka:20180107142810p:plain

 

コモンダイアログが開くのでアドレスバーをコピーする。

別に何もファイルは開かず、コモンダイアログはキャンセルして閉じる。

f:id:hatakeka:20180107090350p:plain

 

こうして得た、MT4のファイルパスが以下のような感じで取得できる。

C:\Users\su5fi\AppData\Roaming\MetaQuotes\Terminal\A4VVV4V0GA7BGHRR43A3987F94G890NHK\history

 

ワシの場合、4つの証券会社にアカウントを作成している。アカウント作成も、維持費も無料なので、複数持っていて損はないかと思う。使っていない証券会社をオフラインにするとよい。

f:id:hatakeka:20180107143120p:plain

 

historyフォルダにある。サーバーのデモフォルダの「*.hst」ファイルを削除する。なるべくLIVEの本番フォルダではなく、デモフォルダがよいかと思う。一応、ファイルを削除する前にフォルダ毎圧縮してバックアップ取っておくことが望ましいだろう。

 f:id:hatakeka:20180107142405p:plain

 

MT4を一旦閉じる。(^_-)-☆

 

MT4のメニュウ>ツール>ヒストリーセンターをクリック。

f:id:hatakeka:20180107095303p:plain

 

C#で作成した間隔が1HでCSVに加工した場合は、USDJPY-cdの1時間足を選択し、ヒストリーセンターのインポートをクリック。

f:id:hatakeka:20180107095416p:plain

同様に、1D(1日)でCSVを作成した場合は、日足のところをクリックしてインポートする。

 

 

 

CSV加工したファイルを読み込ませる。

f:id:hatakeka:20180107095719p:plain

 

気配値表示の画面の「USDJPY-cd」上で右クリックしチャート表示を押す。

f:id:hatakeka:20180107095923p:plain

 

USDJPY-cdタブにビットコインの1Hが表示される。

f:id:hatakeka:20180107100259p:plain

 

ビットコインの日足データは以下となる。

f:id:hatakeka:20180107130750p:plain

 

時系列データの間隔(足)切り替えは以下の赤い部分で囲ったボタンで、切り替えることが出来る。むろん、表示できる間隔(足)はインポートしたデータのみとなる。

f:id:hatakeka:20180107205213p:plain

 

 

以上。