MQL5 と MQL4 の選択とナビゲーションユーティリティ: 「ホームワーク」タブの追加とグラフィックオブジェクトの保存
イントロダクション
前回の記事では、適切なエントリポイントを持つシンボルを並べ替えて選択するためのユーティリティを開発しました。 以前、シンボルをさまざまなパラメータで分類したり、特別にデザインされたボタンを使用してシンボルをナビゲートしたりする方法を行いました。 しかし、この画像は、シンボルの選択の面でそれほどバラ色ではありません。 現在、地球の環境に負の影響がある紙に選択されたツールのティッカを書かなければなりません。
この記事では、常に作成する必要がないように、チャート上に作成されたグラフィカルオブジェクトを自動的に保存する方法を学びます。
条件付きコンパイル関数の適用
まず、MQL4 言語へのユーティリティの移植をしましょう。 前回の記事では、プログラムを MQL4 で動作させるために、コードブロックを別のものに置き換えました。 すると、より困難なタスクに直面します。 1つの言語で開発を行うことができます。その後、常に必要なものと mql4 で動作していないコードのブロックを置き換えます。またはMQL5 と MQL4 で同時に2つのプログラムを開発することができます。:
どちらのオプションも最適ではありません。 MQL4 で動作していないブロック (各バージョンの) を絶えず置き換えるか、または別の言語でユーティリティに変更を実装するために変更したコードの部分を念頭に置いておく必要があります。
したがって、別のアプローチを使用します。 MQL5 と MQL4 はどちらも条件に応じて1つのコードブロックの実行を許可する条件付きコンパイルディレクティブをサポートします。 ディレクティブの中には、現在の MQL 言語バージョンに応じて実行される構造があります。 その主な構文は次のとおりです。
#ifdef __MQL5__ //MQL5 でのみ実行されるコードブロック #else //MQL4 でのみ実行されるコードブロック #endif
これを使用して、 checkSYMBwithPOS関数が MQL5 と MQL4 の両方で正しく動作するようにしましょう。
bool checkSYMBwithPOS(string name){ //ポジションとオーダーを持つシンボルの非表示 bool isskip=false; if( noSYMBwithPOS ){ #ifdef __MQL5__ //すべてのオープンポジションの一覧を表示します int cntMyPos=PositionsTotal(); for(int ti=cntMyPos-1; ti>=0; ti--){ //現在のシンボルのポジションがある場合はスキップ if(PositionGetSymbol(ti) == name ){ isskip=true; break; } } if(!isskip){ int cntMyPosO=OrdersTotal(); if(cntMyPosO>0){ for(int ti=cntMyPosO-1; ti>=0; ti--){ ulong orderTicket=OrderGetTicket(ti); if( OrderGetString(ORDER_SYMBOL) == name ){ isskip=true; break; } } } } #else //すべてのオープンポジションの一覧を表示します int cntMyPos=OrdersTotal(); for(int ti=cntMyPos-1; ti>=0; ti--){ if(OrderSelect(ti,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderSymbol() == name ){ isskip=true; break; } } #endif } return isskip; }
さらにこの記事で、この構築を使用して MQL4 で動作しないコードブロックを移植します。
ホームワークタブ
環境を保護するために、以前に選択したシンボルのみを表示する3つのタブを作成します。 これらのタブには、 Long、Short、Rangeの名前をつけます。 もちろん、上、下、またはレンジなシンボルだけを追加する必要はありません。 裁量で使用することができます。
その結果、チャートユーティリティは、4つのボタンで構成されたさらに別の行の関数で起動します。: Allボタンと3つの前述のもの。
Allボタンはデフォルトで押された状態です。これは、フィルタに適合するすべてのシンボルのリストが下に表示されることを意味します:
したがって、オブジェクティブが設定されています。 これを実装する必要があります。 これを行うには、ユーティリティの小さなパートを書き直す必要があります。
タブの内容を格納する配列。 まず、タブの内容を格納するための変数を追加してみましょう。 以前は、1つのタブしかなく、その内容はarrPanel1変数に格納されていました。 他のタブにも同様の変数を追加します:
//適切なタブに表示される配列シンボル:
CArrayString arrPanel1;
CArrayString arrPanel2;
CArrayString arrPanel3;
CArrayString arrPanel4;
さらに、ループ内のタブにアクセスできるように、別の配列を作成します。 この配列は、以前に作成された4つの配列すべてへのポインタを格納します。
//すべてのタブを結合するための配列 CArrayString *arrPanels[4];
OnInit() 関数で配列を初期化してみましょう。
arrPanels[0]=&arrPanel1; arrPanels[1]=&arrPanel2; arrPanels[2]=&arrPanel3; arrPanels[3]=&arrPanel4;
Tab headers.タブでのタスクはループ内で実行されるので、タブ名をループからアクセスするのにも最適です。 したがって、タブ名を使用して配列を作成してみましょう。
//タブ名配列 string panelNames[4]={"All", "LONG", "SHORT", "Range"};
補助変数。 もう1つの変更点は、 panel1val変数です。 名前をpanelvalに変更しました。 表面的な修正ですが、注意が必要です。
現在アクティブなタブのインデックスを含むcur_panelパラメータも追加されました 変数の型は uchar です。 つまり、4つのタブしかないので、0 ~ 255 の値を取ることができます。
デフォルトでは、最初のタブ (配列内のインデックス0を持つ) がアクティブになっています。 したがって、 OnInit()関数の変数に0の値を割り当てる文字列を追加します。 最後に、 OnInit()関数は最終的な形式を取ります:
int OnInit() { //現在アクティブなタブのインデックス cur_panel=0; //タブの配列を初期化します。 arrPanels[0]=&arrPanel1; arrPanels[1]=&arrPanel2; arrPanels[2]=&arrPanel3; arrPanels[3]=&arrPanel4; start_symbols(); //---タイマーの作成 EventSetTimer(1); //--- return(INIT_SUCCEEDED); }
その他の変更。 実装されたすべての変更を一覧表示することは、あまり重要でないため、不要です。 では、主な変更に移りましょう。 小さな変更については、新しいユーティリティのソースコードと前の記事に添付されているものを比較することによって、自分で検出することができます。
主な変更点は、主に新しいタブの表示に関連するものです。 ループでタブで動作することにしました。 では、その方法を見てみましょう。
タブ付きの行があるので、何とか表示する必要があります。 これを実現するために、別の関数を作成します。 そのコードは以下に提供されます:
void show_panel_buttons(){ int btn_left=0; //タブを表示するために可能な最大 x 軸座標を定義します。 int btn_right=(int) ChartGetInteger(0, CHART_WIDTH_IN_PIXELS)-77; string tmpName=""; for( int i=0; i<ArraySize(panelNames); i++ ){ //新しいボタンの開始座標が可能な最大値を超える場合は、 //新しい行に移動します。 if( btn_left>btn_right-BTN_WIDTH ){ btn_line++; btn_left=0; } //[ホームワーク] タブにシンボルが付いている場合は、その番号を追加します。 //タブ名に tmpName=panelNames[i]; if(i>0 && arrPanels[i].Total()>0 ){ tmpName+=" ("+(string) arrPanels[i].Total()+")"; } //タブボタンの表示 ObjectCreate(0, exprefix+"panels"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_COLOR,clrBlack); ObjectSetString(0,exprefix+"panels"+(string) i,OBJPROP_TEXT,tmpName); ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_SELECTABLE,false); //[ボタン] タブが現在アクティブな場合は、 //押す if( cur_panel == i ){ ObjectSetInteger(0,exprefix+"panels"+(string) i,OBJPROP_STATE, true); } btn_left+=BTN_WIDTH; } }
シンボルボタンを表示するshow_symbols関数を呼び出す前に、 start_symbols関数で呼び出します。
show_symbols関数自体も変更されていますが、若干異なります。 現在アクティブなタブにあるシンボルのボタンだけを表示します:
//配列内の各シンボルのチャート上にボタンを表示します。 //現在アクティブなタブ //ボタンにシンボル名を書きます for( int i=0; i<arrPanels[cur_panel].Total(); i++ ){ if( btn_left>btn_right-BTN_WIDTH ){ btn_line++; btn_left=0; } ObjectCreate(0, exprefix+"btn"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XDISTANCE,btn_left); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_COLOR,clrBlack); ObjectSetString(0,exprefix+"btn"+(string) i,OBJPROP_TEXT,arrPanels[cur_panel].At(i)); ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_SELECTABLE,false); if( !noSYMBwithPOS || cur_panel>0 ){ if( checkSYMBwithPOS(arrPanels[cur_panel].At(i)) ){ ObjectSetInteger(0,exprefix+"btn"+(string) i,OBJPROP_BGCOLOR,clrPeachPuff); } } btn_left+=BTN_WIDTH; }
タブに記号を追加するためのボタン ここで、選択したタブにシンボルを追加する必要があります。 新しいボタン、開いているチャートページ上の Add LONG、Add SHORT、Add Range の助けを借りて行います。
前回の記事で、目的のシンボルボタンをクリックする関数を実装したことを思い出してください。 シンボルをクリックすると、左下隅にあるシンボルリスト全体をナビゲートするためのボタンブロックを含むチャートが開きます。 このブロックにボタンを追加します。 このシンボルが対応するタブにあるかどうかによって、ボタンはタブに追加するか、またはそこから削除します。
ボタンブロックは、 createBTNS関数を使用して表示されます。 新しいボタンを追加するループを追加します。
for( int i=ArraySize(panelNames)-1; i>0; i-- ){ isyes=false; if(arrPanels[i].Total()){ for(int j=0; j<arrPanels[i].Total(); j++){ if( arrPanels[i].At(j)==name ){ isyes=true; break; } } } if( isyes ){ ObjectCreate(CID, exprefix+"_p_btn_panelfrom"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_XDISTANCE,110); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_YDISTANCE,tmpHeight); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_CORNER,CORNER_LEFT_LOWER); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_SELECTABLE,false); ObjectSetString(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_TEXT,"Del "+panelNames[i]); ObjectSetInteger(CID,exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_BGCOLOR,clrPink); }else{ ObjectCreate(CID, exprefix+"_p_btn_panelto"+(string) i, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_XDISTANCE,110); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_YDISTANCE,tmpHeight); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_XSIZE,BTN_WIDTH); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_CORNER,CORNER_LEFT_LOWER); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_SELECTABLE,false); ObjectSetString(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_TEXT,"Add "+panelNames[i]); ObjectSetInteger(CID,exprefix+"_p_btn_panelto"+(string) i,OBJPROP_BGCOLOR,clrHoneydew); } tmpHeight+=25; }
新しいボタンを押して有効にするには、ボタンステータスのチェックコードをOnTimer()関数に追加します。 すべては、前回の記事で行ったのと同じです。
for( uchar i=1; i<ArraySize(panelNames); i++ ){ //[タブから削除] ボタンが押された場合は、最初にシンボルを削除し、シンボルチャートを再度開きます。 if(ObjectGetInteger(curChartID[tmpCIDcnt-1],exprefix+"_p_btn_panelfrom"+(string) i,OBJPROP_STATE)==true ){ delToPanel(i, ChartSymbol(curChartID[tmpCIDcnt-1])); curchart(); return; } //[タブに追加] ボタンが押された場合は、最初にシンボルを追加し、次のシンボルチャートを開きます。 if(ObjectGetInteger(curChartID[tmpCIDcnt-1],exprefix+"_p_btn_panelto"+(string) i,OBJPROP_STATE)==true ){ addToPanel(i, ChartSymbol(curChartID[tmpCIDcnt-1])); nextchart(); return; } }
delToPanel関数は、最初に選択したタブからシンボルを削除してから、ユーティリティが起動されたチャート上のすべてのシンボルボタンを更新するか、ヘッダーボタンのみをアップデートします。
void delToPanel(uchar num, string name){ //配列全体に沿って移動し、最初の要素を削除します。 //シンボルに似た名前で for(int i=0; i<arrPanels[num].Total(); i++){ if( arrPanels[num].At(i)==name ){ arrPanels[num].Delete(i); break; } } //タブが現在開いている場合は、 if(num==cur_panel){ initial_btn_line(); //チャートから以前に作成したシンボルボタンを削除します。 ObjectsDeleteAll(0, exprefix); //更新されたシンボルリストを表示します。 show_panel_buttons(); show_symbols(); }else{ //他のタブが開いている場合は、ヘッダーボタンを更新するだけです。 upd_panel_title(); } }
addToPanel関数は、ちょうど考慮したものとは反対です。 タブにシンボルを追加します。 加えて、シンボルが他のタブに存在するかどうかもチェックします。 シンボルが存在する場合は、そこから削除されます。
void addToPanel(uchar num, string name){ //タブにシンボルを追加する arrPanels[num].Add(name); //他のタブからシンボルを削除する // if present for( int j=1; j<ArraySize(arrPanels); j++ ){ if(j==num) continue; for(int i=0; i<arrPanels[j].Total(); i++){ if( arrPanels[j].At(i)==name ){ if( panelval==i && i>0 ){ panelval--; } arrPanels[j].Delete(i); break; } } } if(num==cur_panel){ initial_btn_line(); //チャートから以前に作成したシンボルボタンを削除します。 ObjectsDeleteAll(0, exprefix); //シンボルリストを表示します。 show_panel_buttons(); show_symbols(); }else{ upd_panel_title(); } }
ユーティリティの起動時にタブの内容を保存 EAを誤って閉じた場合はどうなるでしょうか。 すべての努力は無駄になります。 すべての追加を再開する必要があるでしょうか。 ホームワークのタブに追加したシンボルリストがファイルに保存され、さらに開いたときに復元することを確認してみましょう。
選択したシンボルのリストを格納するために CArrayString 型オブジェクトを使用しました。 この型のオブジェクトの多くの利点の1つは、配列の内容全体をファイルに送信し、ファイルから配列を復元できるようにする標準的な方法です。 ユーティリティを閉じる前に、配列の内容をファイルに保存してみましょう。 つまり、標準のOnDeinit()関数に新しいsavePanels関数の呼び出しを追加する必要があります。
void savePanels(){ for( int i=1; i<ArraySize(arrPanels); i++ ){ fh=FileOpen(exprefix+"panel"+(string) (i+1)+".bin",FILE_WRITE|FILE_BIN|FILE_ANSI); if(fh>=0){ arrPanels[i].Save(fh); FileClose(fh); } } }
配列の内容は、標準のOnInit()関数で復元されます。
for( int i=1; i<ArraySize(arrPanels); i++ ){ fh=FileOpen(exprefix+"panel"+(string) (i+1)+".bin",FILE_READ|FILE_BIN|FILE_ANSI); if(fh>=0){ arrPanels[i].Load(fh); FileClose(fh); } }
現在のパラメータを識別するためのヘッダの追加
異なる相場でトレードする場合は、別の相場に切り替えるときに、常にユーティリティを調整する必要があります。 結局のところ、現在、相場開放後の最初の1時間半でブレイクスルーを待っているアメリカの株式相場をトレードする場合、長時間前に開かれたヨーロッパやロシアの相場の株式を必要としません。 同様に, ロシアの相場でトレードしている場合, 米国の株式を必要としません。
必要なシンボルだけに焦点を当てないようにするためには、異なる国の相場だけでなく、外国為替相場のパラメータの別々のセットを作成し、必要なときにセットファイルのそれぞれをアップロードすることが合理的です。 これは簡単で、数秒かかります。 ただし、現時点でアップロードされている設定を理解することは困難です。
パラメータの適用されたセットを表示するには、現在取り組んでいる相場に関する説明を書き留め、 cmtインプットを追加します:
input string cmt=""; //パラメータ (eng)
タブのボタンで行にこのコメントを表示します:
これを実現するには、すべてのボタンを表示した後、 show_panel_buttons関数に次のコードブロックを追加します。
//指定された場合にコメントを表示する: if(StringLen(cmt)>0){ string tmpCMT=cmt; ObjectCreate(0, exprefix+"title", OBJ_EDIT, 0, 0, 0); ObjectSetInteger(0,exprefix+"title",OBJPROP_XDISTANCE,btn_left+11); ObjectSetInteger(0,exprefix+"title",OBJPROP_YDISTANCE,BTN_HEIGHT*btn_line); ObjectSetInteger(0,exprefix+"title",OBJPROP_XSIZE,133); ObjectSetInteger(0,exprefix+"title",OBJPROP_YSIZE,BTN_HEIGHT); ObjectSetInteger(0,exprefix+"title",OBJPROP_FONTSIZE,8); ObjectSetInteger(0,exprefix+"title",OBJPROP_COLOR,clrGold); ObjectSetInteger(0,exprefix+"title",OBJPROP_BGCOLOR,clrNONE); ObjectSetInteger(0,exprefix+"title",OBJPROP_BORDER_COLOR,clrBlack); ObjectSetString(0,exprefix+"title",OBJPROP_TEXT,tmpCMT); ObjectSetInteger(0,exprefix+"title",OBJPROP_SELECTABLE,false); }
cmtインプットは、現行のパラメータ・セットを識別するだけでなく、「ホームワーク」タブ内のシンボル・リストを区切るのに役立ちます。 結局のところ、米国相場で機能するためのホームワークタブにシンボルを追加した場合、ロシアの株式相場で稼働するときにはそのシンボルは必要ありません。 パラメータのセットが異なるセットファイルには、[ホームワーク] タブの個別のリストも表示されます。
これを実装するには、ファイルに配列を保存し、ファイルから復元するコードを少し変更する必要があります。 例として、ファイルに保存するための変更された関数を考えてみましょう。
void savePanels(){ string tmpCmt=cmt; StringReplace(tmpCmt, " ", "_"); for( int i=1; i<ArraySize(arrPanels); i++ ){ fh=FileOpen(exprefix+"panel"+(string) (i+1)+tmpCmt+".bin",FILE_WRITE|FILE_BIN|FILE_ANSI); if(fh>=0){ arrPanels[i].Save(fh); FileClose(fh); } } }
グラフィックオブジェクトの保存
チャートで機能するために解決する必要があるもう1つの問題は、チャート上に作成したグラフィカルオブジェクトの自動保存と復元です。 チャートにレベルを設定した場合、チャートウィンドウを閉じて再度開くと、再び表示されることを期待します。 確かにシンボルチャートを開くたびにレベルの数十を配置する必要はありません。
これまでに書いたコードは、MQL5 と MQL4 の両方で同様にうまく機能します。 グラフィカルオブジェクトを保存および復元する関数ではありません。 MQL4 では、グラフィカルオブジェクトタイプとその個別のプロパティは数値型の定数で記述され、MQL5 はその列挙 (enum 型) を適用します。 そのため、ファイルに保存して復元するのは困難です。 少なくとも、一般的な意味でこのタスクに対処することができませんでした。 MQL4 のグラフィカルオブジェクトを保存する関数は、ここで大きな助けとなります。 理論的には、任意のグラフィカルオブジェクトを保存することができます (すべてのオブジェクトでテストしていないので、例外が可能です)。 MQL5 関数では、水平線、ラベル、テキストフィールドのみを使用できます。 他のグラフィカルオブジェクトを保存する必要がある場合は、自分で実装する必要があります。
MQL4. グラフィカルオブジェクトを保存するコードは MQL4 の方が簡単なので、この言語の関数から始めましょう。 ファイルに地理的なオブジェクトを保存するための関数:
void savechart(ulong id){ //グラフィカルオブジェクトを保存するのは、 // "作成されたグラフィカルオブジェクトの保存 " パラメータ = true if(saveGraphics){ //シンボル名を取得する string tmpName=""; if(cur_panel<ArraySize(arrPanels)){ tmpName=arrPanels[cur_panel][panelval]; } tmpName=clean_symbol_name(tmpName); StringReplace(tmpName, " ", ""); //グラフィカルオブジェクトの配列をクリアする saveG.Resize(0); //ユーザーが作成したすべてのグラフィカルオブジェクトとそのプロパティを配列に追加する int obj_total=ObjectsTotal((long) id); string name; string tmpObjLine=""; for(int i=0;i<obj_total;i++){ name = ObjectName((long) id, i); if( StringFind(name, exprefix)<0 && StringFind(name, "fix")<0 && StringFind(name, "take")<0 && StringFind(name, "stop loss")<0 && StringFind(name, "sell")<0 && StringFind(name, "buy")<0 ){ tmpObjLine=name; StringAdd(tmpObjLine, "|int~OBJPROP_TYPE~"+(string)(int) OBJPROP_TYPE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_TYPE)); StringAdd(tmpObjLine, "|int~OBJPROP_COLOR~"+(string)(int) OBJPROP_COLOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_COLOR)); StringAdd(tmpObjLine, "|int~OBJPROP_STYLE~"+(string)(int) OBJPROP_STYLE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_STYLE)); StringAdd(tmpObjLine, "|int~OBJPROP_WIDTH~"+(string)(int) OBJPROP_WIDTH+"~"+(string) ObjectGetInteger(id, name, OBJPROP_WIDTH)); StringAdd(tmpObjLine, "|int~OBJPROP_TIME~"+(string)(int) OBJPROP_TIME+"~"+(string) ObjectGetInteger(id, name, OBJPROP_TIME)); StringAdd(tmpObjLine, "|int~OBJPROP_TIMEFRAMES~"+(string)(int) OBJPROP_TIMEFRAMES+"~"+(string) ObjectGetInteger(id, name, OBJPROP_TIMEFRAMES)); StringAdd(tmpObjLine, "|int~OBJPROP_ANCHOR~"+(string)(int) OBJPROP_ANCHOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_ANCHOR)); StringAdd(tmpObjLine, "|int~OBJPROP_XDISTANCE~"+(string)(int) OBJPROP_XDISTANCE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_XDISTANCE)); StringAdd(tmpObjLine, "|int~OBJPROP_YDISTANCE~"+(string)(int) OBJPROP_YDISTANCE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_YDISTANCE)); StringAdd(tmpObjLine, "|int~OBJPROP_STATE~"+(string)(int) OBJPROP_STATE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_STATE)); StringAdd(tmpObjLine, "|int~OBJPROP_XSIZE~"+(string)(int) OBJPROP_XSIZE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_XSIZE)); StringAdd(tmpObjLine, "|int~OBJPROP_YSIZE~"+(string)(int) OBJPROP_YSIZE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_YSIZE)); StringAdd(tmpObjLine, "|int~OBJPROP_XOFFSET~"+(string)(int) OBJPROP_XOFFSET+"~"+(string) ObjectGetInteger(id, name, OBJPROP_XOFFSET)); StringAdd(tmpObjLine, "|int~OBJPROP_YOFFSET~"+(string)(int) OBJPROP_YOFFSET+"~"+(string) ObjectGetInteger(id, name, OBJPROP_YOFFSET)); StringAdd(tmpObjLine, "|int~OBJPROP_BGCOLOR~"+(string)(int) OBJPROP_BGCOLOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_BGCOLOR)); StringAdd(tmpObjLine, "|int~OBJPROP_BORDER_COLOR~"+(string)(int) OBJPROP_BORDER_COLOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_BORDER_COLOR)); StringAdd(tmpObjLine, "|double~OBJPROP_PRICE~"+(string)(int) OBJPROP_PRICE+"~"+(string) ObjectGetDouble(id, name, OBJPROP_PRICE)); StringAdd(tmpObjLine, "|string~OBJPROP_TEXT~"+(string)(int) OBJPROP_TEXT+"~"+(string) ObjectGetString(id, name, OBJPROP_TEXT)); saveG.Add(tmpObjLine); } } //配列の内容をファイルに保存します。 fh=FileOpen(exprefix+"_graph_"+tmpName+".bin",FILE_WRITE|FILE_BIN|FILE_ANSI); if(fh>=0){ saveG.Save(fh); FileClose(fh); } } }
ご覧の通り、オブジェクトのすべてのプロパティを保持していませんが、次のものはあります。OBJPROP_COLOR、OBJPROP_STYLE、OBJPROP_WIDTH、OBJPROP_TIME、OBJPROP_TIMEFRAMES、OBJPROP_ANCHOR、OBJPROP_XDISTANCE、OBJPROP_YDISTANCE、OBJPROP_STATE、OBJPROP_XSIZE、OBJPROP_YSIZE、OBJPROP_XOFFSET、OBJPROP_YOFFSET、OBJPROP_BGCOLOR、OBJPROP_BORDER_COLOR、OBJPROP_PRICE、OBJPROP_TEXT。 ユーティリティを使用しているときに、適用されているグラフィカルオブジェクトのいずれかが正しく保存されない場合、適用されたすべてのプロパティが保存されるとは限りません。 この場合、このタイプのグラフィカルオブジェクトもサポートされるように、不足しているプロパティをこの関数に保存するだけです。
次に、ファイルからグラフィカルオブジェクトをアップロードしてチャートに表示する関数を見てみましょう。
void loadchart(ulong id){ //グラフィカルオブジェクトを表示するのは、 // "作成したグラフィカルオブジェクトを保存 " インプット = true if(saveGraphics){ //シンボル名を取得する string tmpName=""; if(cur_panel<ArraySize(arrPanels)){ tmpName=arrPanels[cur_panel][panelval]; } tmpName=clean_symbol_name(tmpName); StringReplace(tmpName, " ", ""); string tmpObjLine[]; string tmpObjName=""; string sep1="|"; string sep2="~"; //グラフィカルオブジェクトの配列をクリアする saveG.Resize(0); //グラフィックオブジェクトのリストをファイルから配列にアップロードします。 fh=FileOpen(exprefix+"_graph_"+tmpName+".bin",FILE_READ|FILE_BIN|FILE_ANSI); if(fh>=0){ saveG.Load(fh); FileClose(fh); } //チャート上のグラフィカルオブジェクトを連続して表示する for( int i=0; i<saveG.Total(); i++ ){ StringSplit(saveG.At(i), StringGetCharacter(sep1,0), tmpObjLine); for( int j=0; j<ArraySize(tmpObjLine); j++ ){ if(j>0){ string tmpObjSubLine[]; StringSplit(tmpObjLine[j], StringGetCharacter(sep2,0), tmpObjSubLine); if(ArraySize(tmpObjSubLine)==4){ if(tmpObjSubLine[0]=="int"){ //オブジェクトタイプは常に行の先頭に移動します。 //最初にオブジェクトを作成し、その後でそのプロパティを形成するように if(tmpObjSubLine[1]=="OBJPROP_TYPE"){ ObjectCreate(id, tmpObjName, (int) tmpObjSubLine[3], 0, 0, 0); }else if( (int) tmpObjSubLine[3] >= 0 ){ ObjectSetInteger(id, tmpObjName, (int) tmpObjSubLine[2], (int) tmpObjSubLine[3]); } }else if(tmpObjSubLine[0]=="double"){ if( (double) tmpObjSubLine[3] >= 0 ){ ObjectSetDouble(id, tmpObjName, (int) tmpObjSubLine[2], (double) tmpObjSubLine[3]); } }else if(tmpObjSubLine[0]=="string"){ if( StringLen(tmpObjSubLine[3]) > 0 ){ ObjectSetString(id, tmpObjName, (int) tmpObjSubLine[2], tmpObjSubLine[3]); } } } }else{ tmpObjName=tmpObjLine[j]; } } ObjectSetInteger(id, tmpObjName, OBJPROP_SELECTABLE, true); } } }
MQL5. すでに述べたように、MQL5 の関数は効率的ではありません。
void savechart(ulong id){ if(saveGraphics){ string tmpName=""; if(cur_panel<ArraySize(arrPanels)){ tmpName=arrPanels[cur_panel][panelval]; } tmpName=clean_symbol_name(tmpName); StringReplace(tmpName, " ", ""); saveG.Resize(0); int obj_total=ObjectsTotal((long) id); string name; string tmpObjLine=""; for(int i=0;i<obj_total;i++){ name = ObjectName((long) id, i); if( StringFind(name, exprefix)<0 && StringFind(name, "fix")<0 && StringFind(name, "take")<0 && StringFind(name, "stop loss")<0 && StringFind(name, "sell")<0 && StringFind(name, "buy")<0 ){ tmpObjLine=name; //OBJ_HLINE、OBJ_TEXT と OBJ_LABEL オブジェクトタイプでのみ動作 //そのため、他の型のオブジェクトをスキップします。 if( ObjectGetInteger(id, name, OBJPROP_TYPE)!=OBJ_HLINE && ObjectGetInteger(id, name, OBJPROP_TYPE)!=OBJ_TEXT && ObjectGetInteger(id, name, OBJPROP_TYPE)!=OBJ_LABEL ){ continue; } StringAdd(tmpObjLine, "|int~OBJPROP_TYPE~"+(string)(int) OBJPROP_TYPE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_TYPE)); StringAdd(tmpObjLine, "|int~OBJPROP_COLOR~"+(string)(int) OBJPROP_COLOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_COLOR)); StringAdd(tmpObjLine, "|int~OBJPROP_STYLE~"+(string)(int) OBJPROP_STYLE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_STYLE)); StringAdd(tmpObjLine, "|int~OBJPROP_WIDTH~"+(string)(int) OBJPROP_WIDTH+"~"+(string) ObjectGetInteger(id, name, OBJPROP_WIDTH)); StringAdd(tmpObjLine, "|int~OBJPROP_TIME~"+(string)(int) OBJPROP_TIME+"~"+(string) ObjectGetInteger(id, name, OBJPROP_TIME)); StringAdd(tmpObjLine, "|int~OBJPROP_TIMEFRAMES~"+(string)(int) OBJPROP_TIMEFRAMES+"~"+(string) ObjectGetInteger(id, name, OBJPROP_TIMEFRAMES)); StringAdd(tmpObjLine, "|int~OBJPROP_ANCHOR~"+(string)(int) OBJPROP_ANCHOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_ANCHOR)); StringAdd(tmpObjLine, "|int~OBJPROP_XDISTANCE~"+(string)(int) OBJPROP_XDISTANCE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_XDISTANCE)); StringAdd(tmpObjLine, "|int~OBJPROP_YDISTANCE~"+(string)(int) OBJPROP_YDISTANCE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_YDISTANCE)); StringAdd(tmpObjLine, "|int~OBJPROP_STATE~"+(string)(int) OBJPROP_STATE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_STATE)); StringAdd(tmpObjLine, "|int~OBJPROP_XSIZE~"+(string)(int) OBJPROP_XSIZE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_XSIZE)); StringAdd(tmpObjLine, "|int~OBJPROP_YSIZE~"+(string)(int) OBJPROP_YSIZE+"~"+(string) ObjectGetInteger(id, name, OBJPROP_YSIZE)); StringAdd(tmpObjLine, "|int~OBJPROP_XOFFSET~"+(string)(int) OBJPROP_XOFFSET+"~"+(string) ObjectGetInteger(id, name, OBJPROP_XOFFSET)); StringAdd(tmpObjLine, "|int~OBJPROP_YOFFSET~"+(string)(int) OBJPROP_YOFFSET+"~"+(string) ObjectGetInteger(id, name, OBJPROP_YOFFSET)); StringAdd(tmpObjLine, "|int~OBJPROP_BGCOLOR~"+(string)(int) OBJPROP_BGCOLOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_BGCOLOR)); StringAdd(tmpObjLine, "|int~OBJPROP_BORDER_COLOR~"+(string)(int) OBJPROP_BORDER_COLOR+"~"+(string) ObjectGetInteger(id, name, OBJPROP_BORDER_COLOR)); StringAdd(tmpObjLine, "|double~OBJPROP_PRICE~"+(string)(int) OBJPROP_PRICE+"~"+(string) ObjectGetDouble(id, name, OBJPROP_PRICE)); StringAdd(tmpObjLine, "|string~OBJPROP_TEXT~"+(string)(int) OBJPROP_TEXT+"~"+(string) ObjectGetString(id, name, OBJPROP_TEXT)); saveG.Add(tmpObjLine); } } fh=FileOpen(exprefix+"_graph_"+tmpName+".bin",FILE_WRITE|FILE_BIN|FILE_ANSI); if(fh>=0){ saveG.Save(fh); FileClose(fh); } } } void loadchart(ulong id){ if(saveGraphics){ string tmpName=""; if(cur_panel<ArraySize(arrPanels)){ tmpName=arrPanels[cur_panel][panelval]; } tmpName=clean_symbol_name(tmpName); StringReplace(tmpName, " ", ""); string tmpObjLine[]; string tmpObjName=""; string sep1="|"; string sep2="~"; saveG.Resize(0); fh=FileOpen(exprefix+"_graph_"+tmpName+".bin",FILE_READ|FILE_BIN|FILE_ANSI); if(fh>=0){ saveG.Load(fh); FileClose(fh); } for( int i=0; i<saveG.Total(); i++ ){ StringSplit(saveG.At(i), StringGetCharacter(sep1,0), tmpObjLine); for( int j=0; j<ArraySize(tmpObjLine); j++ ){ if(j>0){ string tmpObjSubLine[]; StringSplit(tmpObjLine[j], StringGetCharacter(sep2,0), tmpObjSubLine); if(ArraySize(tmpObjSubLine)==4){ if(tmpObjSubLine[0]=="int"){ //タイプに応じてオブジェクトを作成する if(tmpObjSubLine[1]=="OBJPROP_TYPE"){ switch((int) tmpObjSubLine[3]){ case 1: ObjectCreate(id, tmpObjName, OBJ_HLINE, 0, 0, 0); break; case 101: ObjectCreate(id, tmpObjName, OBJ_TEXT, 0, 0, 0); break; case 102: ObjectCreate(id, tmpObjName, OBJ_LABEL, 0, 0, 0); break; } }else if( (int) tmpObjSubLine[3] >= 0 ){ if(tmpObjSubLine[1]=="OBJPROP_COLOR"){ ObjectSetInteger(id, tmpObjName, OBJPROP_COLOR, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_STYLE"){ ObjectSetInteger(id, tmpObjName, OBJPROP_STYLE, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_WIDTH"){ ObjectSetInteger(id, tmpObjName, OBJPROP_WIDTH, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_TIME"){ ObjectSetInteger(id, tmpObjName, OBJPROP_TIME, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_TIMEFRAMES"){ ObjectSetInteger(id, tmpObjName, OBJPROP_TIMEFRAMES, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_ANCHOR"){ ObjectSetInteger(id, tmpObjName, OBJPROP_ANCHOR, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_XDISTANCE"){ ObjectSetInteger(id, tmpObjName, OBJPROP_XDISTANCE, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_YDISTANCE"){ ObjectSetInteger(id, tmpObjName, OBJPROP_YDISTANCE, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_STATE"){ ObjectSetInteger(id, tmpObjName, OBJPROP_STATE, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_XSIZE"){ ObjectSetInteger(id, tmpObjName, OBJPROP_XSIZE, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_YSIZE"){ ObjectSetInteger(id, tmpObjName, OBJPROP_YSIZE, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_XOFFSET"){ ObjectSetInteger(id, tmpObjName, OBJPROP_XOFFSET, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_YOFFSET"){ ObjectSetInteger(id, tmpObjName, OBJPROP_YOFFSET, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_BGCOLOR"){ ObjectSetInteger(id, tmpObjName, OBJPROP_BGCOLOR, (int) tmpObjSubLine[3]); }else if(tmpObjSubLine[1]=="OBJPROP_BORDER_COLOR"){ ObjectSetInteger(id, tmpObjName, OBJPROP_BORDER_COLOR, (int) tmpObjSubLine[3]); } } }else if(tmpObjSubLine[0]=="double"){ if( (double) tmpObjSubLine[3] >= 0 ){ if(tmpObjSubLine[1]=="OBJPROP_PRICE"){ ObjectSetDouble(id, tmpObjName, OBJPROP_PRICE, (double) tmpObjSubLine[3]); } } }else if(tmpObjSubLine[0]=="string"){ if( StringLen(tmpObjSubLine[3]) > 0 ){ if(tmpObjSubLine[1]=="OBJPROP_TEXT"){ ObjectSetString(id, tmpObjName, OBJPROP_TEXT, tmpObjSubLine[3]); } } } } }else{ tmpObjName=tmpObjLine[j]; } } ObjectSetInteger(id, tmpObjName, OBJPROP_SELECTABLE, true); } } }
詳しく見てみると、異なる型のオブジェクトのオブジェクトを作成するために別の文字列を使用する必要があることがわかりますが、MQL4 では、すべてのオブジェクトに対して1つの文字列で十分です。 オブジェクトプロパティの場合も同様です。 MQL4 では、型 (文字列、実数、または整数) ごとに1つのプロパティ作成文字列を使用しました。 MQL5 では、すべてのプロパティは、その作成の独立した文字列を必要とします。
言語の組み合わせ EAが言語に応じて関数の必要なバージョンを適用するように、条件付きコンパイルを使用してみましょう。
#ifdef __MQL5__ void savechart(ulong id){ //MQL5 関数 } void loadchart(ulong id){ //... ... } #else void savechart(ulong id){ //MQL4 関数 } void loadchart(ulong id){ //... ... } #endif
関数を適用 では、プログラムの適切な部分に関数を呼び出すようにしましょう。
loadchart関数の呼び出しは、押されたボタンに従ってチャートを開くshowcharts関数の内部に追加されます。
チャート保存関数の呼び出しは、チャートのナビゲーションボタンを押すことへの応答に関連するコードのブロックに追加されます。: Next chart, Prev chart、Close chartだけでなく、シンボルを追加/削除するためのボタンホームワークのタブ。
株式の事前選択の finviz.com ウェブサイトの使用
前回の記事では、フィルタのシンボルリストは、ブローカーによって提供されるシンボルのリストからだけでなく、インプットからも取得できることを説明しました。 最初に、必要なオーダーで限定されたシンボルのセットのみを表示できます。 次に、カスタムセットのシンボルを使用して、finviz.com または同様のウェブサイト上で予備的なフィルタを行うことができます。
前回の記事では、ユーザーが価格、ATR などでシンボルを並べ替えることができるインプットのセットを形成しました。 しかし、関数は、finviz.com ウェブサイトのスクリーナと比較して鈍いです。 最も重要なのは、MQL4 は、トレードレベルに基づいて多くの戦略で、重要なインジケータである一方で、実際のボリュームによってシンボルをソートする可能性はありません。 finviz.com ウェブサイトでは、株式の平均出来高、および現在の日に取引されている出来高でソートすることができます。
入力パラメーターからシンボルリストを取得する機能を追加 サードパーティのシンボルリストを使用するには、次の3つの追加のインプットをユーティリティに追加してみましょう。
input string ""; //シンボルのみ (区切り文字、またはスペース) input string ""; //シンボルに接頭辞を追加する input string ""; //シンボルに接尾辞を追加する
ブローカーによって提供されるシンボルの名前が公式のティッカと異なる場合は、 onlySymbolsPrefixとonlySymbolsSuffixパラメータが必要です。 ブローカーは、米国株の.usの接尾辞と欧州株の.eu接尾辞を追加しますが、ブローカーは、任意のティッカにm接尾辞を追加します。 ブローカーはまた、株式ティッカの先頭に#を追加することができます。
ファイルからシンボルをインポートする関数の追加。 先を見て、すぐにインプットからシンボルをインポートすることに問題があるだろうと言うでしょう。 この問題は、文字列の最大長に関係します。 インプットを使用している間、最大で15-20 ティッカに制限されています。 したがって、インプットは、少数のシンボルによって動作するツールを制限するためにのみ使用することができます。
したがって、インプットに加えて、ファイルフォルダに作成されたsymbols.txtファイルに必要なシンボルを配置することもできます。
コードでの実装。 [すべて] タブのシンボルのリストを形成するプロセスを2つのブロックに分割してみましょう。
最初のブロックは、ファイルまたはインプットにシンボルがあるかどうかをチェックします。 yes の場合、結果の配列はよって満たされます。 この配列はOnInit()関数に追加されます。
//「シンボルのみ (区切り文字、またはスペース)」がインプットされている場合 //すべてのデータを関数 if( StringLen(onlySymbols)>0 ){ //行をインプットから配列要素に分割します。 //要素は、;で区切られます。 StringSplit(onlySymbols,StringGetCharacter(";",0),result); if( ArraySize(result)>1 ){ }else{ //分割の結果、配列内に値が1つだけ存在する場合は、 //その後、スプリットは失敗し、明らかにラインのセパレータはスペースです //したがって、インプットから配列要素に行を分割します。 //スペースが要素セパレータとして使用します。 StringSplit(onlySymbols,StringGetCharacter(" ",0),result); } //それ以外の場合、ファイルフォルダには、symbols.txtファイルが含まれているかどうかを確認します。 }else if( FileIsExist("symbols.txt") ){ //ファイルが存在する場合は、その内容を ' outfile ' 一時変数に入れます。 int filehandle=FileOpen("symbols.txt",FILE_READ|FILE_TXT); if(filehandle>=0){ string outfile=FileReadString(filehandle); //' outfile ' 変数に文字列が含まれている場合は、 //配列要素への分割を試みます。 //まず、セパレータを使用します。続いてスペースです。 if(StringLen(outfile)>0){ StringSplit(outfile,StringGetCharacter(";",0),result); if( ArraySize(result)>1 ){ }else{ StringSplit(outfile,StringGetCharacter(" ",0),result); } if( ArraySize(result)>1 ){ from_txt=true; } } FileClose(filehandle); } }
prepare_symbols関数では、最初に結果の配列にデータが含まれているかどうかを確認し、あれば使用します。 それ以外の場合は、ブローカーによって提供されるすべてのシンボル、またはさらにソートするためにマーケット・ウォッチ・パネルに追加した銘柄のいずれかを使用します。
//配列に3つ以上のシンボルがある場合は、それを使用します。 //必要に応じて、必要な接尾辞または接頭辞を事前に追加する if( ArraySize(result)>1 ){ for(int j=0;j<ArraySize(result);j++){ StringReplace(result[j], " ", ""); if(StringLen(result[j])<1){ continue; } tmpSymbols.Add(onlySymbolsPrefix+result[j]+onlySymbolsSuffix); } //それ以外の場合は、ブローカーが提供するすべてのシンボルを使用 }else{ for( int i=0; i<SymbolsTotal(noSYMBmarketWath); i++ ){ tmpSymbols.Add(SymbolName(i, noSYMBmarketWath)); } }
finviz.com スクリーナを使用してシンボルリストの形成 最後に、finviz.com で選択したティッカをユーティリティにインポートする方法を見てみましょう。
すべて簡単です。 並べ替えの後、[スクリーナ] ページの [ティッカ ] タブに移動します。 選択したティッカの名前で構成されるクラウドが表示されます。 すべて選択し、コピーして、symbols.txtファイル、またはインプットに貼り付けます。 並べ替えの結果を含む複数のページがある場合は、次のページに移動して同じ操作を行います。
結論
多くのタスクを行い、ユーティリティをより機能的にしました。 これで紙媒体のノートを離れて株式を選択するために使用することができます。 これが環境のためになることを願っています。 =)
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/5417
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索