Jump to content


プロフィール写真

指標フィルタの仕組みを作る

指標 フィルタ

  • 返信にはログインが必要です
23 件の返信があります

#1 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月18日 18時44分

ちょうど指標を回避する方法が話題に上がりましたので、このスレッドでは、何回かの投稿に分けて、MT4 で使える指標フィルタの仕組みを実際に作ってみたいと思います。

 

ちょっとしたウェブスクレイピングの知識も必要になるため、スクレイピングの経験の無い人にはちょうどよい題材かもしれません。

 

大まかに次のような流れで説明していくことになると思います:

  1. 利用する指標データソースを選んで分析
  2. VPS で動作するデータ変換プログラム/スクリプトを開発
  3. データ変換プログラム/スクリプトを実行して、指標データソースから CSV ファイルを生成
  4. MQL で利用できる CSV ファイルの取得と読み取りルーチンと指標フィルタを作成

なお、現時点では、Forex Factory の XMLDailyFX の HTML に対するデータ変換プログラム/スクリプトをそれぞれ作ることを予定しています。

 

また、4. の部分を MQL ではなく他の言語/処理系で実装すれば、MT4 以外のプラットフォームで使える指標フィルタにもなります。

 

どなたでも本スレッドに割り込んでもらって構いませんので、ご意見/ご感想/ご質問は、気軽に返信として投稿してください。

 

このスレッドで作成したスクリプトを用いて、経済指標データを日曜日の夕方に自動生成しています。

 

TS 登録ユーザ限定で公開しておりますので、利用方法についてはメンバーズフォーラムの記事をご覧ください。

 

このようにして生成された CSV ファイルをダウンロードして指標フィルタとして使える MT4 ライブラリはこちらからダウンロードできます。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#2 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月18日 23時55分

最初に、この指標フィルタの大まかな仕様を決めておきたいと思います。

 

まず、想定される使い途ですが、

  • MT4 の EA で指標回避(ライブおよびバックテスト)
  • 弊社の Forex Studio や Agile Trader のようなソフトが指標の情報をユーザに表示
  • 弊社の Arb Trader のようなソフトが、現在、指標直後何分以内であるかを逆算

 

