FileWriteArray関数の働き・役割
FileWriteArray関数は、配列のデータをバイナリファイル(BINファイル)に書き込むために使用されます。この関数は、string型以外の任意の型の配列をファイルに保存することが可能です。また、文字列や動的配列を含まない構造体の配列もサポートしています。主に、数値や日時などのデータをファイルに効率的に保存する際に役立ちます。
この関数を使うことで、トレード履歴や価格データのような複数のデータを一括でファイルに保存し、後で再利用することが可能になります。ファイルはバイナリ形式で保存されるため、他の形式に比べてデータサイズがコンパクトであり、読み書きの速度も高速です。
FileWriteArray関数の引数について
uint FileWriteArray(
int file_handle, // ファイルハンドル
const void& array[], // 配列
int start=0, // 配列の開始インデックス
int count=WHOLE_ARRAY // 要素数
);
FileWriteArray関数は、4つの引数を取ります。それぞれの引数は以下の通りです。
第1引数: ファイルハンドル
この引数には、FileOpen関数を使って取得したファイルハンドルを指定します。このハンドルは、ファイルを操作する際の識別子として使用されます。ファイルを正しく開いた場合には有効なハンドルが返され、失敗した場合はINVALID_HANDLEが返されます。
第2引数: 配列
この引数には、書き込むデータが格納されている配列を指定します。この配列はstring型以外の任意の型である必要があります。例えば、int型やdouble型、または構造体であれば、string型を含まないものを指定することが可能です。ここでは、ファイルに書き込むデータの塊を表します。
第3引数: 開始インデックス
この引数は、配列内のどのインデックスからデータを書き込むかを指定します。デフォルト値は0で、配列の最初の要素から書き込みを始めます。他の値を指定することで、任意のインデックスからの書き込みが可能です。
第4引数: 書き込む要素数
この引数は、書き込む要素の数を指定します。デフォルト値はWHOLE_ARRAYで、これは配列の開始インデックスから最後の要素までのすべてを書き込むことを意味します。特定の要素数だけを書き込みたい場合は、希望する数を指定することも可能です。
FileWriteArray関数の戻り値について
FileWriteArray関数の戻り値は、ファイルに書き込まれた配列の要素数です。正常にデータが書き込まれた場合、この戻り値を確認することで、実際にいくつの要素がファイルに保存されたかを知ることができます。
例えば、10個の要素を持つ配列を指定して、そのうち5個を書き込むように設定した場合、この関数は5を返します。
一方で、エラーが発生した場合やファイルハンドルが無効な場合、関数は0を返す可能性があります。そのため、戻り値を確認し、期待した数の要素が正しく書き込まれているかを確認することが重要です。
FileWriteArray関数を使う際の注意点
FileWriteArray関数は、string型の配列をバイナリファイルに書き込むことはできません。もし文字列データを保存したい場合は、テキストファイル(TXTファイル)を使用する必要があります。この場合、各文字列は自動的に行末文字(\r\n)で終了され、ファイルタイプに応じてANSIまたはUnicodeの形式に変換されます。
また、バイナリ形式でデータを保存するため、データを正しく読み取るには、バイナリ形式に対応した方法でファイルを開き、読み込む必要があります。
FileWriteArray関数を使ったサンプルコード
//--- 入力パラメータ
input string InpFileName="data.bin"; // ファイル名を指定するための入力パラメータ(デフォルトは "data.bin")
input string InpDirectoryName="SomeFolder"; // ディレクトリ名を指定するための入力パラメータ(デフォルトは "SomeFolder")
//+------------------------------------------------------------------+
//| 価格データを格納する構造体 |
//+------------------------------------------------------------------+
struct prices
{
datetime date; // 日付を保持するdatetime型の変数(サーバー時刻などのタイムスタンプ)
double bid; // 売値を保持するdouble型の変数(現在のシンボルの売値)
double ask; // 買値を保持するdouble型の変数(現在のシンボルの買値)
};
//--- グローバル変数
int count=0; // 現在の配列内に保存されたデータの数を追跡するカウンタ変数
int size=20; // 配列のサイズを表す変数(価格データを保存する構造体配列の要素数)
string path=InpDirectoryName+"//"+InpFileName; // ファイルのパスを表す文字列変数(指定されたディレクトリとファイル名を結合)
prices arr[]; // 価格データを保存するための構造体配列(prices型の要素を持つ動的配列)
//+------------------------------------------------------------------+
//| エキスパート初期化に使用される関数 |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 配列へのメモリ追加
ArrayResize(arr, size); // 構造体配列arrのサイズを、size変数で指定された要素数にリサイズ(ここでは20)
//---
return(INIT_SUCCEEDED); // エキスパートの初期化が成功したことを示すコードを返す
}
//+------------------------------------------------------------------+
//| エキスパート初期化解除に使用される関数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- countが現在の配列サイズより小さい場合、残りのデータをファイルに書き込む
WriteData(count); // 配列のうち、まだファイルに保存されていない部分のデータをファイルに書き込む
}
//+------------------------------------------------------------------+
//| エキスパートティック関数 |
//+------------------------------------------------------------------+
void OnTick()
{
//--- データを配列に保存する
arr[count].date = TimeCurrent(); // サーバーの現在時刻を取得し、配列のdateフィールドに保存
arr[count].bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); // 現在のシンボルの売値を取得し、配列のbidフィールドに保存
arr[count].ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); // 現在のシンボルの買値を取得し、配列のaskフィールドに保存
//--- 現在のデータを表示する
Print("Date = ", arr[count].date, " Bid = ", arr[count].bid, " Ask = ", arr[count].ask); // 取得した日付、売値、買値をエキスパートログに出力
//--- カウンタを増やす
count++; // 保存したデータの数を表すカウンタ変数をインクリメント
//--- 配列がすでに記入済みなら、データをファイルに書いてカウンタをゼロにリセットする
if(count == size)
{
WriteData(size); // 配列のデータをファイルに書き込む
count = 0; // カウンタをリセットし、次のデータの保存を最初の要素から始められるようにする
}
}
//+------------------------------------------------------------------+
//| 配列のn要素をファイルに書き込む関数 |
//+------------------------------------------------------------------+
void WriteData(const int n)
{
//--- ファイルを開く
ResetLastError(); // 前回のエラーコードをリセット
int handle = FileOpen(path, FILE_READ | FILE_WRITE | FILE_BIN); // 指定されたファイルを読み書きモードでバイナリ形式で開く
if(handle != INVALID_HANDLE)
{
//--- 配列データをファイルの末尾に書き込む
FileSeek(handle, 0, SEEK_END); // ファイルの末尾に移動し、そこから書き込みを開始
FileWriteArray(handle, arr, 0, n); // 配列arrのn個の要素をファイルに書き込む
//--- ファイルを閉じる
FileClose(handle); // ファイルを閉じる
}
else
Print("Failed to open the file, error ", GetLastError()); // ファイルのオープンに失敗した場合、エラーメッセージを表示
}
サンプルコード解説1:input変数部分
//--- 入力パラメータ
input string InpFileName="data.bin"; // ファイル名を指定するための入力パラメータ(デフォルトは "data.bin")
input string InpDirectoryName="SomeFolder"; // ディレクトリ名を指定するための入力パラメータ(デフォルトは "SomeFolder")
この部分では、inputという修飾子を使って、エキスパートアドバイザ(EA)が実行される際に、ユーザーが変更できるパラメータを定義しています。input修飾子を使うことで、EAのプロパティダイアログからこれらの値を簡単に設定できるようになります。ユーザーはコードを書き換えることなく、ファイル名や保存先のディレクトリを変更可能です。
InpFileName
InpFileNameは、ファイル名を指定するための文字列型の入力変数です。初期値として”data.bin”が設定されています。この初期値は、特定のバイナリファイルにデータを保存するために使用されます。ユーザーは必要に応じてこのファイル名を変更できます。
InpDirectoryName
InpDirectoryNameは、データを保存するディレクトリを指定するための文字列型の入力変数です。初期値として”SomeFolder”が設定されています。このディレクトリ名をユーザーが変更することで、データの保存先を柔軟に指定することができます。
サンプルコード解説2:価格データを格納する構造体部分
//+------------------------------------------------------------------+
//| 価格データを格納する構造体 |
//+------------------------------------------------------------------+
struct prices
{
datetime date; // 日付を保持するdatetime型の変数(サーバー時刻などのタイムスタンプ)
double bid; // 売値を保持するdouble型の変数(現在のシンボルの売値)
double ask; // 買値を保持するdouble型の変数(現在のシンボルの買値)
};
この部分では、価格データを保存するための構造体を定義しています。構造体とは、異なる種類のデータを一つのまとまりとして扱うことができるデータ型です。ここでは、取引において重要な3つの情報、日付、売値、買値を一つのデータセットとして管理するために使用されています。
datetime型の変数 date
date
は、サーバー時間などのタイムスタンプを保持するための変数です。これはdateTime型の変数で、日時を秒単位で扱います。このフィールドには、各取引や価格データが記録された時刻が保存されます。
double型の変数 bid
bid
は、現在のシンボル(通貨ペアなど)の売値を保持するための変数です。これはdouble型の変数で、取引で提示された売値を保存します。この値は、売り手がそのシンボルを売る際の価格を意味します。
double型の変数 ask
ask
は、現在のシンボルの買値を保持するための変数です。これもdouble型の変数で、取引において買い手がそのシンボルを購入するために提示される価格を表します。このフィールドには、その時点での最良の買値が格納されます。
この構造体によって、複数の価格データを一括して管理できるようになり、コード全体で効率的なデータの取り扱いが可能になります。
サンプルコード解説3:その他グローバル領域部分
//--- グローバル変数
int count=0; // 現在の配列内に保存されたデータの数を追跡するカウンタ変数
int size=20; // 配列のサイズを表す変数(価格データを保存する構造体配列の要素数)
string path=InpDirectoryName+"//"+InpFileName; // ファイルのパスを表す文字列変数(指定されたディレクトリとファイル名を結合)
prices arr[]; // 価格データを保存するための構造体配列(prices型の要素を持つ動的配列)
この部分では、エキスパートアドバイザ(EA)の全体で使用されるグローバル変数を定義しています。グローバル変数は、プログラムのどの部分からでもアクセス可能な変数であり、複数の関数間で共有されます。
count変数
countは、配列内に現在保存されているデータの数を追跡するためのカウンタ変数です。初期値は0に設定されており、これはデータがまだ保存されていない状態を表します。データが新しく保存されるたびに、この変数の値が増加し、配列に追加されたデータの数を記録していきます。
size変数
sizeは、価格データを保存する構造体配列のサイズを表す変数です。この値は、配列に保存できる最大のデータ数を示します。このサンプルコードでは、サイズは20に設定されています。つまり、20件の価格データを保存するための領域が確保されます。この変数は後に配列のリサイズに使用されます。
path変数
pathは、保存先のファイルパスを表す文字列変数です。この変数は、ディレクトリ名とファイル名を結合することで、データが保存される完全なファイルパスを生成しています。このパスを利用して、ファイルの読み書き操作を行います。たとえば、SomeFolder//data.binのような形式でパスが作成されます。
arr配列
arrは、価格データを保存するための構造体配列です。配列の要素は、先に定義された構造体を使っています。この配列は動的にサイズが変更されるため、必要に応じてメモリのサイズが調整されます。countやsizeと組み合わせて、価格データの管理に利用されます。
構造体のインスタンスについて
構造体のインスタンスとは、構造体という設計図を元に作られた具体的なデータのことを指します。構造体は、複数の異なるデータ型をまとめて扱うことができるため、データの集合体として機能します。この場合、pricesは構造体のデータ型であり、arrという配列がその具体的なインスタンス(実体)として機能しています。それぞれのインスタンスには独自のデータが格納され、プログラムの中で一意に扱われます。
サンプルコード解説4:OnInit関数部分
//+------------------------------------------------------------------+
//| エキスパート初期化に使用される関数 |
//+------------------------------------------------------------------+
int OnInit()
{
//--- 配列へのメモリ追加
ArrayResize(arr, size); // 構造体配列arrのサイズを、size変数で指定された要素数にリサイズ(ここでは20)
//---
return(INIT_SUCCEEDED); // エキスパートの初期化が成功したことを示すコードを返す
}
OnInit関数は、エキスパートアドバイザ(EA)の初期化時に自動的に呼び出される関数です。この関数は、EAがチャートに適用されたときや再起動されたときに、一度だけ実行されます。EAが動作を開始する前に、必要な準備を行う役割を持っています。
配列へのメモリ追加
ArrayResize関数は、配列のサイズを指定した値に変更するために使用されます。この場合、arrという構造体配列のサイズをsize変数の値(ここでは20)にリサイズしています。これにより、価格データを保存するための配列のメモリ領域が確保され、配列には20個の価格データを保存できるようになります。
構造体配列は、動的にメモリサイズを変更できるため、事前に決めたサイズ以上のデータが必要になった場合にも対応可能です。ArrayResize関数は、このような動的配列操作の基本となる関数です。
初期化結果の返却
returnは、EAの初期化が正常に完了したことを示す定数を返す部分です。INIT_SUCCEEDEDは、初期化成功を示す標準的なコードで、EAがその後の処理(OnTick関数など)を正常に実行できるようになります。もし初期化が失敗した場合、異なるエラーコードを返すことで、EAの実行が停止される場合もあります。
このOnInit関数により、エキスパートアドバイザは必要なデータ構造や配列の準備を行い、動作の基盤を整えた上で稼働を開始します。
サンプルコード解説5:OnDeinit関数部分
//+------------------------------------------------------------------+
//| エキスパート初期化解除に使用される関数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- countが現在の配列サイズより小さい場合、残りのデータをファイルに書き込む
WriteData(count); // 配列のうち、まだファイルに保存されていない部分のデータをファイルに書き込む
}
OnDeinit関数は、エキスパートアドバイザ(EA)がチャートから削除される際、またはEAが終了する際に自動的に呼び出される関数です。この関数の役割は、EAの終了時に必要な後処理を行うことです。たとえば、未処理のデータをファイルに保存したり、リソースを解放したりする場合に使用されます。
未保存データのファイルへの書き込み
WriteData関数を使用して、count変数が示す現在の配列のサイズ未満のデータ(まだファイルに保存されていないデータ)をファイルに書き込みます。この処理により、EAが終了する前に保存されなかったデータを確実にファイルに保存できます。
例えば、配列にデータが途中までしか記録されていない場合、その残りのデータをファイルに書き込むことで、途中でプログラムが終了した際にデータが失われるのを防ぎます。ここでは、count変数によって配列に保存されているデータの数を把握し、残りのデータだけが正しくファイルに保存されます。
このOnDeinit関数によって、EAの終了時にデータが漏れなく保存されるため、データ管理の信頼性が向上します。
サンプルコード解説6:OnTick関数部分
//+------------------------------------------------------------------+
//| エキスパートティック関数 |
//+------------------------------------------------------------------+
void OnTick()
{
//--- データを配列に保存する
arr[count].date = TimeCurrent(); // サーバーの現在時刻を取得し、配列のdateフィールドに保存
arr[count].bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); // 現在のシンボルの売値を取得し、配列のbidフィールドに保存
arr[count].ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); // 現在のシンボルの買値を取得し、配列のaskフィールドに保存
//--- 現在のデータを表示する
Print("Date = ", arr[count].date, " Bid = ", arr[count].bid, " Ask = ", arr[count].ask); // 取得した日付、売値、買値をエキスパートログに出力
//--- カウンタを増やす
count++; // 保存したデータの数を表すカウンタ変数をインクリメント
//--- 配列がすでに記入済みなら、データをファイルに書いてカウンタをゼロにリセットする
if(count == size)
{
WriteData(size); // 配列のデータをファイルに書き込む
count = 0; // カウンタをリセットし、次のデータの保存を最初の要素から始められるようにする
}
}
OnTick関数は、エキスパートアドバイザ(EA)のメインロジックを実行する関数で、毎回新しいティック(価格変動)が発生するたびに自動的に呼び出されます。この関数内で、価格データを取得し、保存、表示、そして必要に応じてファイルに書き込む処理が行われます。
データの配列への保存
TimeCurrent関数は、サーバー時間を取得するために使用されます。この関数を使って取得した時刻を、配列のdateフィールドに保存します。これにより、データのタイムスタンプが記録され、いつのデータなのかを明確にします。
SymbolInfoDouble関数は、指定したシンボル(通貨ペアなど)の価格情報を取得するために使用されます。ここでは、売値を取得するためにSYMBOL_BID、買値を取得するためにSYMBOL_ASKという識別子を引数として渡しています。取得した売値と買値は、それぞれ配列のbidフィールドとaskフィールドに保存されます。
これにより、リアルタイムの価格データが逐次配列に蓄積されていき、データが体系的に管理されます。
データの表示
取得した価格データ(日時、売値、買値)は、Print関数を使用してエキスパートログに表示されます。これにより、デバッグや動作確認の際に、実際にどのデータが取得されているかをリアルタイムで確認できます。
カウンタの更新
count変数は、保存したデータの数を追跡するカウンタです。新しいティックごとにデータが保存されるたびに、このカウンタがインクリメントされます。このカウンタは、配列がどこまで埋まっているかを管理するために使用されます。
配列のデータをファイルに書き込む
配列が指定されたサイズ(ここではsize変数で設定された20)に達した場合、WriteData関数を使って配列全体をファイルに書き込みます。これにより、一定量のデータが蓄積された段階でファイルへの保存が行われ、データが失われないようにします。
その後、count変数が0にリセットされ、次のデータが配列の最初の要素から再び保存されるようになります。これにより、配列が常に新しいデータを保持し、不要な古いデータが上書きされることなく効率的に管理されます。
このOnTick関数によって、リアルタイムの価格データを効率的に取得、保存し、定期的にファイルへ記録する仕組みが実現されています。
サンプルコード解説7:WriteData関数(オリジナル関数)部分
//+------------------------------------------------------------------+
//| 配列のn要素をファイルに書き込む関数 |
//+------------------------------------------------------------------+
void WriteData(const int n)
{
//--- ファイルを開く
ResetLastError(); // 前回のエラーコードをリセット
int handle = FileOpen(path, FILE_READ | FILE_WRITE | FILE_BIN); // 指定されたファイルを読み書きモードでバイナリ形式で開く
if(handle != INVALID_HANDLE)
{
//--- 配列データをファイルの末尾に書き込む
FileSeek(handle, 0, SEEK_END); // ファイルの末尾に移動し、そこから書き込みを開始
FileWriteArray(handle, arr, 0, n); // 配列arrのn個の要素をファイルに書き込む
//--- ファイルを閉じる
FileClose(handle); // ファイルを閉じる
}
else
Print("Failed to open the file, error ", GetLastError()); // ファイルのオープンに失敗した場合、エラーメッセージを表示
}
WriteData関数は、価格データが保存された配列をファイルに書き込むためのオリジナル関数です。この関数は、データをバイナリ形式でファイルに保存し、次回の使用時や外部プログラムでのデータ分析などに役立てます。
const修飾子について
関数の引数に付けられているconst修飾子は、この引数が関数内で変更されないことを保証するために使用されます。具体的には、nという引数は関数の中で値が変わらないことが明示されており、関数の安全性や可読性を向上させます。つまり、nは関数の中で読み取り専用の変数として扱われます。
引数nの役割
引数nは、配列の中からファイルに書き込む要素の数を指定するためのものです。この値が、実際に書き込むべきデータの量を制御します。たとえば、配列が20個の要素を持っていても、引数nを10と指定すれば、最初の10個だけがファイルに書き込まれることになります。これは、部分的なデータ保存が必要な場合に役立ちます。
使用されている関数の役割
- ResetLastError関数
この関数は、直前のエラーコードをリセットするために使用されます。これにより、関数が実行された後に発生する新たなエラーを正しく追跡できるようにします。これを最初に呼び出すことで、過去のエラーが残っている場合でもそれが影響しないようにします。 - FileOpen関数
ファイルを指定されたモードで開く関数です。この場合、ファイルは読み書き可能で、バイナリ形式で開かれます。この関数が成功すると、ファイルハンドル(handle)が返され、以降のファイル操作で使用されます。もしファイルを開くことができなかった場合、無効なハンドルが返されます。 - FileSeek関数
ファイル内の指定した位置に移動するための関数です。この場合、ファイルの末尾(SEEK_END)に移動し、そこから新たにデータを書き込みます。これにより、既存のデータを上書きすることなく、新しいデータを追加できます。 - FileWriteArray関数
配列のデータをファイルに書き込む関数です。この関数では、先ほど指定したファイルハンドル(handle)を使い、配列のデータをバイナリ形式で保存します。引数のarrは書き込む配列、0は配列の開始位置、nは書き込む要素数を指定しています。 - FileClose関数
ファイルの操作が終わった後、開いたファイルを閉じるための関数です。ファイルを閉じることで、リソースの消費を抑え、次の操作に備えます。 - Print関数
ファイルのオープンに失敗した場合、Print関数を使ってエラーメッセージをエキスパートログに表示します。これにより、プログラムの動作確認やデバッグが行いやすくなります。
このWriteData関数は、指定された数のデータを配列から効率的にファイルに保存し、リソース管理をしっかりと行いながら、ファイルの読み書きを適切に処理します。