2012/03/29

Sqlite3を使って相互のデータを確認できるか試してみた。

kartzさんにご指摘頂いた内容を訂正しました。2012/3/30
※2012/4/5作成したコードを変更しました。【sqlite3_wrapper_2.0.zip
※2012/4/7に追記しました。
テストの内容は、以下のとおりです。また、作成したコードは、ココにあります。
  1. MT4(A)のデータをコピーし、データベースに保存する。
  2. データベースからMT4(B)へデータを送る。
    ※イメージ的にはこんな感じになります。
334

DLLの追記

前に紹介したSqlite3のwrapperにSqlite3関数を直接操作する関数を追加しました。
※追加作成した関数には、_nを追記しています。なお、以下は抜粋したものです。
////////////////////////////////////////////////////////////////////////////
//int sqlite_query_n (int handle_db, string sql, int& cols[]);
//  handle : sqlite_open_n にて取得
//  sql         : クエリ
//  cols[ ]      : int cols[1];と設定。列数の合計を受け取る。
//  ※戻りデータのあるクリエを発行し、ハンドルを返す。
//  ※反復処理は、(sqlite_next_n,sqlite_get_*_n)を使用。
//  ※(sqlite_free_query_n)を使用しリセットをおこなう。
// ※戻り値は、実行用のハンドルhandel_qr
////////////////////////////////////////////////////////////////////////////
__declspec(dllexport) int __stdcall sqlite_query_n (int handle_db, const char *sql, int* cols){
    sqlite3 *s= (sqlite3 *)handle_db;
    sqlite3_stmt *stmt;
    int res;
    res = sqlite3_prepare (s, sql, strlen (sql), &stmt, NULL);
    if (res != SQLITE_OK) return -1;
     *cols = sqlite3_column_count (stmt);
    return (int)stmt;
}
////////////////////////////////////////////////////////////////////////////
//int sqlite_next_row_n (int handle_qr);
//  ※次の行を取得する 。
//  ※戻り値(1:あり 0:なし)
////////////////////////////////////////////////////////////////////////////
__declspec(dllexport) int __stdcall sqlite_next_row_n (int handle_qr){
    sqlite3_stmt *stmt = (sqlite3_stmt*)handle_qr;
    int ret;
    ret = sqlite3_step (stmt);
    return ret == SQLITE_ROW ? 1 : 0;
 //return ret;
}
///////////////////////////////////////////////////////////////////////////
//string sqlite_bind_*_n (int handle_qr,int index,* data);
// ※データの入力
//  ※int index 列番号 (1,2,3,4.....)
//  ※*  data  入力データ
//  ※戻り値 エラーコード
///////////////////////////////////////////////////////////////////////////
//文字
__declspec(dllexport) int  __stdcall sqlite_bind_text_n (int handle_qr,int index,const char *text){
    sqlite3_stmt *stmt = (sqlite3_stmt*)handle_qr;
    int ret;
    ret = sqlite3_bind_text(stmt,index,text,(int)strlen(text),SQLITE_STATIC);
    return ret;
}


MQL4の作成

【sqlite3.mqh】 Sqlite3wrapper.dllを使用するためのヘッダー
【Import_sqlite.mq4】 データベースにコピーするためのインジケータ
【export_sqlite.mq4】 データベースから値を取得する(今回は、HiとLowのみ)インジケータ
パラメータの説明
  • extern string Symb = "USDJPY";//通貨ペア名
  • extern int Tframe = 5 ;//タイムフレーム(1,5,15,30…)
  • extern string DB = "C:\\HistData.db" //DB名(絶対パス)
    ※ 通貨ペア名+タイムフレームでテーブル名を作成します。


セット

  1. 以下の様にセットしてください。
    【terminal.exe】と同じ位置にSqlite3.dll
    【expertsフォルダ】
      ┣【librariesフォルダ】にSqlite3wrapper.dll 
      ┣【include】にsqlite3.mqh
      ┗【indicators】にImport_sqlite.mq4、export_sqlite.mq4
  2. 2種類のMT4に同じようにセットします。
  3. データを抽出する側のMT4を起動させ、チャートを開き新規のデータを取得させます。
  4. Import_sqlite作動させ、指定した場所にDBが作成されたことを確認します。
  5. データを取得する側のMT4を起動させて、export_sqliteを作動させます。 
     


Build418への対応

現在、最新版のBuild418は、DLLからの戻り値がdoubleの場合にエラーが発生するとの報告が上がっています。そこで、Build418で使用する場合は、double型で取得している部分のコードを文字型で取得する様に変更してやる必要があります。以下がその変更部分です。
export_sqlite.mq4 コメント化されているコードと変更する。
//取得 for(i=0; i<limit; i++){    if(sqlite_next_row_n (handle_qr)==1){
   
