記事「MQL5におけるSQLiteの機能:銘柄とマジックナンバー別の取引統計を表示するダッシュボード」についてのディスカッション

 

新しい記事「MQL5におけるSQLiteの機能:銘柄とマジックナンバー別の取引統計を表示するダッシュボード」はパブリッシュされました:

この記事では、口座別、銘柄別、および取引戦略別に取引統計をダッシュボードに表示するインジケーターの作成について考察します。コードは、ドキュメントおよびデータベース操作に関する記事の例に基づいて実装します。

この関数は、選択された銘柄、マジックナンバー、または口座全体に対する最終的な統計テーブルを描画するために設計されています。関数は、統計テーブルの種類、およびシンボル名、マジックナンバーの文字列値、またはアカウント番号を受け取ります。渡された文字列から、対応する統計データ構造体配列内の銘柄、マジックナンバー、またはアカウントのインデックスを特定します。必要な構造体データは、取得したインデックスを使用して抽出され、次にテーブルのセル座標に従って描画されたテーブル内に配置されます。このとき、表示されるテキストの水平方向のオフセットは、見出しがテーブルセルの左端に揃えられ、値のテキストがセルの右端に揃えられるように計算されます。すべてのデータは4列で表示され、パネル上で視覚的に「項目名 -- 値」の2列構成が2セットにグループ化される形式になります。

インジケーターをコンパイルして、結果を確認してみましょう。

すべての宣言された機能が期待通りに動作していることが確認できます。テーブル内のテキストが、カーソルを動かしたりスクロールしたりする際にわずかに「点滅」して見えることがありますが、これは最適でない再描画方式によるもので、テーブルの表示部分全体が継続的に再描画されているためです。カーソルの下にあるテーブル行だけを対象としたより複雑なロジックを導入すればこれを回避することもできますが、それは本プロジェクトの目的ではありません。

作者: Artyom Trishkin

 

大量の取引履歴を扱うことができるツールが不足している。

残念なことに、このツールキットは、他の多くのツールと同様、履歴を要求すると単にハングアップする。



履歴を取得するのに5分かかる。その後、ウィンドウを使って何かをすることは不可能である。

 
fxsaber #:

大量の取引履歴を扱うツールが不足している。

残念ながら、このツールキットは、他の多くのツールキットと同様、履歴を要求するとハングアップしてしまう。



履歴を取得するのに5分。その後、ウィンドウを使って何かをすることは不可能である。

プライベート・メッセージで、投資家のアカウントにアクセスすることはできますか?
 
Artyom Trishkin #:
投資家が口座にアクセスすることはできますか?

残念ながら、そのような可能性はありません。デモ口座で、非同期OrderSendを使用して、1時間に異なるシンボル/マジックで必要な数のポジションをオープン/クローズするスクリプトを使用します。

 

モスクワ証券取引所で働きたくない



 
Konstantin Seredkin #:

モスクワ証券取引所で働きたくない

当然だ。1つのシンボルで複数のロボットが動作している場合(またはロボット+手動取引)、合計ポジションを除いて、ポジションに関連するすべてのものは、ネットでは役に立たない。

 
fxsaber #:

大規模な取引履歴を扱うためのツールが不足している。

残念ながら、このツールキットも他の多くのツールと同様に、取引履歴の取得をリクエストすると単にフリーズしてしまいます。



履歴を取得するのに5分かかります。その後、ウィンドウでは何も操作できなくなります。CPU使用率が100%に達してしまうからです。

毎回履歴全体をダウンロードする必要はないかもしれません。最後に記録された取引の時点からデータを取得し、別のサーバーにあるデータベースを定期的に更新するようにすればよいのです。ダッシュボードの指標に必要なデータのみをクエリで取得すればよいのです。
 
fxsaber #:

膨大な取引履歴を扱うためのツールが不足している。

残念ながら、このツールも他の多くのツールと同様、取引履歴を照会しようとするとフリーズしてしまいます。



履歴の取得に5分かかります。その後、ウィンドウで何も操作できなくなり、CPUがフル稼働したままになります。

いやいやいや
SQLiteはリソースを大量に消費するタスクには全く向いていない。
なぜこの学生向けの「おもちゃ」をMQL5に組み込んだのか、私には謎です。PostgreSQLを組み込んだ方がはるかに良かったでしょう。そちらなら、少なくとも高性能なJsonBがあります。
そもそも、構造体の配列をバイナリ形式で保存・読み込むことに勝る方法はありません。データベースなど必要ありません。ましてや、あなたにはすでにすべてのノウハウがあるのですから。
 
Nikolai Semko #:
いや、いや、いや
SQLiteは、リソースを大量に消費するタスクにはまったく不向きです。
なぜこの学生プロジェクトをMQL5に無理やり組み込んだのか、私には謎です。PostgreSQLを統合した方がはるかに良かったでしょう。少なくともPostgreSQLには高性能なJsonBがありますから。
実際、構造体の配列をバイナリファイルとして保存・読み込むことに勝る方法はありません。しかも、データベースなど全く必要ありません。特に、必要なコードはすでにすべて揃っているのですから。

