FileWriteString関数の働き・役割
FileWriteString関数は、ファイルハンドルを通じて指定されたBIN、CSV、またはTXTファイルに文字列を書き込むために使用されます。
文字列の内容は、ファイルポインタが示す位置に書き込まれます。CSVやTXTファイルに書き込む際に、改行コード「\n」だけが含まれている場合、自動的に「\r\n」に変換されます。BINファイルに書き込む場合、文字数を指定するオプションがあり、指定された文字数に応じて文字列がファイルに書き込まれます。
この関数は、ファイルにテキストデータを保存する際に有用で、特にデータの書式や形式を自動的に適用できるCSVやTXTファイルの操作に便利です。また、BINファイルの操作にも対応しており、ファイル形式に応じて異なる動作をします。
FileWriteString関数の引数について
uint FileWriteString(
int file_handle, // ファイルハンドル
const string text_string, // 書かれる文字列
int length=-1 // シンボル数
);
FileWriteString関数は、次の3つの引数を取ります。
第1引数: file_handle
ファイルハンドルを指定します。FileOpen関数で取得したファイル記述子を渡します。このファイルハンドルによって、書き込み対象のファイルを特定します。
第2引数: text_string
書き込む文字列を指定します。文字列はstring型で渡され、指定されたファイルに書き込まれます。
第3引数: length=-1
オプションの引数で、書き込む文字数を指定します。BINファイルの場合に使用され、指定がない場合は文字列全体が書き込まれます。CSVやTXTファイルでは無視されます。
FileWriteString関数の戻り値について
FileWriteString関数は、正常に実行された場合、書き込まれたバイト数を返します。戻り値として返されるバイト数は、書き込んだ文字列の長さやファイル形式に依存します。
- FILE_UNICODEフラグを使用したファイルやFILE_ANSIフラグなしで開かれたファイルに文字列を書き込む場合、戻り値は書かれた文字数の2倍になります。これは、各文字が2バイトとして書き込まれるためです。
- FILE_ANSIフラグを使用して開かれたファイルでは、戻り値は書かれた文字数と同じになります。
エラーが発生した場合、戻り値は0になります。この場合、GetLastError関数を使ってエラーコードを確認することが推奨されます。
FileWriteString関数を使ったサンプルコード
//--- スクリプトの起動時に入力パラメータのウィンドウを表示する
#property script_show_inputs
//--- 端末からデータを受け取るパラメータ
input string InpSymbolName="EURUSD"; // 通貨ペアの指定(ここではEURUSD)
input ENUM_TIMEFRAMES InpSymbolPeriod=PERIOD_H1; // 時間軸の指定(ここでは1時間足)
input int InpMAPeriod=14; // 移動平均線の期間(ここでは14期間)
input ENUM_APPLIED_PRICE InpAppliedPrice=PRICE_CLOSE; // 価格の種類(ここでは終値を使用)
input datetime InpDateStart=D'2013.01.01 00:00'; // データコピー開始日(ここでは2013年1月1日)
//--- データをファイルに書き込むためのパラメータ
input string InpFileName="RSI.csv"; // 書き込むファイル名(ここではRSI.csv)
input string InpDirectoryName="Data"; // ディレクトリ名(ここではDataフォルダを使用)
//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数 |
//+------------------------------------------------------------------+
void OnStart()
{
datetime date_finish; // データコピー終了日時
double rsi_buff[]; // RSI値を格納する配列
datetime date_buff[]; // 日付を格納する配列
int rsi_size=0; // RSI配列のサイズ
//--- 終了日時を現在の日時に設定
date_finish=TimeCurrent();
//--- RSI指標ハンドルを取得
ResetLastError();
int rsi_handle=iRSI(InpSymbolName,InpSymbolPeriod,InpMAPeriod,InpAppliedPrice);
//--- 指標ハンドル取得に失敗した場合
if(rsi_handle==INVALID_HANDLE)
{
//--- エラーメッセージをエキスパートログに出力
PrintFormat("RSI指標ハンドルの取得に失敗しました。エラーコード = %d",GetLastError());
return; // 処理を終了
}
//--- RSIが全ての値を計算するまで待機
while(BarsCalculated(rsi_handle)==-1)
Sleep(10); // 計算完了までスリープ(10ミリ秒)
//--- RSIの値をコピー
ResetLastError();
if(CopyBuffer(rsi_handle,0,InpDateStart,date_finish,rsi_buff)==-1)
{
//--- コピー失敗時のエラーメッセージ
PrintFormat("RSI値のコピーに失敗しました。エラーコード = %d",GetLastError());
return; // 処理を終了
}
//--- RSIの日付をコピー
ResetLastError();
if(CopyTime(InpSymbolName,InpSymbolPeriod,InpDateStart,date_finish,date_buff)==-1)
{
//--- コピー失敗時のエラーメッセージ
PrintFormat("日付のコピーに失敗しました。エラーコード = %d",GetLastError());
return; // 処理を終了
}
//--- メモリ解放:不要になった指標ハンドルを解放
IndicatorRelease(rsi_handle);
//--- 配列サイズを取得
rsi_size=ArraySize(rsi_buff);
//--- 指標値を書き込むためにファイルを開く(存在しない場合は自動的に作成される)
ResetLastError();
int file_handle=FileOpen(InpDirectoryName+"//"+InpFileName,FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI);
//--- ファイルが正常に開けたか確認
if(file_handle!=INVALID_HANDLE)
{
//--- ファイルが開けたことを通知
PrintFormat("%s ファイルが書き込み可能です",InpFileName);
PrintFormat("ファイルパス: %s\\Files\\",TerminalInfoString(TERMINAL_DATA_PATH));
//--- 文字列を組み立てるための変数を準備
string str="";
bool is_formed=false;
//--- 買われ過ぎ(RSI >= 70)および売られ過ぎ(RSI <= 30)の領域に該当するデータを書き込む
for(int i=0;i<rsi_size;i++)
{
//--- RSI値が買われ過ぎか売られ過ぎの領域か確認
if(rsi_buff[i]>=70 || rsi_buff[i]<=30)
{
//--- 領域の最初の値の場合
if(!is_formed)
{
//--- 値と日付を組み合わせて文字列を作成
str=(string)rsi_buff[i]+"\t"+(string)date_buff[i];
is_formed=true;
}
else
//--- 既存の文字列に値と日付を追加
str+="\t"+(string)rsi_buff[i]+"\t"+(string)date_buff[i];
//--- 次のループへ
continue;
}
//--- フラグを確認し、文字列が作成されている場合ファイルに書き込む
if(is_formed)
{
//--- 作成した文字列をファイルに書き込む(改行コードを追加)
FileWriteString(file_handle,str+"\r\n");
//--- フラグをリセット
is_formed=false;
}
}
//--- ファイルを閉じる
FileClose(file_handle);
//--- 書き込み完了を通知
PrintFormat("データが書き込まれ、%s ファイルは閉じられました",InpFileName);
}
else
//--- ファイルが開けなかった場合、エラーメッセージを出力
PrintFormat("%s ファイルのオープンに失敗しました。エラーコード = %d",InpFileName,GetLastError());
}
サンプルコード解説1:グローバル領域部分
//--- スクリプトの起動時に入力パラメータのウィンドウを表示する
#property script_show_inputs
//--- 端末からデータを受け取るパラメータ
input string InpSymbolName="EURUSD"; // 通貨ペアの指定(ここではEURUSD)
input ENUM_TIMEFRAMES InpSymbolPeriod=PERIOD_H1; // 時間軸の指定(ここでは1時間足)
input int InpMAPeriod=14; // 移動平均線の期間(ここでは14期間)
input ENUM_APPLIED_PRICE InpAppliedPrice=PRICE_CLOSE; // 価格の種類(ここでは終値を使用)
input datetime InpDateStart=D'2013.01.01 00:00'; // データコピー開始日(ここでは2013年1月1日)
//--- データをファイルに書き込むためのパラメータ
input string InpFileName="RSI.csv"; // 書き込むファイル名(ここではRSI.csv)
input string InpDirectoryName="Data"; // ディレクトリ名(ここではDataフォルダを使用)
このセクションでは、スクリプトのグローバル領域にある設定や入力パラメータについて解説します。
1. スクリプトの起動時に入力パラメータのウィンドウを表示
script_show_inputsプロパティは、スクリプトが実行される際に入力パラメータを設定するウィンドウを表示するために使用されます。これにより、ユーザーはスクリプトを実行する前に必要な値を手動で入力できます。
2. 通貨ペア、時間軸、移動平均線期間などの入力パラメータ
inputキーワードを使って、ユーザーが変更可能なパラメータを定義しています。例えば、InpSymbolNameは取引対象の通貨ペアを指定するためのパラメータで、初期値として”EURUSD”が設定されています。InpSymbolPeriodでは、分析する時間軸を指定します。このスクリプトでは1時間足を使用しています。また、InpMAPeriodは移動平均線の計算期間で、InpAppliedPriceは終値を使用するよう設定されています。
InpDateStartは、RSI指標のデータをコピーする際の開始日時を指定します。この例では2013年1月1日が初期値として設定されています。
3. データの保存に関するパラメータ
InpFileNameは、RSI値を書き込むファイルの名前を指定するためのパラメータで、”RSI.csv”という名前のファイルにデータが保存されます。InpDirectoryNameは、保存先のディレクトリを指定しており、ここでは”Data”フォルダにデータを保存するようになっています。
このように、スクリプトのグローバル領域では、ユーザーが必要に応じてカスタマイズできる設定がまとめられています。
サンプルコード解説2:OnStart関数部分その1
//+------------------------------------------------------------------+
//| スクリプトプログラムを開始する関数 |
//+------------------------------------------------------------------+
void OnStart()
{
datetime date_finish; // データコピー終了日時
double rsi_buff[]; // RSI値を格納する配列
datetime date_buff[]; // 日付を格納する配列
int rsi_size=0; // RSI配列のサイズ
//--- 終了日時を現在の日時に設定
date_finish=TimeCurrent();
//--- RSI指標ハンドルを取得
ResetLastError();
int rsi_handle=iRSI(InpSymbolName,InpSymbolPeriod,InpMAPeriod,InpAppliedPrice);
このセクションでは、スクリプトの実行が開始された際に呼び出されるOnStart関数の冒頭部分について解説します。
1. データコピー終了日時の設定
まず、date_finishという変数に、現在のサーバー時間を取得して設定します。この時刻は、RSI指標をコピーする際の終了日時として使用されます。TimeCurrent関数はサーバーの最新の時刻を取得するために使用されます。
2. RSI値や日付を格納する配列の準備
次に、RSI値とその対応する日付を格納するための配列をそれぞれ準備します。rsi_buffはRSI値を、date_buffはそれぞれのRSI値が計算された時点の日付を格納するための配列です。これらの配列は後で、指定された通貨ペアや時間軸に基づいたRSIデータを保存するために使用されます。
また、rsi_sizeはRSI配列のサイズを保持するための変数です。後ほど、実際のRSIデータのサイズを取得してこの変数に格納します。
3. RSI指標ハンドルの取得
次に、指定されたパラメータ(通貨ペア、時間軸、移動平均期間、価格の種類)に基づいて、RSI指標を計算するためのハンドルを取得します。iRSI関数を使ってこのハンドルを取得し、計算されたRSIデータを後にコピーできるようにします。
ハンドル取得が失敗した場合、ResetLastError関数を使ってエラー情報をリセットし、その後にハンドルが無効であることを確認しています。
サンプルコード解説3:OnStart関数部分その2
//--- 指標ハンドル取得に失敗した場合
if(rsi_handle==INVALID_HANDLE)
{
//--- エラーメッセージをエキスパートログに出力
PrintFormat("RSI指標ハンドルの取得に失敗しました。エラーコード = %d",GetLastError());
return; // 処理を終了
}
//--- RSIが全ての値を計算するまで待機
while(BarsCalculated(rsi_handle)==-1)
Sleep(10); // 計算完了までスリープ(10ミリ秒)
このセクションでは、OnStart関数の続き部分で、RSI指標ハンドル取得に関する処理と、計算の待機について解説します。
1. 指標ハンドル取得に失敗した場合の処理
まず、取得したRSI指標ハンドルが無効であるかどうかを確認しています。iRSI関数が無効なハンドル(INVALID_HANDLE)を返した場合は、エラーが発生したことを意味します。この場合、GetLastError関数を使用してエラーコードを取得し、そのエラーコードをエキスパートログに出力しています。
このようにして、指標の取得が失敗した場合は、早期に処理を終了し、原因をログに記録してデバッグを容易にしています。return文により、エラー発生時にはそれ以上の処理が行われないようにしています。
2. RSIの計算が完了するまで待機
RSI指標の計算が完了するまで待機するために、BarsCalculated関数を使用しています。この関数は、指定された指標の計算状況をチェックし、すべてのバーに対する計算が完了するまで待機します。
BarsCalculated関数が-1を返す場合、計算がまだ終わっていないことを示しています。そのため、計算が完了するまで10ミリ秒間スリープする処理を繰り返します。Sleep関数を使用して、スクリプトの実行を一時的に停止させ、リソースの消費を抑えながら計算の完了を待つことができます。
サンプルコード解説4:OnStart関数部分その3
//--- RSIの値をコピー
ResetLastError();
if(CopyBuffer(rsi_handle,0,InpDateStart,date_finish,rsi_buff)==-1)
{
//--- コピー失敗時のエラーメッセージ
PrintFormat("RSI値のコピーに失敗しました。エラーコード = %d",GetLastError());
return; // 処理を終了
}
//--- RSIの日付をコピー
ResetLastError();
if(CopyTime(InpSymbolName,InpSymbolPeriod,InpDateStart,date_finish,date_buff)==-1)
{
//--- コピー失敗時のエラーメッセージ
PrintFormat("日付のコピーに失敗しました。エラーコード = %d",GetLastError());
return; // 処理を終了
}
このセクションでは、OnStart関数の後半部分にあたる、RSI値と日付のコピー処理について解説します。
1. RSIの値をコピー
まず、ResetLastError関数を使ってエラーステータスをリセットし、次にCopyBuffer関数を使用して、RSI指標の値をコピーします。CopyBuffer関数は、取得した指標ハンドルから、指定した期間にわたるRSI値をrsi_buffという配列にコピーします。
この関数が-1を返した場合、コピーに失敗したことを意味します。失敗した場合は、GetLastError関数でエラーコードを取得し、エキスパートログにエラーメッセージを出力します。その後、処理を終了します。
2. RSIの日付をコピー
次に、同じくResetLastError関数でエラーステータスをリセットし、CopyTime関数を使用して、RSIの値に対応する日付をコピーします。CopyTime関数は、指定した通貨ペアと時間軸に基づいて、開始日から終了日までの日付データをdate_buffという配列にコピーします。
この処理もCopyBuffer関数と同様、失敗した場合にはエラーメッセージをログに出力し、処理を終了します。これにより、RSIの値だけでなく、その値がいつのものであるかを記録することが可能になります。
サンプルコード解説5:OnStart関数部分その4
//--- メモリ解放:不要になった指標ハンドルを解放
IndicatorRelease(rsi_handle);
//--- 配列サイズを取得
rsi_size=ArraySize(rsi_buff);
//--- 指標値を書き込むためにファイルを開く(存在しない場合は自動的に作成される)
ResetLastError();
int file_handle=FileOpen(InpDirectoryName+"//"+InpFileName,FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI);
このセクションでは、OnStart関数の最後の部分で、不要になったメモリの解放やファイルの作成・オープンについて解説します。
1. 指標ハンドルのメモリ解放
取得した指標ハンドル(rsi_handle)は、使用後に不要となるため、メモリの解放が必要です。IndicatorRelease関数を使用することで、このハンドルに関連するリソースを解放し、システムのメモリ管理を適切に行います。これにより、不要なリソースが残らず、メモリリークのリスクを回避します。
2. 配列サイズの取得
次に、ArraySize関数を使用して、rsi_buff配列に格納されたRSI値のサイズを取得します。このサイズは、後ほどファイルに書き込む際にループを制御するために使用されます。RSIデータの個数を把握することは、正確なデータ書き込みにおいて重要です。
3. ファイルのオープン
次に、FileOpen関数を使用して、指定したディレクトリとファイル名でファイルを開きます。このとき、FILE_READ、FILE_WRITE、FILE_CSV、およびFILE_ANSIフラグを指定しています。
- FILE_READとFILE_WRITEは、ファイルを読み書き可能なモードで開くためのフラグです。
- FILE_CSVは、ファイルをCSV形式で扱うことを指定します。CSV形式は、データをカンマで区切って保存するためのフォーマットです。
- FILE_ANSIは、文字エンコーディングをANSI形式で保存するためのフラグです。これにより、保存されるデータがANSI形式でエンコードされます。
ファイルが存在しない場合は、自動的に新しいファイルが作成され、存在する場合はそのファイルにデータが追加される形で扱われます。
サンプルコード解説6:OnStart関数部分その5
//--- ファイルが正常に開けたか確認
if(file_handle!=INVALID_HANDLE)
{
//--- ファイルが開けたことを通知
PrintFormat("%s ファイルが書き込み可能です",InpFileName);
PrintFormat("ファイルパス: %s\\Files\\",TerminalInfoString(TERMINAL_DATA_PATH));
//--- 文字列を組み立てるための変数を準備
string str="";
bool is_formed=false;
このセクションでは、ファイルのオープン後の処理と、データ書き込み準備の部分について解説します。
1. ファイルが正常に開けたかの確認
ファイルが正常に開けたかどうかを確認するために、file_handleが無効なハンドルではないかをチェックします。もし、ファイルハンドルがINVALID_HANDLEではなければ、ファイルが正常に開けたことを意味します。
ファイルが正常に開けたことを確認すると、PrintFormat関数を使って、ファイルが書き込み可能であることをエキスパートログに出力します。また、ファイルの保存先ディレクトリのパスを、TerminalInfoString関数を使って取得し、エキスパートログに表示しています。これにより、データがどこに保存されるかを確認できるため、後でファイルを探しやすくなります。
2. 文字列組み立ての準備
ファイルに書き込むために、strという変数を準備します。この変数は、実際に書き込むデータを格納するために使われます。ここでは空の文字列として初期化され、ループ処理の中でデータが追加されていきます。
また、is_formedというブール型の変数が準備されています。この変数は、データが正しく組み立てられたかどうかを追跡するために使用されます。データが条件を満たした場合に、is_formedがtrueに設定され、ファイルに書き込む準備が整ったことを示します。これにより、後の処理で効率的にデータを組み立てて書き込むことが可能になります。
サンプルコード解説7:OnStart関数部分その6
//--- 買われ過ぎ(RSI >= 70)および売られ過ぎ(RSI <= 30)の領域に該当するデータを書き込む
for(int i=0;i<rsi_size;i++)
{
//--- RSI値が買われ過ぎか売られ過ぎの領域か確認
if(rsi_buff[i]>=70 || rsi_buff[i]<=30)
{
//--- 領域の最初の値の場合
if(!is_formed)
{
//--- 値と日付を組み合わせて文字列を作成
str=(string)rsi_buff[i]+"\t"+(string)date_buff[i];
is_formed=true;
}
else
//--- 既存の文字列に値と日付を追加
str+="\t"+(string)rsi_buff[i]+"\t"+(string)date_buff[i];
//--- 次のループへ
continue;
}
このセクションでは、RSI指標が「買われ過ぎ」や「売られ過ぎ」の状態にあるデータをチェックし、該当するデータをファイルに書き込む処理について解説します。
1. RSI値の判定
ループ処理で、rsi_buff配列に格納されたすべてのRSI値を順番にチェックしています。RSI値が70以上の場合、買われ過ぎとみなし、30以下の場合、売られ過ぎとみなします。これらの閾値は、一般的に相場の転換点を示すサインとして使われます。
2. 最初の値の場合の処理
RSI値が買われ過ぎまたは売られ過ぎの条件に該当する場合、その値と対応する日付を文字列として組み立てます。もしis_formedがfalseである場合、つまり最初の値であれば、まずは新しい文字列を作成し、RSI値と対応する日付をタブ区切りで結合します。ここでは、RSI値を文字列に変換し、それに対応する日付も文字列に変換して結合しています。
※\t
は、タブ文字を表すエスケープシーケンス(特殊文字を表現するための文字列)です。テキスト内で使用すると、スペース数に相当するタブが挿入されます。テキストやデータを整列させる際に使われます。
3. 既存の文字列へのデータ追加
is_formedがtrueの場合は、すでに文字列が作成されていることを意味します。この場合、既存の文字列に新しいRSI値と日付を追加します。各データはタブで区切られ、次のデータが連続して結合されます。
4. 次のループへ移行
条件に該当するRSI値が見つかった場合は、次のループに移行します。continue文を使用することで、すぐに次のループ反復へと進み、無駄な処理を避けています。これにより、効率的に買われ過ぎや売られ過ぎの値だけを抽出して処理することができます。
サンプルコード解説8:OnStart関数部分その7
//--- フラグを確認し、文字列が作成されている場合ファイルに書き込む
if(is_formed)
{
//--- 作成した文字列をファイルに書き込む(改行コードを追加)
FileWriteString(file_handle,str+"\r\n");
//--- フラグをリセット
is_formed=false;
}
}
//--- ファイルを閉じる
FileClose(file_handle);
//--- 書き込み完了を通知
PrintFormat("データが書き込まれ、%s ファイルは閉じられました",InpFileName);
}
else
//--- ファイルが開けなかった場合、エラーメッセージを出力
PrintFormat("%s ファイルのオープンに失敗しました。エラーコード = %d",InpFileName,GetLastError());
}
このセクションでは、作成された文字列をファイルに書き込み、ファイルを閉じる処理について解説します。
1. フラグを確認し、文字列をファイルに書き込む
is_formedフラグを確認し、文字列がすでに作成されている場合には、その文字列をファイルに書き込みます。FileWriteString関数を使用して、str変数に格納されたデータをファイルに書き込む際に、\r\n
(改行コード)を追加して行を区切っています。これにより、ファイル内でデータが改行され、整然とした形式で保存されます。
※\r\n
は、キャリッジリターンとラインフィードを表すエスケープシーケンス(特殊文字を表現するための文字列)です。これは、改行を意味し、Windowsなどのシステムでテキストの行を区切る際に使用されます。\r
はキャリッジリターン(カーソルを行頭に戻す)を、\n
はラインフィード(次の行に移る)を表します。
2. フラグのリセット
データを書き込んだ後、is_formedフラグをfalseにリセットします。これにより、次のループで新しいデータの組み立てが始まる準備が整います。このリセット処理を行わないと、前回のデータが次回のループに影響を与えてしまう可能性があります。
3. ファイルのクローズ
すべてのデータを書き終えた後、FileClose関数を使ってファイルを閉じます。ファイルを閉じることにより、システムリソースが解放され、データが正しく保存されることを保証します。
4. 書き込み完了の通知
ファイルの書き込みとクローズが正常に完了したことをPrintFormat関数を使用してエキスパートログに通知します。これにより、ユーザーはデータが無事に保存されたことを確認できます。
5. エラー処理
もしファイルが正常に開けなかった場合は、エラーが発生したことをエキスパートログに出力します。GetLastError関数でエラーコードを取得し、その詳細を通知します。これにより、ファイルのオープン失敗時に問題を特定する手がかりを提供します。