// Hi[i] = StrToDouble(sqlite_get_col_n(handle_qr, 0));//Build418用
   
// Lw[i] = StrToDouble(sqlite_get_col_n(handle_qr, 1));//Build418用
    
Hi[i] = sqlite_get_col_double_n(handle_qr, 0);
    
Lw[i] = sqlite_get_col_double_n(handle_qr, 1);
 
}




挙動不安定

データをDBに送るImport_sqliteは、安定しているのですが、DBからデータを取得するexport_sqliteが、Buildのバージョンによって挙動が不安定になるようです。作動確認したBuildは、409、416、418です。(OS:XP)(・・?
  • 409:ヒストリカルデータは取得は出来るが、更新時にDLLエラーが発生しexport_sqliteが停止する。
  • 416:更新時に挙動が不安定になる。(データ抜けが発生する。)
  • 418:文字型で取得すれば416よりも挙動が安定する。 
※再確認します。(2012/3/30追記)
   確認した内容を記載します。
   ※418:ヒストリカルデータは取得できるが、更新時にDLLエラーが発生し停止する。
   ※409:更新時に挙動が不安定となった。不安定の原因は、足の作成タイミングのズレが原因?
   ※416:今回は、テストしていません。。orz

(2012/4/7追記)
現在添付してあるコードでの作動状況を動画で添付しておきます。
左側:データを取得しDBへ保存及びDBから自データを取得しチャートに表示したものです。
右側:左側のデータをDBから取得しチャートに表示したものです。
 動画より以下の点が確認できます。
  • 左側のチャートは、問題なく作動している。
  • 右側のチャートの指標も新規のBarが確立していれば問題なく作動する。
  • 左側のチャートのBarが確立後に右側のBarが確立すれば問題は起こらない。
  • 指標に問題が発生するのは、右側のチャートのBarが左側のBarよりも早く表示された時。
  • 誤作動が起こった箇所のデータは、Bar[2]のデータを表示している。
となっています。新規Barの発生タイミングのズレが何らかのバグを発生させているのは解りましたが、その後の対応策は現在のところ思いついていません。。。orz


まとめ

どんな目的でこれを使用するか?を考えてみると。
  • データの抜け確認用に使用すれば簡単に抜けが確認できそうです。
    rrt 
    上記は、週末・週初の取引時間が違うためにデータ抜けを起こした部分です。

export_sqliteの挙動を安定させようと試行錯誤してみましたが良い成果を上げることができませんでした。。orz

14 件のコメント :

kartz さんのコメント...

こんばんは。

export_sqlite.mq4 を拝見して、気付いた箇所だけコメントします。実行はしていません。

if(handle_db == 0){
string table = StringConcatenate(Symb,Tframe);
handle_db = sqlite_open_n (DB);
}
(中略)
query = StringConcatenate(…, table, …);

となっていますが、2回目には handle_db != 0 のはずですので、table が未定義状態でクエリに突入してしまいます。動いていたのは、たまたま table が上書きされずに内容が保持されていたためだと思います。

static string とするのが簡単ですかね。

table を大域変数にして、sqlite_open_n() は init() に入れる方が、deinit() の sqlite_close_n() とバランスがとれるような気もしますが。

bighope さんのコメント...

kartzさんへ
ご指摘ありがとうございます。
コードを修正致しました。
何度もすみません。
作動状況について再確認いたします。

kartz さんのコメント...

どういたしまして。

自分のコメントを読み直して気が付きましたが、
C/C++ ではなく MQL4 なので、

「table が未定義状態で …」
  ↓
「table が未設定 (空文字列) で …」

と訂正しておきます orz。

kartz さんのコメント...

こんにちは。

> 確認した内容を記載します。
> ※409:更新時に挙動が不安定となった。不安定の原因は、足の作成タイミングのズレが原因?

limit=iBars(Symb,Tframe)-counted_bars-1;
の下に、
if (limit <= 0) return(0);
が要るんじゃないでしょうか。SQL の文法違反になりますので。

入れたら 409 で更新時も動きました。

kartz さんのコメント...

もしかして…。

ご存じとは思いますが、SQLite を複数のプロセスから同時に使用する際の注意点については SQLite の FAQ をご覧下さい。

http://www.sqlite.org/faq.html#q5

bighope さんのコメント...

kartzさんへ

貴重な情報をありがとうございます。

SQLの文法エラーに関しては、以下のコードを作成し確認しています。(SQLを固定してみました。)しかし、結果は、同じ現象が発生します。。orz

https://docs.google.com/open?id=0B42sh1xkCfgteXQ4bjJ3bEpRN21GVEhyazJBSllJQQ


【複数のプロセスから同時に使用】は、知りませんでした。。。orz

DBの保存場所のファイルシステムは、、NTFSを選択しているので問題ないはずです。

最後の『SQLiteは別のプロセスによってロックされているファイルにアクセスしようとすると、 デフォルトの動作では、SQLITE_BUSY返します。sqlite3_busy_handler()またはsqlite3_busy_timeout()API関数を使用してCコードからこの動作を調整することができます。』

これが原因ぽいですね。

busy関数を使て調整してみます^^;


2012年3月31日22:59
削除

reptile さんのコメント...

Can you provide HistData.db ?
Is this wraper working good with 419build? I have script EventHist script + db and no reaction..

bighope さんのコメント...

Hi, reptile
I made the save the file. Please confirm.
This HistData.db was created in 419build.
As an example, I have created a data USDJPYM5.
The input is not a problem.
Output, there is a problem listed in the blog.
Thank you for your comment!
https://docs.google.com/open?id=0B42sh1xkCfgtdkdib3hpMU1TTTA

reptile さんのコメント...

Many thanks bighope! I was in hope you can help and.. now EventHist works and export to sqlite ok!
Strange case.. start works after I found build 418 and instal this.Then b419 ok.Poor MQ : )
For other user.Coz this case is crazy and complicated.I sugest to copy all files to \experts\libraries what provide bighope I don't know why but when i made this everything works ok.. I repeat this case many time with other instances of mt4 418/9 and when I copied all then works if not or selected files when I want found what realy is need then no reaction.. very strange.. coz I belived that only .dll and .def is need. After copy all I deleted other and only .dll in \experts\libraries and work!OMG.. crazy mt4.And don't forgot about sqlite3.dll in amin folder of mt4 :D If this not helps windows\system32..