おそらくPostgreSWLの方が良いかもしれません。しかし、この記事の著者はテーブルにインデックスを使用しておらず、クエリを見てみると、なぜごく少量のデータセットでしかレスポンシブに動作しないのかがわかります。インデックスを適用し、そのインデックスに合わせたクエリを記述すれば、大規模なデータセットでも問題なく処理できます。

構造体を保存するのは、データをクエリする必要がなく、単に読み込むだけの場合にのみ有用です。データをクエリする必要があるなら、SQLに勝るものはありません。

 
Nikolai Semko #:
いや、いや、いや
SQLiteは、リソースを大量に消費するタスクには全く向いていません。
なぜこの学生向けの「おもちゃ」をMQL5に組み込んだのか、私には謎だ。PostgreSQLを組み込んだほうがはるかに良かった。そちらには、少なくとも高性能なJsonBがあるのだから。
そもそも、構造体の配列をバイナリ形式で保存・読み込むことに勝る方法はありません。データベースなど必要ありません。ましてや、あなたにはすでにすべてのノウハウがあるのですから。

私たちの環境(顧客数が100,500人にも満たない場合)では、SQLiteはPostgreSQLを牛が羊を倒すように圧倒します。ただ、TSはそもそもDBAではなく、データベースの扱いに慣れていないという事情もあります :-) 記事に書かれている内容では、処理が遅くなるのは避けられません

そこにはインデックスもなく、二重集計を伴うめちゃくちゃなネストされたクエリがあり、データベースへの負荷も大きい。記事は、まあご想像の通り、あの目的のために書かれたものです。

ps/ 議論を避けるために、専門サイトなどでそのクエリのどこが間違っているのか尋ねてみてください

//--- マジックナンバー別に、EAごとの取引統計を取得する
   request=DatabasePrepare(db, "SELECT r.*,"
                           "   (case when r.trades != 0 then (r.gross_profit+r.gross_loss)/r.trades else 0 end) as expected_payoff,"
                           "   (case when r.trades != 0 then r.win_trades*100.0/r.trades else 0 end) as win_percent,"
                           "   (case when r.trades != 0 then r.loss_trades*100.0/r.trades else 0 end) as loss_percent,"
                           "   r.gross_profit/r.win_trades as average_profit,"
                           "   r.gross_loss/r.loss_trades as average_loss,"
                           "   (case when r.gross_loss!=0.0 then r.gross_profit/(-r.gross_loss) else 0 end) as profit_factor "
                           "FROM "
                           "   ("
                           "   SELECT MAGIC,"
                           "   sum(case when entry =1 then 1 else 0 end) as trades,"
                           "   sum(case when profit > 0 then profit else 0 end) as gross_profit,"
                           "   sum(case when profit < 0 then profit else 0 end) as gross_loss,"
                           "   sum(swap) as total_swap,"
                           "   sum(commission) as total_commission,"
                           "   sum(profit) as total_profit,"
                           "   sum(profit+swap+commission) as net_profit,"
                           "   sum(case when profit > 0 then 1 else 0 end) as win_trades,"
                           "   sum(case when profit < 0 then 1 else 0 end) as loss_trades "
                           "   FROM DEALS "
                           "   WHERE SYMBOL <> '' and SYMBOL is not NULL "
                           "   GROUP BY MAGIC"
                           "   ) as r");
 

手短に、細かいことを言っていると言われるのを避けるために:

SYMBOLのデータ型はTEXT NOT NULLでなければなりません。SQLiteの データ型はごくわずかです:int、real、text、blob(しかもこれらはオプションであり、指定しなくても構いません)。NOT NULLを指定することで、各クエリでWHERE SYMBOL NOT NULLといった無意味なチェックを行う必要がなくなります

ID、TRADE_IDなどは単なるKEYではなく、PRIMARY KEYです

WHEREやGROUP BYの式で頻繁に使用されるフィールドには、インデックスを付けるのが有効です。スキーマ内のインデックスが不足しているため、クエリの処理が遅くなっています。EXPLAINについては後述します

データベースへのデータ投入は「一括」で行うのが望ましい。 取引ごとにINSERTを行うと、非常に時間がかかります。 可能であれば、1回のクエリで100件のレコードを送信できるようにしましょう。(理想を言えば、作成時に単にデータを投入するだけでなく、起動のたびに同期を行うべきです)

クエリには重複した計算が多く、アプリケーション側に移管できる/移管すべき処理も含まれています。ネストされたSELECT文自体は悪い兆候です。どうしても避けられない場合は、別途EXPLAINを実行して、データベースとクエリを最適化する必要があります。

実運用や大量のトランザクションを扱う場合、当然ながらページネーションや仮想テーブル(必要に応じてビューにデータを読み込み、膨大な構造配列を保持しないようにするもの)が必要になりますが、これはおそらくこの記事の範疇を超えるでしょう。

---

結局のところ、データベースのスキーマが十分に検討されていないため、fxsaberではすべてが重くなってしまったのです。スキーマを適切に修正・補完し、クエリを最適化すれば、最小限の修正で動作するはずです。