[MT4からMT4]コピートレードシステムの開発と使い方[無料ダウンロード]
公開日:
:
コピートレードツール開発 MT4, EA, コピートレード, コピーツール
目次/もくじ
コピートレードツールの特徴
MT4で自動売買ソフトを稼働させるトレーダーには、ときたまトレードを別のMT4にコピーしたい場合があります。例えば、デモ口座でしか稼働しないトライアル版のEAや、口座番号に制限がかかっているEA、FX業者に制約があるEAなどです。
今回は同一コンピューター上でのコピートレードシステムを作ります。コピートレードツールにはサーバーを経由するタイプのものとサーバーを経由しないタイプがありますが、今回はサーバーを経由しないシンプルなタイプです。
ネットを経由させないコピーツールとしてのメリットは、トレードの反映のスピードが早いこと、外部サーバーの構築、管理をする手間がないことが挙げられます。
逆にデメリットとしては、”コピー元のMT4とコピー先のMT4が同一のPC上になければならない”という点があります。この使用場面はいろいろと限られてくると思いますが、基本的には個人利用を想定しています。
コピートレードツールの仕様と使い方
仕様は下記のとおりです。
・DLL、外部ソフトは必要なし
・WEBサーバーを経由しないコピーツール
(同一コンピューター上に複数のコピー元とコピー先のMT4がなければならない)
・コピー元、コピー先に同じEAを一つずつセット
(パラメータでコピー元なのか、コピー先なのか指定)
↑普通だったらコピー元とコピー先のEAを分けるところなんでしょうけど、管理・編集がめんどいってことで合体させました。
・コピーは、レートよりもスピード重視(成行注文)
・コピーしたポジションを裁量決済した場合は、そのポジションはもうコピーされない
・コピー先のロットはパラメータで指定
・コピーするかどうかのタイミングはレート更新ベース
(レート更新が遅い時間帯・通貨ペアにEAをセットするとコピーが遅くなる)
・シンボル名が6文字でないGold,Silver,Oil,DAU,SP500,Coffeeなどはコピーできない
.
左が配信元(コピー元)、右が受信先(コピー先)
受信側のFX業者のレート更新が遅いので、反映に多少ラグが生じています。←汎用版は修正済み
■パラメータ
- IsSender: 配信元のMT4にセットする場合trueにします。
- IsReceiver: 受信(コピー先)のMT4にセットする場合trueにします。
- SendersAccountNumber: 受信(コピー先)のMT4にセットする場合、親のアカウント番号を入力します。
- LotForReceiver: 受信(コピー先)のMT4にセットする場合、コピーするトレードのロットを入力します。
- Slippage: 受信(コピー先)のMT4にセットする場合のトレードのスリッページです。
予約注文には対応していません。
※このEAを使って発生した損失などは一切責任を負いません。
ダウンロード
下記はこのEAのダウンロードリンクです。
(MT4toMT4コピートレードEA)ダウンロード(期限付き)
(MT4toMT4コピートレードEA)ダウンロード(パスコード版)
現在、EAを持て余したプログラマによる EA無料使い放題企画をしています。EAのパスコードは企画参加者に配布しています。詳しくはこちら
汎用版MT4/MT5コピートレードツール
このページはあくまでMT4間のコピートレードツールの開発に焦点を当てたページになります。コピートレード総合はこちら。
開発概要
さて、実際の仕様の話ですが、2010年代初頭であれば外部ソフトやDLLの開発が必要でしたが、現在のMT4であればDLLや外部ソフトを使わずに、EA単体で稼働させることが可能です。(もちろんプログラマの腕ありきですが・・・)
これを実現させる肝となるのが、ファイル関数のCOMMON機能です。ファイル関数は、EA上のデータをMT4外のファイルに記録することができます。
MT4のターミナル上にデータを保存するグローバル変数はそれぞれのMT4上に記録されますが、ファイル関数でCOMMON指定することで、より上位のディレクトリにデータを保存することが可能です。それにより、異なるFX業者のMT4同士でデータのやりとりが可能になります。
ちなみに、データ格納のアクセス範囲としては、
ファイル関数(COMMON指定)>ファイル関数(通常)>グローバル変数(外域変数)>PUBLIC変数>ローカル変数
といった感じです。
右に行くほどデータにアクセスする範囲が狭くなり、外側から参照・書き込みできなくなります。
逆に、左になればなるほど、長期の間データを保存できます。頻繁に出し入れするデータはなるべく短期型の格納方式を使ってメモリの消費を減らしましょう。
MQLを長年いじったことがある方でもファイル関数を触る機会というのは、そう多くはないと思います。
用途としては、
・コピートレード
・アービトラージ
・外部ソフトを使った分析(Rとか)
に限られてくるので、一般的な自動売買ソフトを開発しているだけではお目にかかることはないでしょう。
全体のタスク
全体的にやるべきタスクは下記になります。
■コピー元(配信側)
・オーダーをファイル関数でファイルに記録する
■コピー先(受信側)
・ファイル関数でトレードデータを引っ張ってくる
・引っ張て来たデータを現在のポジション情報と照らし合わせる
・照らし合わせた内容に従ってエントリーor決済する
FXの注意すべき仕様
コピーツールを作る前に、MT4の仕様上の話をしなくてはなりません。複数のMT4業者を利用したことがある方はご存じかと思いますが、業者によって通貨ペアの名称が若干違います。
ある業者ではUSDJPY、ある業者ではUSDJPYm、またある業者はusdjpyだったり…
大文字小文字が異なっていたり、通貨ペアの後(サフィックス)に変な記号を勝手につけられることがあります。
これをちゃんと処理しないとバグに繋がります。
トレードデータを送信する際にはこれらの邪魔な記号を消して、6文字に統一すると処理が楽になります。
これは、文字列関数を使えば一発です。
1 |
symbolSuffix = StringSubstr(Symbol(),6); |
StringSubstrは指定文字列の指定番号以降の文字列を抽出することができるので、適当にsymbolSuffixなどの変数を文字列型で宣言して、横に置いておきましょう。
データをやり取りする際には、このsymbolSuffix を外して統一し、オーダーを出す際には再び必要になります。(あとでくっつけます)
ストップレベルによるバグの回避
またFX業者によって仕様が異なるものとして、ストップレベルがあります。
ストップレベルとは、予約注文を出す際に最も近くに出せるレートの幅のことです。業者としては予約注文(リミットオーダー、ストップオーダー)は、現在レートからある程度離れたところに置いてほしいわけですが、その最小許容幅がストップレベルです。
しかし、今回はレートの厳密性よりも、スピードを重視したコピートレードシステムを開発したいので、すべて成行注文でオーダーします。(トレードをコピーする側のMT4の話)
もちろん、コピー元のトレードは成行以外で自由にやっていただいてOKです。また、スリッページもあまり狭くすると、リクオートが多発してエントリーしなくなるので、なるべくデフォルトは広めにしましょう。
為替と仮想通貨以外のシンボル(ゴールド、インデックス、米、石油など)は、FX業者によってシンボル名が全然違ったり、そもそもチャートがないこともあるので、今回は為替と仮想通貨だけを考えます。
コピー元のプログラムを作る
まずは、コピー元のプログラムを作ります。トレードを受信する側と比較するとかなり簡単です。コピー先に送る情報として最低限必要なのは、通貨ペア、売り買いの方向、オーダーを識別するためのチケットナンバーです。
この三つがあれば、とりあえずコピーは可能です。
ファイル関数は文字列型でしか格納できないので、これらを一つの情報にまとめます。
チケット番号:通貨ペア(6文字):売買@
で一つのオーダーの情報にします。
情報を区切る記号(:@)はなんでもOKですが、業者がシンボル名に絶対に使用しない記号にしましょう。WEB系の言語やAPIを触ったことがある方ならJSON形式の方が分かりやすい方もいるかもしれません。(ただし、MQL4/MQL5にJSONをエンコード、デコードする関数はありません。)
オーダーが複数になった場合には、
チケット番号:通貨ペア(6文字):売買@チケット番号:通貨ペア(6文字):売買@…
という風にどんどん連結していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
string TradingData() { string PreTradingData = ""; string PositionSymbol = ""; for(i=0;i<OrdersTotal();i++) { R = OrderSelect(i,SELECT_BY_POS); if( OrderType() == OP_BUY || OrderType() == OP_SELL) { //only forex PositionSymbol=StringSubstr(OrderSymbol(),0,6); PreTradingData += OrderTicket()+":"+PositionSymbol+":"+OrderType()+"@"; } } return(PreTradingData); } |
あとはこの関数が吐き出すトレード情報の文字列をファイル関数でファイルに書き込みます。
ファイル名はなんでもOKです。
ここではCoypingTradingData.txtとしています。
ただし、ファイル名に変な記号(カンマなど)を入れると、OSが変な拡張子として認識したりするので、特殊記号はいれないでください。
ファイル関数の使い方
ファイル関数は通常の変数などと違って、
”○○という名前のファイルを(書き込み専用or読み込み専用)開きます”と宣言します。 FileOpen
次に、
”さっき指定したファイルに書き込みますor読み込みます” FileWrite, FileRead
最後に、
”今開いているファイルを閉じます” FileClose
という手順を踏まなければなりません。
JavaやPHPなど他の高級言語を触ったことがある場合はそれほど違和感はないと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
string FileName = "CopyingTradingData"+".txt"; FileHandle = FileOpen(FileName,FILE_WRITE|FILE_TXT|FILE_COMMON); int TryCount = 0; while( FileHandle == INVALID_HANDLE && TryCount < 10) { TryCount++; FileHandle = FileOpen(FileName,FILE_WRITE|FILE_TXT|FILE_COMMON); } if( FileHandle != INVALID_HANDLE ) { FileWriteString(FileHandle,TradingData()); FileClose(FileHandle); } |
ファイル関数はMT4ターミナルの外部フォルダにアクセスするので、PCの状態によってはアクセスに失敗することがあります。(メモリがいっぱいとか、ビジー状態とか)そのため、成功するまで最大10回繰り返す仕様にしています。
また、コピー元がファイルに書き込みをするタイミングと受信側ファイルを読み込むタイミングが重なると、Windowsの仕様上例外処理が必要になります。(対象のファイルに対して読み込みと書き込みを同時に行うことはできない。)
【追記】ここの処理は後の記事でwhile文からfor文に書き換えています。あとスリープも入れています。
※一応MQLリファレンスにはFILE_SHARE_WRITE, FILE_SHARE_READという指定もあるのですが、これは機能していないので、通常版で記述しています。
ここで宣言していない変数は、あとでコードをまとめたときに別の場所で宣言しているので、コピペする際には全体のコードから取得してください。
ここで一番重要なのはファイル関数を使う際にFILE_COMMONを指定することです。これを入れることでフォルダアクセスが一つ上層になり、ほかのMT4からもアクセス可能な領域にデータが保存されます。
この保存場所は「C:UsersAppDataRoamingMetaQuotesTerminalCommonFiles」です。
もしこのディレクトリの書き込み権限がない場合はエラーになりますので、書き込み権限を与えておいてください。(デフォルトでは通常書き込みOKになっているはずです。)
コピー先:受信側のコードを書く
コピー先(受信側)のコードに入ります。
コピー先でまずやらなくてはならないことは、ファイル関数でファイルからトレード情報を引っ張ってくることです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
string GetTradingData() { string FileName = "CopyingTradingData"+".txt"; FileHandle = FileOpen(FileName,FILE_READ|FILE_TXT|FILE_COMMON); TryCount = 0; while( FileHandle == INVALID_HANDLE && TryCount < 10) { TryCount++; FileHandle = FileOpen(FileName,FILE_READ|FILE_TXT|FILE_COMMON); } if( FileHandle != INVALID_HANDLE ) { FileData = FileReadString(FileHandle); FileClose(FileHandle); } else FileData = "FAIL"; return(FileData); } |
【追記】while文をfor文に直しています。(OnTimerでもOKです。)
FILE_READを使えば、ファイルの情報を取得することができます。ここらへんはヘルプでもフォーラムでも載っているので、詳しくはそちらを見てください。
この関数が吐き出す情報は、
チケット番号:通貨ペア(6文字):売買方向@…
となっているので、
この情報を扱いやすいように分割しなければなりません。
FileDataという変数にとりあえずこの文字列を格納し、
文字列関数を使ってそれぞれ分割した文字列を変数に入れます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
FileData = GetTradingData(); string OrderData[100] = {""}; string OrderData_ticket[100] = {""}; string OrderData_symbol[100] = {""}; string OrderData_type[100] = {""}; int HostPositionNum = 0; for(i=0;i<100;i++) { if( FileData == "" || FileData == "@") break; HostPositionNum++; OrderData[i] = StringSubstr(FileData,0,StringFind(FileData,"@")+1); StringReplace(FileData,OrderData[i],""); //separate the trading data OrderData_ticket[i] = StringSubstr(OrderData[i],0,StringFind(OrderData[i],":")+1); StringReplace(OrderData[i],OrderData_ticket[i],""); OrderData_symbol[i] = StringSubstr(OrderData[i],0,StringFind(OrderData[i],":")+1); StringReplace(OrderData[i],OrderData_symbol[i],""); OrderData_type[i] = OrderData[i]; StringReplace(OrderData_ticket[i],":",""); StringReplace(OrderData_symbol[i],":",""); StringReplace(OrderData_type[i],"@",""); ..... |
MQL4はMQL5と違って構造体は使えないので、OrderData_ticketのような変数名で代用しています。
【追記】バージョンアップでMQL4でも構造体が使えるようになりました。
for文の100というのは「配信元が100以上のポジションを同時に保有しないという前提の数字」です。
文字列の検索が終わり次第、breakするので、実際に100回計算することはないと思います。
StringReplaceとその使い方
StringReplace関数は、特定の文字列の検索文字列を別の文字列に置き換えます。
例えば、
string Moji = “あいうえお”;
StringRelace(Moji,”あ”,”か”);
とすれば、
Moji == “かいうえお”
となるように、一部だけを置換することができます。
ここら辺はコードを読んで理解するよりも自分で考えた方が早いと思います。
要するに、
1124310:USDJPY:0@1123311:EURUSD:0@1123412:GBPUSD:0@1123413:AUDJPY:0@1123414:CADJPY:0@1123415:USDJPY:1@…
のような形でデータが並んでいるので、それぞれデータごとに変数に格納できればOKです。
これでざっくり、コピーするべきトレードの情報が取得できたので、次は「そのトレード情報を現在のトレード状態と照らし合わせる」箇所に入ります。
ポジションを照らし合わせる
取得したコピー元のトレード情報を、現在のトレード状態と照らし合わせる作業をします。
最も容易に想定されるバグとして、何度も同じポジションを取ったり決済したりを繰り返す、というものがあります。
これを解消するには、コピー先のオーダーの履歴に、すでにそのオーダーを取得したことがあるかどうかを調べる作業が必要です。
まだオーダーをコーディングしていませんが、受信側がオーダーをコピーしてポジションを取得する際に、そのオーダーのコメントに親のオーダーのチケット番号を入れます。
受信側が持っているオーダーのコメント と 配信側が持っているオーダーのチケット番号が一致していれば、
それはコピートレードで対応されたオーダーということになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//check if you had this position in the past bool CheckHadThisPositionPast(string TheComment) { bool ThisPositionHadAlready = false; for(j=0;j<OrdersHistoryTotal();j++) { R = OrderSelect(j,SELECT_BY_POS,MODE_HISTORY); //if you had this position already if( StringFind(OrderComment(),TheComment,0) > 0 ) { ThisPositionHadAlready = true; break; } } return( ThisPositionHadAlready ); } |
(もっと良い関数名があればなぁ….)
【追記】後の記事では、コメントがFX業者によって勝手に編集される可能性を考慮して、
受信側のオーダーのコメントに配信側のチケット番号を入れるのではなく、
受信側のオーダーのマジックナンバーに配信側のチケット番号を入れています。
MQL4ではOrderSelectの際にMODE_HISOTRYを選択すると、決済済み、削除済みのオーダーを参照できます。
ただし、参照できる決済済み削除済みのオーダーは、MT4で表示されている履歴に依存するので、表示されている履歴が短い場合は、その分しか参照できません。
逆に全履歴がターミナル上で表示されている場合には、全履歴を取得することができます。
(今後のアップデートで変わるかもしれませんね。)
今回のコピートレードでは、コピーしたオーダーのコメントに、コピー元のチケット番号を入れる仕様にしました。
これによって、そのオーダーを過去にコピーしたことがあるのかどうかがわかります。
ちなみにMT5のネッティングアカウントの場合、オーダーコメントは比較的変わってしまうので、この手法は使えません。
オーダーの反映の状態は二つ
また、履歴上のオーダーの検索と同時に、コピーするべきトレードが現在コピーされているかどうかを検索する必要があります。
A・今、コピーするべきオーダーを持っているか調べる
B・過去にコピーするべきオーダーを持っていたか調べる
つまりこの2パターンです。
何を想定しているかというと、せっかくコピーしたトレードを裁量で決済されてしまったパターンです。
この場合、
もう一度ポジションを保有するか、それとも、トレーダーを信用してポジションを見送るか、の選択になるのですが、
せっかくトレーダーが何かの意図をもって決済したものを、間髪入れずに再ポジションしたら、クレームになりそうな気がします。
(もちろん誤って決済してしまった、という可能性もあると思います。)
しかし、過去のトレードも参照しておけばバグの可能性も減らせることができるので、今回は
「コピーしたポジションをトレーダーが裁量で決済した場合、再エントリーしない」仕様にします。
先ほどと同じように、今度は”現在もっているポジション”とコピー元のトレード情報を照らし合わせます。(照らし合わせる関数を作ります)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
bool CheckHaveThisPositionNow(string CheckComment) { bool ThisPositionHaveNow = false; for(j=0;j<OrdersTotal();j++) { R = OrderSelect(j,SELECT_BY_POS,MODE_TRADES); //if you have this order if( StringFind(OrderComment(),CheckComment,0) > 0 ) { ThisPositionHaveNow = true; break; } } return(ThisPositionHaveNow); } |
ちょっとややこしいかもしれませんが、やっていることは、
A・今、コピーするべきオーダーを持っているか調べる
B・過去にコピーするべきオーダーを持っていたか調べる
ことが必要で、そのための独自関数を作っただけです。
これまでで
「”過去に”コピー元のトレードを持っていたか調べる関数」と「”現在”コピー元のトレードを持っているか調べる関数」を作りました。
次はまず、それぞれのオーダー情報に対して、2つの関数を適応した際の組み合わせ4パターンを考えます。
オーダーの状態とパターンを調べる
[]パターン1
履歴にコピー元のトレードを持っていて、今は持っていない
→一度コピーはしたが裁量で決済された
→何もしない
[]パターン2
履歴にコピー元のトレードを持っていて、今も持っている
→ありえないパターン。(バグってる)
→何もしない
[]パターン3
履歴にコピー元のトレードがなく、今もない
→直ちにエントリー
[]パターン4
履歴にコピー元のトレーがないが、今現在持っている
→現在配信元がポジションを持っている
→何もしない
こうしてみると、実際にエントリー処理するべきなのはパターン3だけです。
決済処理は、
・受信側であるポジションを持っている
&&ファイル関数のデータにそのポジションに対応するオーダーがない
場合に決済します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
if( !CheckHadThisPositionPast(OrderData_ticket[i]) && !CheckHaveThisPositionNow(OrderData_ticket[i]) ) { OrderSending(OrderData_ticket[i],OrderData_symbol[i],OrderData_type[i]); } 前回までで買い=0、売り=1としたのは、OP_BUY==0であり、OP_SELL==1であるためです。 買い=1、売り=-1の方が直観的ではありますが、0,1にすることによってそのままOrderSendが使えます。 void OrderSending(string TheTicketForComment, string TheSymbol, string Direction) { int OrderType_; double OrderRate; RefreshRates(); if( Direction == "0" ) { OrderType_ = OP_BUY; OrderRate = MarketInfo(TheSymbol+Ex,MODE_ASK); } else if( Direction == "1" ) { OrderType_ = OP_SELL; OrderRate = MarketInfo(TheSymbol+Ex,MODE_BID); } R = OrderSend(TheSymbol+Ex,OrderType_,Lot,OrderRate,Slippage,0,0,"at"+TheTicketForComment,Magic,0,clrYellowGreen); //we should check the return just in case. } |
OrderSendはOnTicke(Start)内にべた書きでもいいですが、一応関数化して使いましょう。
ファイル関数から取得したトレード情報は文字列型なので、オーダー関数を使うには必要に応じて型を変換する必要があることに注意してください。
また、ファイル関数の通貨ペアは6文字ですが、実際にこれからエントリーしようとする通貨ペアはFX業者によって6文字ではないかもしれません。(ex. USDJPY^, USDJPY-m)
通貨ペアの文字列は完璧に一致していないと、オーダーがはじかれるため、あらかじめ保存したExを使い、文字列を連結しています。
決済する必要がある場合の決済処理
決済処理をします。
決済は「ファイル関数から取得した文字列に既定のトレード情報がなくなったら」決済とします。
勘の良い方はお気づきだと思いますが、これ、比較的危ういです。
というのもファイル関数がファイルの取得に失敗した場合、返り値が””になって、全決済してしまう可能性があるからです。(実際にはないです。)
これを回避するにはファイル関数の参照エラーをきっちり分けてやればOKです。
つまり、
パターン1:ファイル関数が正常に作動し、トレード情報がまだある状態
パターン2:ファイル関数が正常に作動し、トレード情報が消えた状態(決済)
パターン3:ファイル関数が失敗した場合(エラー)
パターン2とパターン3をちゃんと区別して書けば、万事OKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//check if you shold close the position------------------------------------------ for(i=0;i<OrdersTotal();i++) { R = OrderSelect(i,SELECT_BY_POS); if( R ) { //if the position is not taken by this EA if( StringFind(OrderComment(),"at",0) < 0 ) continue; //check if the position is still holded by HOST bool HostHasPosition = false; for(j=0;j<HostPositionNum;j++) { if( StringFind(OrderComment(),OrderData_ticket[j],0) != -1 ) { //HOST still has this position HostHasPosition = true; break; } } //if host already closed this position if( !HostHasPosition ) { //then close CloseRequest(OrderTicket()); } } } void CloseRequest(int Ticket) { R = OrderSelect(Ticket,SELECT_BY_TICKET,MODE_TRADES); double OrderRate; if( OrderType() == OP_BUY ) OrderRate = MarketInfo(OrderSymbol(),MODE_BID); else if( OrderType() == OP_SELL ) OrderRate = MarketInfo(OrderSymbol(),MODE_ASK); else OrderRate = NULL; R = OrderClose(Ticket,OrderLots(),OrderRate,Slippage,clrWhite); if(!R) Print("OrderClose was rejected"); } |
for文の中でOrderCloseを使うべきではない
話が少しそれますが、多くのプログラマやEA開発代行業者が決済の際にfor文とOrdersTotal()を併用しています。
しかし、これはちょっとあやういです。
というのも、
OrdersTotal()は現在保持しているオーダーの総数です。しかし、そのfor文の中でオーダーを決済してしまえば、処理の途中でOrdersTotalの数が変わってしまいます。
for文が本来回るはずだった回数が減っていき、ポジションの番号の順番も変わる可能性があります。
今のところはたまたま、RefreshRate()するかOnTickから一度抜け出すまではOrdersTotalに入っている値が変わらないという芳しくない”仕様”がこのバグの防波堤になっていますが、将来的にこの”仕様”が改良される可能性はかなり高いです。
そのため、決済するべきオーダーのチケット番号は一度なにがしかの変数で保管して、for文を抜けた後に決済処理した方が安全です。
オーダーコメントはFX業者に勝手に書き換えられる
話を戻します。
注意するべきは、業者によってオーダーコメントに変な文字列が追加されるケースがあることです。
例えばオーダーコメントを”at12345678″としたつもりでも、
業者側が勝手に”at12345678tp”のようないらない文字列を付け足すことがあります。
(タイミングはまちまちです。)
そのため、
if(オーダーコメント==調べるチケット番号)
としてしまうと、正常に処理されない可能性があります。
ここはStringFindを使って、オーダーコメントの中に指定のチケット番号が”含まれているかどうか”
を使いましょう。
if(StringFind(オーダーコメント,調べるチケット番号,0) > 0 )
です。
検索文字列が見つかった場合、その文字列の位置を返すので、0以上の場合は合致ということになります。
もし最後まで合致しなければコピー元がポジションを決済したということになり、オーダーを決済します。
【追記】FX業者によっては決済後のオーダーコメントを”tp”だけにしたりする場合があるので、オーダーコメントではなくマジックナンバーに親のチケット番号を入れる仕様に変更しました。
これでコピーツールの全体が終わりました。次回は全体のコードとそれぞれのケースについて考察します。
.
改良の余地として、
・予約注文もコピーする
・SL,TPもコピーする
・配信元のロット比率も反映できるようにする
・コピーしようとしたタイミングで配信元が約定してから○○分以上経っているポジションはコピーしない
などの追加機能を付けても良いと思います。
バグ取りと改良
前回までに書いたコードでは、ごくまれに受信側が勝手にオーダーを決済してしまうというバグがありました。
これはファイルにアクセスする際に書き込みと読み込みがまったく同じタイミングで行われることで、読み込み結果が空白となり、それをEAが”ポジションなし”と判断していただめです。
解決策1:書き込み、読み込みするファイルを複数に分けて、読み込み結果が同じ時だけ処理する
↑
コーディングがめんどい、泥沼になるような気がする…
解決策2:書き込み、読み込みのタイミングをずらすために、ローカルタイムの秒数の剰余で実行をずらすとか
↑
コピー処理が早いことが唯一のメリットなのに、それが失われると作る意味がない…
しかし、
とりあえず「ポジションがない状態」と「読み込みに失敗した状態」を区別して、
ファイルにアクセスできない場合にはSleepを入れてちょっと待つ仕様にしたところ、問題は解決しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
string GetTradingData() { FileData = "failed"; for(k=0;k<10;k++) { FileHandle = FileOpen(FileName,FILE_READ|FILE_TXT|FILE_COMMON); if( FileHandle == INVALID_HANDLE ) Sleep(100); else { FileData = FileReadString(FileHandle); FileClose(FileHandle); break; } } return(FileData); } |
(これは以前while文で書いていた、ファイルのトレードデータを読み込む関数です)
より高速にコピーさせる
また、やっぱりコピーの反映が遅いのが気になります。EAをセットしている通貨ペアチャートだけが低速で、他のチャートが頻繁に動いているとき、コピーの反映が遅いというのはやっぱり良くないと思います。
他のパターンとしても、コピーする通貨ペアが頻繁に動いていて、セットしている通貨ペアが鈍い可能性も考えられます。
そこで、OnTickの中に再帰的なループを入れました。
While文で延々とループするとメモリを圧迫したり、フリーズしたりする可能性があるのですが、妥協案として、
100回ループしてOnTickで再読み込みという手を取りました。
1 2 3 4 5 6 7 8 |
void OnTick() { //--- for(l=0;l<100;l++) { Sleep(100); RefreshRates(); .... |
下記はその結果です。
前よりも反映速度が格段に上がっています。(ごく稀に遅くなる可能性は内包していますけどね。)
リバーストレードできるようにする
つづいてはリバーストレードができるように改良します。
端的に言うと、コピー元と逆向きのポジションをコピーできるようにするということです。
と言っても、エントリー直前のエントリーの向きをパラメータによって場合分けすればいいだけなので、
てきとうにReverseModeという名前のパラメータを作って、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
if( Direction == "0" )//コピー元のポジションの方向 { if( !ReverseMode ) { OrderType_ = OP_BUY;//取るポジションの向き OrderRate = MarketInfo(TheSymbol+Ex,MODE_ASK); } else { OrderType_ = OP_SELL; OrderRate = MarketInfo(TheSymbol+Ex,MODE_BID); } } else if( Direction == "1" ) { if( !ReverseMode ) { OrderType_ = OP_SELL; OrderRate = MarketInfo(TheSymbol+Ex,MODE_BID); } else { OrderType_ = OP_BUY; OrderRate = MarketInfo(TheSymbol+Ex,MODE_ASK); } } |
ちょっとわかりにくいかもしれませんが、左はFXPROのデモ口座で右はXMのデモ口座です。左でトレードを行うと右で逆向きのトレードをすぐさま入れています。
のようにすればOKです。(ニュアンスだけわかってもらえればOKです)
関連記事
-
サーバータイプのMT4MT5互換コピートレードツール(EA)
この記事ではWEBサーバーを経由するMT4/MT5のコピートレードシステムの作り方について解説します
-
[MT4⇔MT5] コピートレードツール(EA) 使い方 無料ダウンロード
これまでに開発したコピートレードツールの紹介です。開発の経緯順のため、最新版は一番下にあります。また
コメント
スキャルピングでも早くコピートレードできるソフトを探してます。宜しくお願いしますm(__)m
ダウンロードして使わせてもらいます。
親のアカウント番号とは口座番号でしょうか?
メールの場所がわからずこちらに質問しました。
変則的な決済に対応できますでしょうか?
例えば、2万通貨のポジションがあり、決済は1万通貨で2回に渡る場合です。
よろしくお願い致します。
>LotForReceiver: 受信(コピー先)のMT4にセット>する場合、コピーするトレードのロットを入力します。
ロットを親と同じにする方法はありすますか?
よろしくお願いいたします。
横瀬兼元様。お世話になります、野田と申します。突然ですが、私事ですが結構長くFXをやっているのですがなかなか勝てずいろいろなスクールやら詐欺商材にひっかかったりして結局私では勝てないというのが結論です。それで自分で勝てないならEAに稼いでもらおうと考えました。それでEAの勉強を始めようと本を買ってはみたのですが勉強ぎらいで3日坊主の性格でさっぱり進みません。それでも1つだけどうしても試してみたい勝てそうなロジックがあるのですが自分では作れないのでプロの方にお願いしようと思ってEA制作の方のページを見ていてこちらのページにたどり着きました。EA制作をお願いするにはこの方しかないと確信してこのメッセージを書いています。先生のお時間に余裕のある時で構いませんのでEAを作っていただけないでしょうか。ただFXでさんざん負けてしまってあまりお金に余裕がないので高額だとすぐにはお願いできません。ロジックはそれほど複雑なものではないのですがもしEAを作っていただけるとしたらおいくらぐらいなのでしょうか。お返事いただけましたら幸いです。よろしくお願い致します。
有効なEAだと思います。試してみます。
こんばんは。EAの無料プレゼントは今でも対応していただけてますか?
まだ対応しています。
コピー元のXAUUSDからコピー先へコピートレードされるんですが
肝心のコピー先には取り扱いのない商品なのでエラーが出ます
どうすればよいでしょうか?
リミテッド版の制限は何ですか?宜しくお願いします。
試用版は1週間の期間限定仕様です。
はじめまして
コピートレードツールに興味を持ちました。
そこでご質問があります。
A業者のMT4で売り注文をすると、それに連動してB業者のMT5で買い注文を入れる両建てアービトラージEAも作れるものでしょうか?
仕様は、ワンクリック注文で同時に両建てができて、どちらかがロスカットされたら、もう一社の方は連動して利確するEAです。短期のボーナスアービトラージを狙えるかと思いました。
大変お忙しいと思いますが、そのようなEAの無料配布があるとうれしいです。
MT4/MT5クロスプラットフォーム対応の業者間アービトラージEAはすでにありますので、サイト内を探してみてください。ちなみに、業者側にボーナスアービトラージだとばれるような取引をすると無効にされるのでバレないようにうまくやる必要があります。
初めまして。
最新版 MT4⇔MT5 コピートレードツール (完全上位互換版)
のソースコード提供お願いしたいです。
サポートなしでもいいならEAのソースコードの提供OKです。
↑
このような記載があったので。もちろんサポートは必要ありません。
自分でコードを分析したいと思っています。