Once again thx bighope!
Bigchaos in this comment but I tested this wraper many times on few builds and always something was wrong :D Today looks work.Backing to coding..

bighope さんのコメント...

Hi, reptile
There is a problem in b418. It is better not to use
http://forum.mql4.com/47037/page2
To copy the library is the only sqlite3_wrapper.dll
(´・ω・)b good luck!

cupie さんのコメント...

はじめまして。

expert advisorからdbを使いたく、こちらのラッパーを利用させていただいてるのですが上手くいかないことがあります。

テーブルに2000個ぐらいレコードがあり、SQLで
SELECT * FROM tblTest LIMIT 10
として、sqlite_next_row_nがずっと回ってしまいます(おそらく2000回)。レコードは10個分しか返っていないようで、10個のレコードが200回ループしているようです。

ついでですがsqlite3.mqhのsqlite_query_nのコメントに戻り値がhandle_dbと書いてありますがhandle_qrですよね。

今後の進化に大変期待をしております。

cupie さんのコメント...

すいません、上記ぐだぐだ書きましたが
sql = "SELECT s_date, value FROM tblA";
handle_query =sqlite_query_n (handle_db, sql, cols);

while (sqlite_next_row_n (1, handle_db, handle_query) == 1)
{
//hoge hoge
}
でなぜかループがずっと続くのが原因のようでした。
原因がわかりましたらご教示ください。

bighope さんのコメント...

cupieさんへ
はじめまして。
ご質問の内容ですが、v2.0は、下記の様になっているのが原因だと思います。コメントなど書き換えていませんでした。。orz
バージョンを落して頂けると対応できると思います。以下が以前のバージョンです。
https://docs.google.com/file/d/0B42sh1xkCfgtbFNfZTV0TTlUS1NRNHJlYnlaNHRWQQ/edit


////////////////////////////////////////////////////////////////////////////
//int sqlite_next_row_n (int sleeptime,int handle_db,int handle_qr);
// ※sleeptime(停止時間(ms)
// ※次の行を取得する 。
// ※戻り値(1:あり 0:なし)
////////////////////////////////////////////////////////////////////////////
__declspec(dllexport) int __stdcall sqlite_next_row_n (int sleeptime,int handle_db,int handle_qr){
sqlite3_stmt *stmt = (sqlite3_stmt*)handle_qr;
sqlite3 *db = (sqlite3*)handle_db;
int ret = 0;
int i = 0;

while(sqlite3_step (stmt)<100){
while(sqlite3_busy_timeout(db,sleeptime)!=0){
if(i>1000)return -1;
i++;
}
if(ret>1000)return 0;
ret++;
}
return 1;
//ret = sqlite3_step (stmt);
//return ret == SQLITE_ROW ? 1 : 0;
//return ret;
}

cupie さんのコメント...

ご返信ありがとうございます。

技術がないのでコンパイルはできませんがsqlite3_step が SQLITE_DONEであれば0を返すようにすれば正常になるようですね。

いただいた前バージョンを使用させていただきます。