これらのユースケースで使いやすいように、指標情報には

  • 指標の基本情報(日時重要度影響する通貨
  • 指標の名称(ユーザ表示用、日本語が望ましい)
  • 指標の予想値前回値実測値(実測値はバックテスト用データでのみ有効な値を持つ)

といったフィールドを含めるようにします。

 

 

さて、簡単のため (DB は使わず) これらの情報は CSV ファイルとして、次のようなフォーマットで保存します:

2013-01-13 00:00:00,JP,JPY,0,架空の日本の祝日,,,
2013-01-13 12:30:00,US,USD,5,架空の重要指標,5.0%,4.5%,5.2%
2013-01-14 03:00:00,DE,EUR,3,架空の指標,350mil,400mil,340mil

フィールドは全部で七つ、それぞれ次のように定義します:

  1. (必須) 日本標準時で表した指標の日時。yyyy-MM-dd HH:mm:ss という形式。ただし、終日または日時が不明なイベントは 99:99:99 とする。
  2. (必須) イベントに最も関連が深い国コード文字列。
  3. (必須) 最も影響しそうな通貨コード文字列。
  4. (必須) 重要度。0 なら祝日のような終日イベント、それ以外は 1 から 5 までの五段階で表した整数値。
  5. (必須) 指標やイベントのタイトル文字列。
  6. (オプション)指標の予想値。単位などを統一するのが困難なので、ユーザ表示を目的とした単なる文字列フィールドとする。
  7. (オプション)指標の前回値。同上。
  8. (オプション)指標の実測値。同上。バックテスト用データでのみ有効な値が入っていることがある。

 

その他、次のルールに従うものとします:

  • オプションフィールドの内容は空文字列でも構いませんが、CSV ファイルですから、フィールド自体は存在しなければいけません。
  • CSV の各行は、かならず日時の昇順に整列しているものとします。ただし、終日または日時が不明なイベントは 0 時の位置に置かれます。
  • 同じ日時のイベントが複数ある場合、同じ通貨に関連するイベントは連続していて、優先度の昇順に並んでいなければいけません。
  • 国コードについては、たとえばドイツの指標なら "DE" となりますが、ECB 関連の指標や発言などユーロ圏全体に影響するものには、架空の "EU" という国コードを設定することにします。

 

CSV ファイルのエンコーディングについては、後ほど、MT4 に読み込む部分を作るときに決めます。


編集: ソフトゲート, 2015年2月22日 02時25分.
国コードのフィールドを追加、フィールド順序を変更、日時は日本時間に変更、終日または不明な日時は99:99:99に変更

  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#3 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月19日 20時23分

注意: CSV のフィールド順序と日時を日本標準時に変更したので、このスクリプトでは上記の CSV フォーマットで出力できません

 

それでは、Forex Factory の XML を取得して、上で定義した CSV フォーマットで出力する簡単なスクリプトを Python で作成してみます。
 
このスクリプトの特徴/注意点としては

  • 重要度が 'Holiday' になっているイベントは時刻を 00:00 にする
  • 重要度が 'Low', 'Medium', 'High' の三段階なので、それぞれ 1, 3, 5 という五段階に変換する
  • 元のデータが綺麗な XML なので ElementTree というモジュールを使って問題なくパース可能

などがあります。
 
動作確認は Python 3.3 で行いましたが、一応 Python 2.x (多分 2.6 以上)でも 3.x でも動作するように作ったつもりです。
 

# Python 2.x, 3.x の違いを吸収 (print と urllib2)

from __future__ import print_function

import urllib
try:
    import urllib.request as urllib2
except:
    import urllib2

# 一応 User-Agent は Chrome にして、XML を取得

user_agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.63 Safari/537.36'
headers = {'User-Agent': user_agent}

request = urllib2.Request('http://www.forexfactory.com/ffcal_week_this.xml', None, headers)
response = urllib2.urlopen(request)
xml_content = response.read()

# XMl 要素からテキストを取り出すヘルパー関数
def get_text(ev, name):
    t = ev.find(name).text
    return t if t else ''

impact_table = {'Holiday': 0, 'Low': 1, 'Medium': 3, 'High': 5}

# XML をパースしながら CSV 形式で標準出力に書き出し

import xml.etree.ElementTree as etree
from datetime import time, datetime

root = etree.fromstring(xml_content)

for ev in root.findall('event'):

    title, country, d, t = get_text(ev, 'title'), get_text(ev, 'country'), get_text(ev, 'date'), get_text(ev, 'time')
    impact, forecast, previous = get_text(ev, 'impact'), get_text(ev, 'forecast'), get_text(ev, 'previous')

    dt = datetime.strptime('%s %s' % (t, d), '%I:%M%p %m-%d-%Y')

    # 祝日は時刻部分を 00:00:00 にする (time())
    if impact == 'Holiday':
        dt = datetime.combine(dt.date(), time())

    print('{0:%Y-%m-%d %H:%M:%S},{1},,{2},{3},{4},{5},'.format(dt, impact_table[impact], country, title, forecast, previous))

ファイルダウンロードはこちら:  添付ファイル  ForexFactoryCalendarToCsv.py   1.54KB   11 ダウンロード

 

ちなみに、FF のデータは指標名が英語であるため、この変換スクリプトは最終的には利用しない予定ですが、もしも、本スクリプトを指標データの自動更新に利用するなら、データがちゃんと取得できたかどうかの確認/再試行などの処理も追加する必要があるでしょう。

 

※本プロジェクトでは、指標名が日本語で取得できる DailyFX からの変換スクリプトの方を使って、指標フィルタの仕組みを自動化することになると思います。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#4 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月20日 22時15分

さて、次に DailyFX の経済指標カレンダーを CSV に変換するスクリプトの作成に移りますが、今回はいきなりスクリプトを書いて終わりにするのではなく、ウェブサイト (HTML) を調べるところから着手したいと思います。
 
まず、上記のページをブラウザで開くと次のような内容が表示されます。
 

2014-04-20_DailyFxTop.png

 
経済指標カレンダーの本体のすぐ上に 「データ更新」 というボタンがありますから、HTML ソースを表示して、この文字列を検索すると、経済指標カレンダーの部分は次のような HTML になっています。
 

<div class="contentsHeader">
  <ul>
    <li><a href="calendar-backnumber.html">過去分</a></li>
    <li style="float:right; background:none;">
      <button onclick="window.frames.iframe1.location.href = '/ec_in/2014-16.html'">データ更新</button>
    </li>
  </ul>
</div>
<!-- /.contentsHeader -->
<iframe  name="iframe1" id="iframe1" src="/ec_in/2014-17.html" scrolling="yes" style="overflow-x: hidden; height:2800px;" class="calendar_detail"></iframe>
<!-- /box_center -->

 
つまり、現在 (来週というか今週というか) の指標カレンダーの URL は

http://www.dailyfx.co.jp/ec_in/2014-17.html

であることが分かります。
 
URL の "2014" は年を表すのは明らかだとしても、"17" の意味がよく分かりませんので、引き続き HTML を調べましょう。
 
上の HTML ソースをもう一度見てみると、「過去分」というリンクがあるので、その URL である

http://www.dailyfx.co.jp/market/calendar-backnumber.html

のソースも確認すると、中盤以降に次のような箇所がありました: 

<div style="clear:both"></div>
<p class="blogttl">経済指標</p>
<h1>経済指標バックナンバー</h1>
<form method="POST" action="calendar-detail.html">
<select name="kt_year">
<option value="2013-01">2012年12月31日の週</option>
<option value="2013-02">2013年1月7日の週</option>
<option value="2013-03">2013年1月14日の週</option>
<option value="2013-04">2013年1月21日の週</option>
<option value="2013-05">2013年1月28日の週</option>
<option value="2013-06">2013年2月4日の週</option>
<option value="2013-07">2013年2月11日の週</option>
<option value="2013-08">2013年2月18日の週</option>
<option value="2013-09">2013年2月25日の週</option>
<option value="2013-10">2013年3月4日の週</option>
<option value="2013-11">2013年3月11日の週</option>
<option value="2013-12">2013年3月18日の週</option>
<option value="2013-13">2013年3月25日の週</option>

ここから想像するに、さきほどの URL "2014-17.html" は、2014 年の第 17 週という意味だと考えてほぼ間違いないでしょう。

現在が年の第何週であるかをどのように計算するかはひとまず置いておき、これで今週 (というか来週というか) の経済指標データを入手する URL が分かりました。

次の投稿では、それをパースして CSV に変換するスクリプトを書くために、HTML の構造を詳しく調べたいと思います。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#5 TeamJunTradeez

TeamJunTradeez

    エキスパート

  • カスタマー
  • PipPipPipPipPip
  • 258 件の投稿
  • 居住地Hokkaido Japan

投稿: 2014年4月21日 23時56分

ソフトゲートさん、お疲れ様です。

フムフム・・・勉強させてもらってます。〆(._.)メモメモ

次はHTML の構造についてですね。

楽しみにしています!


  • ソフトゲートさんがいいね!と言っています

#6 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月22日 14時51分

それでは、引き続き、DailyFX の指標カレンダー HTML を調べてみたいと思います。
 
まず、2014 年 4 月 22 日現在、今週のカレンダーの URL は http://www.dailyfx.c...in/2014-17.html ですので、これをブラウザで開きます。
 

2014-04-22_DailyFxCalendar.png

 
ソースを確認するまでもなく、各イベント/指標がテーブルの一つのになっていると思われるので、基本的には行毎に変換処理をしていけば良いような気がします。
 
とはいっても、一つ面倒くさそうな点がありまして、日付の部分が複数行にまたがっていることから、ただ単純に行毎の処理を記述するだけでは済まないかもしれません。
 
 
パースの方法
 
ところで、先日の投稿で取り上げた Forex Factory の指標データは、綺麗に構造化された XML でしたので、XML パーサを使って簡単に変換できました。
 
HTML の場合も、似たようなアプローチで、たとえば .NET を使うなら Html Agility Pack のようなライブラリでパースし、XPath で要素を指定して情報にアクセスすることもできます。また、Python などのスクリプト言語であれば、スクレイピング用のパッケージもいろいろ存在すると思います。
 
ただ、HTML の構造によっては、汎用のライブラリで要素をうまく取り出せないケースもありうるため、ここでは正規表現でマッチさせる素朴な方法を採ることにします。
 
 
HTML の構造
 
さて、ぼちぼち上記の指標ページの HTML ソースを実際に覗いてみましょう。
 

2014-04-22_Table.png

 
予想通り、基本的には一つの指標/イベントのデータがテーブルの同一行にまとまっている (図の赤い部分) ので、これらを順番に正規表現でマッチさせて取り出していけば良いように思います。ただ、やっぱり日付の取得方法は少し工夫が必要です。


しかし、ソースを見ていただくと分かりますが、<tr> 開始タグがないのに、何度も </tr> 終了タグが出現していて、HTML の構造としてはかなりデタラメですね。

 

各行から取り出したい情報をもう一度整理すると次のようになります:

  • イベントの時刻
  • 国 (通貨) 名
  • 重要度
  • 予想値
  • 前回値
  • 結果値

 

イベントの時刻を取得するには

<td\s+class="event_time">(\d+):(\d+)</td>

こんな正規表現で良さそうですが、祝日のような終日イベントでは td タグの中身が空になるため、0 回または 1 回の存在にマッチするように、全体を ? で修飾して

<td\s+class="event_time">((\d+):(\d+))?</td>

としておきます (もちろん、パターンの書き方/対処方法はいろいろ考えられるので、これはあくまで一例ですが)

 

eventname というセルには、国あるいは通貨の情報、それからイベント/指標の名称が日本語で記述されています。まず、国旗アイコンのファイル名から数字を取得し、06 なら GBP、07 なら EUR といった具合で変換してやる必要があります。指標の名称は、(国旗の img タグを除いて) td タグの中身を丸々マッチさせればオーケーです。

 

パターンの書き方としては、たとえばこんな感じでしょうか:

<td\s+class="eventname"><img\s+src=".+?/flag_(\d+).png">(.*?)</td>

注意していただきたいのは、指標名称にマッチさせるグループを(.*?) という風に、? を付けることでノングリーディーなマッチをさせている点です。このようにしないと、パターン.* が複数のテーブル行にまたがって、果てしなく (は言い過ぎですが) マッチしてしまいます。

 

残りのフィールドにマッチする表現は、重要度の星印画像の URL がダブルクォートではなくシングルクォートで囲まれているなど、微妙な引っかけはあるものの、同じようなアプローチで簡単に作ることができます。

 

決してエレガントな正規表現ではないですが、正規表現の出来映えはこの際重要ではないので、こんな感じで十分でしょう:

<td\s+class="event_time">((\d+):(\d+))?</td>\s+<td\s+class="eventname"><img\s+src=".+?/flag_(\d+).png">(.*?)</td>\s+<td\s+class="rank">(<img\s+src=['"].+?/star_(\d+).png['"]>)?</td>\s+<td\s+class="event_last_data"\s+nowrap="nowrap">(.*?)</td>\s+<td\s+class="event_exp_data"\s+nowrap="nowrap">(.*?)</td>\s+<td\s+class="event_result_data"\s+nowrap="nowrap">(.*?)</td>

なお、こういう正規表現を作るときは、正規表現を書いたらすぐにそれが対象の文章にマッチするかどうか、マッチするならどのようにグループにマッチできているかを確認するソフトウェアがあると便利です。

 

.NET の正規表現ならばフリーウェアの Expresso なんかがいいかもしれません。ちなみに私自身は、.NET 以外に Python など他の言語の正規表現エンジンにも対応した Regex Buddy を使っています。こちらはシェアウェアです。

 

2014-04-22_Regex.png

 

これでひとまず各イベント/指標の情報を抽出するための正規表現は完成しました。

 

次の投稿では、日付を取得する方法を考えつつ、これをスクリプトに組み込んでみたいと思います。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#7 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月24日 01時37分

DailyFX の指標カレンダーを変換するプログラム/スクリプトですが、なるべく安価な Linux VPS で定期的に実行したいので、Python で書いてみることにします。

 

まず、簡単なところで、ある日本時間表記の日時が年の何週目に該当するかを計算する関数を作りましょう。

 

Python の datetime パッケージに含まれる date や datetime にはisocalendar() というメソッドがあり、これが返す tuple の二番目の要素が年初からの何週目にあたるかを表しています。

 

ただし、土日に変換スクリプトを実行した場合は、週明けの月曜日からの指標カレンダーにアクセスしないといけません。

 

これと、isocalendar() メソッドでは月曜日が週の初めとなることを考慮すると、指定された日時が土曜日の朝 7 時以降または日曜日ならば、値を 1 増やして返すようにします。

 

# 日本時間表記の日時が年初から何週目かを計算する関数
# ただし土曜日の朝7時以降と日曜日は翌週の値が欲しいので+1
def get_week_of_year(dt):
    woy = dt.isocalendar()[1]
    if (dt.isoweekday() == 6 and dt.hour >= 7) or dt.isoweekday() == 7:
        woy += 1
    return woy

次に、これを利用して、取得するべき指標カレンダー HTML の URL を生成する関数も作ります。

def compose_calendar_url(dt):
    woy = get_week_of_year(dt)
    return 'http://www.dailyfx.co.jp/ec_in/{0:%Y}-{1:0>2}.html'.format(dt, woy)
さて、上記の二つの関数と urllib パッケージを組み合わせれば、適切な指標カレンダーの HTML データはダウンロードできそうです。
 
 
変換処理の構造
 
肝心の 「CSV に変換する処理をどのように記述するか」 という点に話を進めましょう。
 
先日の投稿で述べたとおり、日付が複数のテーブル行にまたがっている関係上、HTML から特定の日付に対応するコンテンツを切り出して
  • HTML から特定の日付に対応するコンテンツを切り出して処理する外側のループ
  • 切り出されたコンテンツを全体とみなして、テーブル行ごとに処理をしていく内側のループ
という二段階のループ構造にするのが良さそうです。

 

もう一度、日付が記述されている箇所の HTML を確認すると

  <tbody>
    <tr>
      <td
       rowspan="13"
      class="eventdate">
      2014-04-21
      </td>
      <td
       rowspan="13"
      >
      月
      </td>
      <td class="event_time"></td>
      <td class="eventname"><img src="/image/flag_07.png"><font color=red>休場(復活祭)</font></td>

こんな感じになっていますので、eventdate という文字列を区切りとして HTML を分割したら良いかもしれません。

 

文字列を区切りとして分割するメソッドが str クラスあたりに用意されているのかどうかは知りませんが、少なくとも正規表現の re パッケージに含まれる split メソッドを使えば、やりたいことは実現できそうです。

 

分割後の先頭の要素にはヘッダ的な情報しか入っていませんので、そこは捨てることにすると、外側のループは次のようになるでしょうか。

import re

daily_blocks = re.split('eventdate">', content)[1:] # 先頭の要素は捨てる

for daily_block in daily_blocks:
    date_match = re.match('\s+([0-9-]+)\s+', daily_block)
    if date_match != None:
        curr_date = datetime.strptime(date_match.group(1), '%Y-%m-%d').date()
        # その他、daily_block を全体とみなして、ここで指標ごとの変換などを行う

ここでは、文字列を区切るパターンにダブルクォートと閉じ括弧も含めて、eventdate"> としています。

 

次の投稿では、テーブル行ごと、つまり指標ごとの変換処理を追加して、いよいよスクリプトを完成させたいと思います。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#8 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月24日 15時27分

それでは、細かな調整は後で行うことにして、変換スクリプトの初期バージョンを作ってしまいましょう。
 
まず、国旗アイコンのファイル名から国コードや通貨コードに変換する部分です。
 
ここは若干泥臭い作業になりますが、DailyFX の HTML と表示を見比べて、変換テーブルを用意するのが簡単でしょう。
 

# 国旗アイコンのファイル名から国コードへの変換テーブル
country_table = {'01': 'CH', '02': 'ZA', '03': 'CA', '04': 'NZ', '05': 'AU',
                 '06': 'GB', '07': 'EU', '08': 'US', '09': 'JP', '10': 'CN',
                 '11': 'DE', '12': 'KR', '13': 'RU', '14': 'FR', '15': 'IT',
                 '16': 'ES', '24': 'HK'}


# 国旗アイコンのファイル名から通貨コードへの変換テーブル
currency_table = {'01': 'CHF', '02': 'ZAR', '03': 'CAD', '04': 'NZD', '05': 'AUD',
                  '06': 'GBP', '07': 'EUR', '08': 'USD', '09': 'JPY', '10': 'CNY',
                  '11': 'EUR', '12': 'KRW', '13': 'RUB', '14': 'EUR', '15': 'EUR',
                  '16': 'EUR', '24': 'HKD'}

このテーブルさえ出来てしまえば、前回作った構造の骨組みに、正規表現のマッチと CSV 書き出し部分を追加して、基本的な出力は可能になります。
 

pattern = r'''<td\s+class="event_time">((\d+):(\d+))?</td>\s+<td\s+class="eventname"><img\s+src=".+?/flag_(\d+).png">(.*?)</td>\s+<td\s+class="rank">(<img\s+src=['"].+?/star_(\d+).png['"]>)?</td>\s+<td\s+class="event_last_data"\s+nowrap="nowrap">(.*?)</td>\s+<td\s+class="event_exp_data"\s+nowrap="nowrap">(.*?)</td>\s+<td\s+class="event_result_data"\s+nowrap="nowrap">(.*?)</td>'''

regex = re.compile(pattern)

daily_blocks = re.split('eventdate">', content)[1:]

for daily_block in daily_blocks:
    date_match = re.match('\s+([0-9-]+)\s+', daily_block)
    if date_match != None:
        curr_date = datetime.strptime(date_match.group(1), '%Y-%m-%d').date()
        matches = regex.finditer(daily_block)
        for e in matches:
            print('{0} {1}:{2},{3},{4},{5},{6},{7},{8},{9}'.format(
                curr_date, e.group(2), e.group(3),
                e.group(7),
                country_table[e.group(4)],
                currency_table[e.group(4)],
                e.group(5),
                e.group(9), e.group(8), e.group(9)))

完成したスクリプトファイル(バージョン1)はこちらからダウンロードしてください: 添付ファイル  DailyFxCalendarToCsv.py   2.9KB   0 ダウンロード
 
実行してみると、一応それなりの出力は得られますが、まだまだブラッシュアップが必要であることが分かります。

2014-04-21 None:None,None,ZA,ZAR,<font color=red>休場(家族の日)</font>,,,
2014-04-21 08:50,3,JP,JPY,3月貿易収支(通関ベース),-1809億円,-8025億円↓,-1809億円
2014-04-21 08:50,3,JP,JPY,3月貿易収支(通関ベース)<季調済>,-14276億円,-11840億円↓,-14276億円
2014-04-21 21:30,3,US,USD,3月シカゴ連銀全米活動指数,0.20,0.53↑,0.20
2014-04-21 23:00,3,US,USD,3月景気先行指数(前月比),+0.7%,+0.5%,+0.7
2014-04-22 14:00,2,JP,JPY,2月景気先行CI指数<確報値>,-,108.5,-
2014-04-22 14:00,2,JP,JPY,2月景気一致CI指数<確報値>,-,113.4,-
2014-04-22 18:00,2,EU,EUR,2月建設支出(前月比),-,+1.6%↑,-
2014-04-22 18:00,2,EU,EUR,2月建設支出(前年比),-,+8.0%↓,-
2014-04-22 23:00,4,US,USD,4月リッチモンド連銀製造業景況指数,2,-7,2
2014-04-22 23:00,4,US,USD,<A Href=http://www.dailyfx.co.jp/market/data/usd-data11.html>3月中古住宅販売件数</A>,456万件,460万件,456万件
2014-04-22 23:00,4,US,USD,3月中古住宅販売件数(前月比),-0.9%,-0.4%,-0.9
2014-04-22 23:00,3,EU,EUR,4月消費者信頼感指数<速報値>,-9.3,-9.3,-9.3
2014-04-22 23:30,2,CA,CAD,オリバー加財務相講演,,,
2014-04-22 24:45,2,US,USD,バーナンキ前FRB議長講演,,,
2014-04-22 26:00,1,US,USD,米2年債入札,,,

次回以降の投稿で、上記の出力のおかしな点を徐々に解消していきます。

 


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#9 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月24日 21時36分

前回出力した CSV の結果をよく見てみると、いくつか修正するべき点が見つかります。
 

2014-04-21 None:None,None,ZA,ZAR,<font color=red>休場(家族の日)</font>,,,
2014-04-21 08:50,3,JP,JPY,3月貿易収支(通関ベース),-1809億円,-8025億円↓,-1809億円
2014-04-21 08:50,3,JP,JPY,3月貿易収支(通関ベース)<季調済>,-14276億円,-11840億円↓,-14276億円
2014-04-21 21:30,3,US,USD,3月シカゴ連銀全米活動指数,0.20,0.53↑,0.20
2014-04-21 23:00,3,US,USD,3月景気先行指数(前月比),+0.7%,+0.5%,+0.7
2014-04-22 14:00,2,JP,JPY,2月景気先行CI指数<確報値>,-,108.5,-
2014-04-22 14:00,2,JP,JPY,2月景気一致CI指数<確報値>,-,113.4,-
2014-04-22 18:00,2,EU,EUR,2月建設支出(前月比),-,+1.6%↑,-
2014-04-22 18:00,2,EU,EUR,2月建設支出(前年比),-,+8.0%↓,-
2014-04-22 23:00,4,US,USD,4月リッチモンド連銀製造業景況指数,2,-7,2
2014-04-22 23:00,4,US,USD,<A Href=http://www.dailyfx.co.jp/market/data/usd-data11.html>3月中古住宅販売件数</A>,456万件,460万件,456万件
2014-04-22 23:00,4,US,USD,3月中古住宅販売件数(前月比),-0.9%,-0.4%,-0.9
2014-04-22 23:00,3,EU,EUR,4月消費者信頼感指数<速報値>,-9.3,-9.3,-9.3
2014-04-22 23:30,2,CA,CAD,オリバー加財務相講演,,,
2014-04-22 24:45,2,US,USD,バーナンキ前FRB議長講演,,,
2014-04-22 26:00,1,US,USD,米2年債入札,,,

まず明らかに直すべき箇所は

  • 一行目の時刻と重要度がNone:None,None になっているところ
  • 一行目のイベント名に含まれる font タグ
  • イベント名の中の a タグ
  • 時刻が 24 時以上になっているイベントがある

といったところです。
 
また、今作っている指標フィルタの目的は、指標を避けるためのデータを提供することですから、同じ国/通貨に影響する指標で同じ日時に発表されるものは、最も重要度の高いもの一つにまとめてしまうことにします。
 
さらに欲を言えば、指標の種別さえ分かれば良く、それが何月の値の発表なのかはどうでもいいので、たとえば 「3月中古住宅販売件数(前月比)」 という指標ならば、「3月」 や 「(前月比)」 などの情報も削除した方が個人的にはすっきりして好みです。
 
None と 24 時以上の日時の修正
 
最初に None と出力されている箇所から修正していきます。
 
重要度が None の場合は、祝日などの終日イベントであることを意味するので、重要度を 0 にします。

impact = e.group(7) or '0'

こんな感じでimpact という変数に適切な文字列を格納することが出来るでしょう。
 
日時のフィールドも同様に処理できますが、時間が 24 時以上になっていることがあるので、その対処も合わせて行うことにします。
 
・・・が、ここが意外と面倒なところで、ただ日時だけを気軽に修正してしまうと、次のような結果になります:
 

2014-04-24 21:30,4,US,USD,新規失業保険申請件数,+31.5万件,+30.4万件,+31.5万件
2014-04-24 21:30,2,US,USD,失業保険継続受給者数,+274.5万人,+273.9万人,+274.5万人
2014-04-25 01:15,3,EU,EUR,コンスタンシオECB副総裁講演,,,
2014-04-25 02:00,1,US,USD,米7年債入札,,,
2014-04-25 00:00,0,AU,AUD,<font color=red>休場(アンザック・デー)</font>,,,
2014-04-25 00:00,0,NZ,NZD,<font color=red>休場(アンザック・デー)</font>,,,
2014-04-25 00:00,4,US,USD,オバマ米大統領訪日(~25日),,,
2014-04-25 08:30,4,JP,JPY,3月全国CPI(前年比),+1.6%,+1.5%,+1.6

24 日の 25:15 と 26:00 の指標が、25 日の終日イベント(時刻 00:00)よりも先に出力されていますね。
 
そのため、時刻が 24 時以上のイベントは、すぐさま出力せず一旦キューに積んでおき、
 

時刻が 24 時未満で、かつ時間的にもっと未来のイベント X

 
が出現したら、キューから取り出した行をX よりも先に出力する、という一工夫が必要になります。

 

以上を反映して、スクリプトはこんな感じになりました:

queue = []
for daily_block in daily_blocks:
    date_match = re.match('\s+([0-9-]+)\s+', daily_block)
    if date_match != None:
        curr_datetime = datetime.datetime.strptime(date_match.group(1), '%Y-%m-%d')
        matches = regex.finditer(daily_block)
        for e in matches:
            impact = e.group(7) or '0'
            correct_hour = hour = int(e.group(2) or '00')
            minute = int(e.group(3) or '00')
            event_date = curr_datetime
            if hour >= 24:
                # 24時以上なら時間と日付を補正
                correct_hour -= 24
                event_date += datetime.timedelta(days=1)
            line = '{0} {1:0>2}:{2:0>2},{3},{4},{5},{6},{7},{8},{9}'.format(
                event_date.date(), correct_hour, minute,
                impact,
                country_table[e.group(4)],
                currency_table[e.group(4)],
                e.group(5),
                e.group(9), e.group(8), e.group(9))
            if hour < 24:
                # 24時未満ならキューを調べる
                # より過去のイベントがあればそれを先に出力
                earlier_events = [x for x in queue if x < line]
                for earlier_event in earlier_events:
                    print(earlier_event)
                print(line)
                # キューから処理済みのエントリを除去
                queue = [x for x in queue if x >= line]
            else:
                # 24時以上なら出力せずキューに追加
                queue.append(line)


# キューに残っている行があれば出力
for queue_line in queue:
    print(queue_line)

スクリプト全体はこちら: 添付ファイル  DailyFxCalendarToCsv.py   4KB   6 ダウンロード

 

次は、イベント/指標のタイトルに時々含まれている font や a タグを取り除く予定です。

 


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#10 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月25日 12時43分

イベント/指標のタイトルに含まれている HTML タグの除去については、font と a タグだけだと思われるので、簡単な正規表現で処理することにします。

 

同時に、レイアウト調節のつもりでしょうが、連続する全角スペースが大量に含まれていることがありますので、それも空白一つに置き換えましょう。

 

また、ついでに、タイトルの中の括弧やカギ括弧の部分を取り除くフィルタも実装してみます。フィルタを適用するかどうかはスクリプトのオプションで指定可能です。

def simplify_text(text):
    text = re.sub(r'</?[a-zA-Z]+\s*[^>]*?>', '', text)
    if not args.verbose:
        text = re.sub('<[^>]*?>', '', text)
        text = re.sub('<[^>]*?>', '', text)
        text = re.sub('\([^>]*?\)', '', text)
        text = re.sub('([^)]*?)', '', text)
    # 元データは全角スペースでインデントされていることがあるので、
    # 連続する空白(全角も)をひとまとめにする
    text = re.sub(r'\s+', ' ', text)
    return text

 

最後に、同じ日時で同じ国(通貨)に関連するイベントは、重要度が一番高いものを代表として出力し、他はフィルタアウトする機能を付けます。

 

※なお、この処理が結構面倒くさいので、CSV ファイルのフォーマットを少し変更して、日時フィールドの次に国と通貨コードのフィールド、その後に重要度フィールドが来るようにしました。

 

フィルタ処理を実装するにあたって、他の変換処理をすべて終えた出力行を print で出力する代わりに、出力キューに追加することにします。

 

ちなみに、どうせ出力キューを用意するのであれば、前回の投稿で説明したような (日時が24時以上のイベントを一時キューに待避するような) 処理は不要になります。

 

出力キューを昇順にソートすれば、自然と日時の昇順になりますからね。

 

フィルタ処理のアルゴリズムを口で説明するのは大変なので、フィルタしながら出力する部分のスクリプトを示します。

output.sort()


if not args.duplicate:
    # 重複イベントは重要度が高いものにまとめて出力
    if len(output) > 1:
        i = 0
        while i < len(output) - 1:
            curr = output[i].split(',')
            j = i + 1
            while j < len(output):
                next = output[j].split(',')
                if curr[:3] == next[:3]:
                    j += 1
                else:
                    print(output[j - 1])
                    i = j
                    break


        print(output[-1])
else:
    for output_line in output:
        print(output_line)

現時点のスクリプトの全体像はこんな感じです: 添付ファイル  DailyFxCalendarToCsv.py   4.94KB   13 ダウンロード

 

次の投稿では、スクリプトが生成した CSV を読み込む MT4 側のライブラリに着手したいと思います。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#11 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年4月28日 18時16分

指標 CSV ファイルを HTTP でダウンロードして指標フィルタの処理を行うような MT4 ライブラリは、MQL でも記述できるとは思うのですが、そういうプログラム例はネットにいろいろ転がっている気がするので、ここでは C++ でネイティブ DLL を作ってみます。

 

まず、Visual C++ を使って、プロジェクトの骨組みを用意することにします。

 

※なお、以下の例は Visual Studio 2013 (Visual C++ 12) を使いましたが、Visual C++ 10 以降ならほとんど同じ手順でプロジェクトを作成することが出来るはずです。

 

作成するプロジェクトの種類は Win32 プロジェクト を選択してください。

 

2014-04-28_Win32Project.png

 

DLL を選択した上で、なるべく余計なファイルや設定を追加されたくないので、空のプロジェクトにチェックを付け、プロジェクトを作成します。

 

2014-04-28_ProjectSettings.png

 

プロジェクトを作成したら、最初にいくつかプロジェクトの設定を変更します。

 

まず、配布先の PC に Visual C++ ランタイム DLL をインストールする必要がないように、Visual C++ ランタイムを静的にリンクするようにしておきましょう。

 

Debug と Release の両方を次のように設定してください。

 

2014-04-28_ProjectCompiler1.png

 

2014-04-28_ProjectCompiler2.png

 

次に、リンカーの設定ページで、モジュール定義ファイル (後で作成します) の名前を入力します。ここでは Evfilter.def としています。

 

二つの構成 (Debug と Release) の両方を変更しなければいけませんので、お忘れなく。 

 

2014-04-28_ProjectLinker.png

 

以上のプロジェクト設定が済んだら、次にMain.cppというソースファイルを新規作成してください。

 

このファイルには、DLL を作る際のおまじない的な WinMain の定義を記述しますので、他の DLL を作るときにも流用可能です。

 

#include <Windows.h>


HINSTANCE hDllInstance;


BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD reason, LPVOID pVoid)
{
  if (reason == DLL_PROCESS_ATTACH)
  {
    // この DLL のモジュールハンドルを保存しておく
    hDllInstance = hInstance;
    DisableThreadLibraryCalls(hDllInstance);
  }
  else if (reason == DLL_PROCESS_DETACH)
  {
    // 何もしない
  }
  return 1;
}

それから、モジュール定義ファイル Evfilter.def を新規作成して、次のような内容を書き加えてください:

LIBRARY Evfilter


EXPORTS
EXPORTS セクションには、後でエクスポートする関数の名前を追加します。
 
とりあえず、現時点で、メニューから ビルド>ソリューションのビルド を選択すれば、DLL が問題なくビルドできるはずなので、確認してみてください。
 

 

エクスポートする関数の追加

 

それでは、エクスポートする関数 (の空っぽの定義) を加えてみます。

 

Exports.cpp という新規ソースファイルをプロジェクトに追加して、次のプログラムを記述してください。

#include  <Windows.h>

extern HINSTANCE hDllInstance;

int __stdcall EventReloadA(const char *url)
{
  return 1;
}

int __stdcall EventReloadW(const wchar_t *url)
{
  return 1;
}

int __stdcall EventInEffectA(const char *currency, int minutesBefore, int minutesAfter)
{
  return 0;
}

int __stdcall EventInEffectW(const wchar_t *currency, int minutesBefore, int minutesAfter)
{
  return 0;
}
ビルド 509 と 600 以降の両方に対応できるように、それぞれの関数に、マルチバイト文字列を受け取るバリエーションと Unicode 文字列を受け取るバリエーションを用意しました。
 
一応、予定としては、EventReloadA / EventReloadW 関数で指標データのダウンロードと読み取りを行い、EventInEffectA / EventInEffectW 関数で、現在が指標の直前または直後に該当するかどうかを判定するつもりです。もしかすると、関数のプロトタイプは後で変更するかもしれませんが、今回はコンパイルが通ればよしとしましょう。

 

それでは、これらの関数がエクスポートされるように、モジュール定義ファイルに関数名を書き加えます:

LIBRARY Evfilter

EXPORTS
    EventReloadA
    EventReloadW
    EventInEffectA
    EventInEffectW

ここまでの手順を正しく再現すれば、四つの関数がエクスポートされ、かつ VC++ ランタイムに依存しない DLL がビルドできるようになっているはずです。

 

試しに、PE ダンプツールで、たったいまビルドされた DLL を確認してみると

 

2014-04-28_ImportTable.png

 

インポートしている DLL は KERNEL.32.dll だけであることが分かりますし、
 
2014-04-28_ExportTable.png
 
エクスポートテーブルにも四つの関数の名前がちゃんと出力されているようです。
 
ちなみに、A と W がどちらも同じ仮想アドレスに配置されていますが、これは、現時点ではどちらも中身が全く一緒であるために、リンク時の最適化が施された結果です。
 
それでは、次の投稿で、これらの関数の内容を作り込んでいきましょう。
 

現時点のソースファイル一式はこちら: 添付ファイル  Evfilter-20140428.zip   3.25KB   2 ダウンロード

 

これは、新しく DLL を作るときの雛形としても使えるかもしれません。


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#12 Kenta

Kenta

    パワーユーザ

  • カスタマー
  • PipPipPip
  • 102 件の投稿

投稿: 2014年4月28日 18時42分

いつもご苦労さまです^^;勉強させていただいてます :rolleyes:

記事更新楽しみにしてます。


  • TeamJunTradeezさんがいいね!と言っています

#13 あらいど@急速潜行中

あらいど@急速潜行中

    ルーキー

  • メンバ
  • Pip
  • 1 件の投稿

投稿: 2014年4月29日 21時39分

大変勉強になります。

 

早速色々試してみます^^ありがとうございます。


  • TeamJunTradeezさんがいいね!と言っています

#14 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年5月 2日 12時31分

指標データをダウンロード&パースする DLL を C++ で作る話題を続けます。

 

正直なところ、簡単なものであれ C++ のプログラムを理解できる人にはそもそも解説なんて不要かも、と思わないでもないのですが、他にちょうどよいネタもあまりないので、このままの方向性で進めていきます。

 

ダウンロードとパースの部分を実装する前の、おおまかな骨組みは、一例として次のように記述することが出来ます。

#include <Windows.h>
#include <time.h>
#include <comutil.h>
#include "Utils.h"

#pragma comment(lib,"comsuppw.lib")

extern HINSTANCE hDllInstance;

namespace
{
  const DWORD MyTimeout = 30000;
  const wchar_t *MyMutexName = L"TraderSquareEvfilterMutex";

  bool FetchAndReadDataFile(const std::wstring &url, const std::wstring csvPath)
  {
    return true;
  }
}

int __stdcall EventReloadW(const wchar_t *url)
{
  wchar_t dllPathBuff[MAX_PATH];
  if (GetModuleFileName(hDllInstance, dllPathBuff, MAX_PATH) > 0)
  {
    std::wstring dllPath(dllPathBuff);
    std::wstring csvPath = ExtractDirectory(ExtractDirectory(dllPathBuff, false), false) + L"\\files\\event.csv";


    MyHandle hMutex(CreateMutex(NULL, FALSE, MyMutexName), NULL);
    if (hMutex != NULL)
    {
      DWORD waitResult = WaitForSingleObject(hMutex, MyTimeout);
      if (waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED)
      {
        MutexReleaser mutexReleaser(hMutex, waitResult == WAIT_ABANDONED);
        
        return FetchAndReadDataFile(std::wstring(url), csvPath);
      }
    }
  }
  return 0;
}

int __stdcall EventReloadA(const char *url)
{
  _bstr_t bStr(url);
  return EventReloadW(bStr);
}

int __stdcall EventInEffectW(__time64_t datetime, const wchar_t *currency, int minutesBefore, int minutesAfter)
{
  return 0;
}

int __stdcall EventInEffectA(__time32_t datetime, const char *currency, int minutesBefore, int minutesAfter)
{
  return 0;
}

まず、前回お目にかけた骨組みと比較すると、EventInEffect 関数のプロトタイプが少し変わりました (順番も関数宣言の便宜上 W を A の上にしました)

 

前回は、暗黙の内に現在の日時を使って判定をするつもりで、日時のパラメータを用意していなかったのですが、そこは考え直し、(指標データさえ用意すれば) バックテストでも利用できるように日時のパラメータを与えることにしました。

 

また、日時パラメータの型ですが、基本的にビルド 509 以前の MQL では datetime 型は 32 ビットの整数でしたが、ビルド 600 以降は 64 ビット整数に変わっています。

 

したがって、ビルド 509 から呼び出す前提の EventInEffectA の方は 32 ビットの__time32_t で、EventInEffectW は 64 ビットの__time64_t にしています。

 

その他、上のコードの注意点をいくつか述べておきます。

 

モジュールパスの取得

 

将来的に、ダウンロードする指標データを experts\files フォルダに一時的に保存したいので、DllMain で保存しておいた DLL のモジュールハンドルhDllInstance を使い GetModuleFileName API で DLL 自体のファイルパスを取得しています。

 

ビルド 600 以降、DLL が配置される場所はデータフォルダ以下になっていて、メインの実行ファイルである terminal.exe のファイルパスを取得してもあまり意味が無いので、このようにして DLL 自分自身のパスを取得します。

 

ANSI から Unicode への変換

 

似たような処理を ANSI 版と Unicode 版それぞれ記述するのは無駄ですので、ANSI 版は文字列の変換をした後で Unicode 版を呼び出すようにしましょう。

 

ここでは、極力サボりたいので、_bstr_t という COM 関連のライブラリに含まれるクラスを拝借して、文字列の変換を丸投げしています。

int __stdcall EventReloadA(const char *url)
{
  _bstr_t bStr(url);
  return EventReloadW(bStr);
}

そのために、ヘッダファイルをインクルードしている箇所

#include <comutil.h>

およびライブラリのリンクを指定している箇所

#pragma comment(lib,"comsuppw.lib")

があります。

 

特に、追加のライブラリファイルを #pragma で記述する方法は、コンパイル時のプリプロセッサ定義によってリンクするターゲットを変更したい、というようなケースで便利ですので、覚えておいても損はないかもしれません。

 

リソースの解放

 

上のプログラムでは、Mutex のハンドルを MyHandle という単純なハンドル管理クラスに管理させています。

 

MyHandle の定義はこんな感じです:

class MyHandle
{
public:
  MyHandle(HANDLE handle, HANDLE invalidValue) { hHandle = handle; hInvalidValue = invalidValue; }
  ~MyHandle() { if (hHandle != hInvalidValue) CloseHandle(hHandle); }
  operator HANDLE () { return hHandle; }

private:
  MyHandle(MyHandle &);
  MyHandle& operator=(MyHandle &);

  HANDLE hHandle;
  HANDLE hInvalidValue;
};

これは RAII (Resource Acquisition Is Initialization) と呼ばれるもの (イディオムというかテクニックというか) で、リソースを取得すると同時にその生存期間管理を自動変数に任せ、スコープを抜けたときに自動変数がクリーンアップされるタイミングでリソースも解放する構造になっています。

 

これの利点は、複数のリソースを利用しているときなどに、その後の処理を実行している途中で例外が発生しても、(割り当て済みのリソースだけが) 適切に解放処理が行われる点にあります。

 

同じようにして、Mutex をリリースする処理も MutexReleaser というクラスに任せています。

 

今回のソースファイル: 添付ファイル  Exports.cpp   1.47KB   2 ダウンロード

およびヘッダファイル: 添付ファイル  Utils.h   1.17KB   2 ダウンロード


  • TeamJunTradeez と あらいど@急速潜行中がいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#15 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年5月 9日 23時35分

ちょっとバタバタしているので、ネタ小出しで続行します。 -_-

 

前回の投稿では中身が空っぽでしたが、FetchAndReadDataFile という関数があったのを覚えてますでしょうか。

 

この関数には、その名の通り、CSV ファイルを (ウェブサーバから) 取得して、メモリに読み込むような動作をさせるつもりです。

 

今回は、ウェブサーバからダウンロードしてファイルに保存する部分を作ってみます。

 

C++ で HTTP 周りを実装する場合、通常は軽量の HTTP クライアントライブラリを使った方が楽だと思いますが、この指標フィルタの DLL に限れば、特定の URL から GET するだけですので、wininet.dll を使ってゴリゴリと実装します。

 

内容的には、こんな感じです:

  const wchar_t *MyAgentName = L"EvfilterAgent";

  const int ReadBufferSize = 1024 * 8;
  char ReadBuffer[ReadBufferSize];

  bool FetchAndReadDataFile(const std::wstring &url, const std::wstring csvPath)
  {
    MyInternetHandle hInternet(InternetOpen(MyAgentName, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0));
    if (hInternet == NULL)
      return false;

    MyInternetHandle hUrl(InternetOpenUrl(hInternet, url.c_str(), NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_RAW_DATA | INTERNET_FLAG_PRAGMA_NOCACHE, 0));
    if (hUrl == NULL)
      return false;

    MyHandle hWriteFile(CreateFile(csvPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL), INVALID_HANDLE_VALUE);
    if (hWriteFile == INVALID_HANDLE_VALUE)
      return false;

    DWORD bytesRead, bytesWritten;
    while (InternetReadFile(hUrl, ReadBuffer, ReadBufferSize, &bytesRead))
    {
      if (bytesRead == 0)
        break;

      WriteFile(hWriteFile, ReadBuffer, bytesRead, &bytesWritten, NULL);
    }

    return true;
  }

前回の投稿で簡単に紹介した RAII という定石を使って、インターネットハンドルとファイルハンドルの解放処理を自動変数に任せているので、関数の途中でエラーが起きて return false しても、その前に作成したハンドルはちゃんと解放されます。

 

wininet や CreateFile などの API の説明を始めるとキリが無いので割愛しますが、これでダウンロードする部分は出来上がり。

 

実際に MQL スクリプトから呼び出して、files フォルダに CSV ファイルがダウンロードされるかどうかを確認してみます。

 

まず、DLL 関数の宣言を MQH にまとめます:

#import "Evfilter.dll"
bool EventReloadW(string url);
bool EventReloadA(string url);
bool EventInEffectW(datetime dt, string currency, int minBefore, int minAfter);
bool EventInEffectA(datetime dt, string currency, int minBefore, int minAfter);
#import

これを MT4 データフォルダの include サブフォルダに Evfilter.mqh というファイル名で保存してください。

 

また、スクリプトはこんな感じになります。無論、URL の部分は、適宜 CSV ファイルが公開されている URL に差し替える必要があります。

#include "Evfilter.mqh"

int start()
{
  EventReloadA("http://somewhere.com/filename.csv");
  return(0);
}

※なお、Trader Square メンバ向けに、弊社が VPS で自動生成している CSV ファイルを公開しています。詳しくはこちらの記事をご覧ください。

 

ビルド 600 以降の場合は Unicode 版の関数である EventReloadW にするのをお忘れなく。

 

で、ビルド 509 でも 600 でも、スクリプトを実行した後には、files フォルダに CSV ファイルがダウンロードされていることが確認できます。

 

2014-05-09_Build509.png

 

2014-05-09_Build600.png

 

ソースファイル一式はこちら: 添付ファイル  Evfilter20140509.zip   4.99KB   3 ダウンロード


  • あらいど@急速潜行中さんがいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#16 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年5月13日 15時19分

前回ファイルのダウンロード部分まで作りましたので、

  • ダウンロードしたファイルを読み込む部分
  • ユーザの日時/通貨を指定した問い合わせに回答する部分

を作成すれば、MT4 向け指標フィルタライブラリ、とうとう完成です。

 

…で、ファイルを読み込んだり、構造体のリスト(実際には STL の vector をコンテナとして使っています)を管理したりするコードを解説するのも大変というか退屈ですので、ばっさり割愛します。 :blink:

 

出来上がったソースとライブラリはこちら: 添付ファイル  Evfilter20140513.zip   68.62KB   19 ダウンロード

 

ダウンロードコーナーにも簡単な説明を付けてアップしておきます。


  • あらいど@急速潜行中さんがいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#17 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年5月19日 21時30分

DailyFX の指標データ変換スクリプトにおいて、重複行をまとめる処理が終わらない(無限ループに陥る)バグがあったので修正しました。

 

最新版のスクリプトはこちら: 添付ファイル  DailyFxCalendarToCsv.py   5.39KB   25 ダウンロード


  • Kentaさんがいいね!と言っています

Softgate Limited - http://www.softgate.co.jp/


#18 hasegawak

hasegawak

    ルーキー

  • メンバ
  • Pip
  • 4 件の投稿

投稿: 2014年5月21日 14時32分

ソフトゲートさん、

 

お世話になってます。CSV使わせていただいています。

コーディングの話からずれてしまうのですが、ちょっと思い出したのでソフトゲートさんの

作品の利用方法の参考になれば幸いです。

 

取引する通貨がUSD絡み以外の場合、たとえば AUDNZDだとします。

この場合でも USDの重要指標では、全通貨で Spreadが開いたり値段がぶっ飛ぶことが多いので、 取引通貨に加えて USDも

Filterすると成績があがることがあります。 NFPやFOMCがその一例です。

複数通貨でのFilterを実装したEAのParameterの参考例をご紹介します。

 

extern int    AvoidNews           = 0;
extern int    AvoidNewsBeforeMin1 = 220;
extern int    AvoidNewsAfterMin1  = 0;
extern int    AvoidNewsBeforeMin2 = 80;
extern int    AvoidNewsAfterMin2  = 30;
extern int    AvoidNewsBeforeMin3 = 55;
extern int    AvoidNewsAfterMin3  = 25;
extern int    Avoid_USD_BeforeMin1= 52;
extern int    Avoid_USD_AfterMin1 = 86;
extern int    Avoid_USD_BeforeMin2=  0;
extern int    Avoid_USD_AfterMin2 = 30;
extern int    Avoid_USD_BeforeMin3=  0;
extern int    Avoid_USD_AfterMin3 = 10;
extern int    Avoid_US_FOMC       =  0; // o or 1
extern int    Avoid_US_FOMC_Sev   =  3; // Severity
extern int    Avoid_US_FOMC_Before=  0; // Min
extern int    Avoid_US_FOMC_After =  0; // Min
 

これでその通貨とUSDと、FOMC特化で、 シビリティーは1,2,3ごとに

分けて Filterするように実装しました。 

Parameterだけですみませんが、わかる人にはわかると思います。


  • Kentaさんがいいね!と言っています

#19 naoya0001a

naoya0001a

    ルーキー

  • メンバ
  • Pip
  • 3 件の投稿

投稿: 2014年6月28日 06時23分

EventInEffectW関数 のminAfterを0に設定すると

minBeforeに何を入れても、なぜか必ずtrueを返すようです。

 

 

あと、質問ですがローカルにEvfilterEvents.csvを残すようですが

2回目アクセスするときは、サーバー側のタイムスタンプとか比較して

次はこのローカルのファイルから参照する動きでしょうか?

毎回サーバファイルをダウンロードしにいく仕様なら、ダウンロードする関数と

メモリにアップする関数を分けたほうがいいと思いました。

 

それと、EvfilterEvents.csvを開いていたらツールが動かないみたいで

なんらかの例外処理かメッセージの出力がいるのかなと。



#20 ソフトゲート

ソフトゲート

    運営スタッフ

  • 管理者
  • 591 件の投稿

投稿: 2014年6月28日 09時43分

ご指摘ありがとうございます。判定している行

        if ((minutesBefore <= 0 || it->datetime - minutesBefore * 60 <= datetime) && (minutesAfter <= 0 || it->datetime + minutesAfter * 60 >= datetime))

        if ((minutesBefore < 0 || it->datetime - minutesBefore * 60 <= datetime) && (minutesAfter < 0 || it->datetime + minutesAfter * 60 >= datetime))

にしておかないといけませんね。後でファイルを差し替えておきます。

 

「2回目アクセス」の「アクセス」がどの処理を指しているのか分かりませんが、EventReloadA/W を呼べば必ずサーバからダウンロードしますし、

EventInEffectA/W を呼ぶだけならメモリ上のデータをサーチします。

 

そもそもなるべく簡単な API にしようという方針で作ったものですし、EventReloadA/W は週明けなどに呼び出すだけという前提であるため、

わざわざダウンロードとメモリへの読み込みを分ける必要性は感じません。

 

「ツールが動かない」というのは、MT4 がハングアップした状態になるとか、

単にEAのアタッチでエラーが起きるとか、いろいろあると思いますが、具体的にどういう現象でしょうか。

CSV が開かれている場合はエラーが返りませんか?


Softgate Limited - http://www.softgate.co.jp/





1人のユーザがこのトピックを閲覧中

メンバ0人、ゲスト1人、匿名ユーザ0人