<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>バックテスト アーカイブ - 自動売買を作ろう！</title>
	<atom:link href="https://mql-programing.com/archives/tag/%E3%83%90%E3%83%83%E3%82%AF%E3%83%86%E3%82%B9%E3%83%88/feed/" rel="self" type="application/rss+xml" />
	<link>https://mql-programing.com/archives/tag/バックテスト/</link>
	<description>MQLプログラミング学習サイト</description>
	<lastBuildDate>Wed, 01 Apr 2026 04:29:28 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://mql-programing.com/main29/wp-content/uploads/2021/02/cropped-ブログアイコン-32x32.jpg</url>
	<title>バックテスト アーカイブ - 自動売買を作ろう！</title>
	<link>https://mql-programing.com/archives/tag/バックテスト/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>【MQL4】知っておきたいユーティリティ関数6選！Sleep・RefreshRates・IsTradeAllowed・IsConnected・IsTesting・IsOptimizationの使い方</title>
		<link>https://mql-programing.com/archives/13065/%e3%80%90mql4%e3%80%91%e7%9f%a5%e3%81%a3%e3%81%a6%e3%81%8a%e3%81%8d%e3%81%9f%e3%81%84%e3%83%a6%e3%83%bc%e3%83%86%e3%82%a3%e3%83%aa%e3%83%86%e3%82%a3%e9%96%a2%e6%95%b06%e9%81%b8%ef%bc%81sleep%e3%83%bbr/</link>
		
		<dc:creator><![CDATA[朝日奈りさ]]></dc:creator>
		<pubDate>Thu, 23 Apr 2026 01:00:00 +0000</pubDate>
				<category><![CDATA[【中級編】MQLプログラムの読み方・書き方]]></category>
		<category><![CDATA[バックテスト]]></category>
		<category><![CDATA[MQL4]]></category>
		<category><![CDATA[EA開発]]></category>
		<category><![CDATA[ユーティリティ関数]]></category>
		<category><![CDATA[Sleep]]></category>
		<guid isPermaLink="false">https://mql-programing.com/?p=13065</guid>

					<description><![CDATA[<p>EA（自動売買プログラム）を作っていると、「注文を出す前にサーバーと接続できているか確認したい」「バックテスト中は処理を軽くしたい」といった場面に必ず出くわします。こうした場面で活躍するのが、MQL4に用意されたユーティ [&#8230;]</p>
<p>投稿 <a href="https://mql-programing.com/archives/13065/%e3%80%90mql4%e3%80%91%e7%9f%a5%e3%81%a3%e3%81%a6%e3%81%8a%e3%81%8d%e3%81%9f%e3%81%84%e3%83%a6%e3%83%bc%e3%83%86%e3%82%a3%e3%83%aa%e3%83%86%e3%82%a3%e9%96%a2%e6%95%b06%e9%81%b8%ef%bc%81sleep%e3%83%bbr/">【MQL4】知っておきたいユーティリティ関数6選！Sleep・RefreshRates・IsTradeAllowed・IsConnected・IsTesting・IsOptimizationの使い方</a> は <a href="https://mql-programing.com">自動売買を作ろう！</a> に最初に表示されました。</p>
]]></description>
										<content:encoded><![CDATA[<p>EA（自動売買プログラム）を作っていると、「注文を出す前にサーバーと接続できているか確認したい」「バックテスト中は処理を軽くしたい」といった場面に必ず出くわします。こうした場面で活躍するのが、MQL4に用意された<strong>ユーティリティ関数</strong>たちです。</p>
<p>今回は、EA開発で特に使用頻度が高い以下の<strong>6つのユーティリティ関数</strong>をまとめて解説します。</p>
<ul>
<li><strong>Sleep()</strong> — 処理の一時停止</li>
<li><strong>RefreshRates()</strong> — 価格データの最新化</li>
<li><strong>IsTradeAllowed()</strong> — 自動売買の許可状態を確認</li>
<li><strong>IsConnected()</strong> — サーバー接続の確認</li>
<li><strong>IsTesting()</strong> — バックテスト中かどうかの判定</li>
<li><strong>IsOptimization()</strong> — 最適化モードかどうかの判定</li>
</ul>
<p>それぞれの基本的な使い方から、最後に6つすべてを組み合わせた実践テンプレートまで紹介しますので、ぜひ最後までお読みください。</p>
<h2><span id="toc1">Sleep() — ミリ秒単位で処理を一時停止する</span></h2>
<h3><span id="toc2">基本構文</span></h3>
<pre><code class="language-mql4">void Sleep(int milliseconds);
</code></pre>
<p><strong>Sleep()</strong>は、指定したミリ秒（1000ミリ秒＝1秒）だけEAの処理を一時停止する関数です。注文のリトライ処理やサーバーとの通信待ちなどで頻繁に使われます。</p>
<h3><span id="toc3">使用例：注文失敗時のリトライ</span></h3>
<p>注文送信が失敗した場合、少し間をおいてからリトライするのが一般的なパターンです。</p>
<pre><code class="language-mql4">int ticket = -1;
int retryCount = 3;

for(int i = 0; i < retryCount; i++)
{
    ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Buy Order", 0, 0, clrBlue);

    if(ticket > 0)
    {
        Print("注文成功！ チケット番号: ", ticket);
        break;
    }
    else
    {
        int err = GetLastError();
        Print("注文失敗（", i + 1, "回目） エラー: ", err);
        Sleep(1000);  // 1秒待ってからリトライ
    }
}
</code></pre>
<h3><span id="toc4">Sleep()の注意点</span></h3>
<ul>
<li><strong>ストラテジーテスターでは無視されます。</strong>バックテスト中にSleep()を呼んでも、実際には一時停止しません。テスト速度に影響を与えないための仕様です。</li>
<li>Sleep()の内部では<strong>約0.1秒ごと</strong>にEAの停止フラグがチェックされています。そのため、Sleep(10000)（10秒停止）を実行中でも、ユーザーがEAを停止すれば比較的すぐに処理が終了します。</li>
<li>カスタムインジケーターの中では使用できません。EAおよびスクリプト専用の関数です。</li>
</ul>
<h2><span id="toc5">RefreshRates() — 価格データを最新の状態に更新する</span></h2>
<h3><span id="toc6">基本構文</span></h3>
<pre><code class="language-mql4">bool RefreshRates();
</code></pre>
<p><strong>RefreshRates()</strong>は、Bid、Ask、Volumeなどの定義済み変数を最新のサーバーデータに更新する関数です。更新に成功するとtrueを返します。</p>
<h3><span id="toc7">なぜRefreshRates()が必要なのか</span></h3>
<p>EAの<code>OnTick()</code>が呼び出された瞬間に、BidやAskには最新の値がセットされます。しかし、その後にSleep()で待機したり、重い計算処理を行ったりすると、BidやAskの値が古くなってしまう可能性があります。</p>
<p>つまり、<strong>Sleep()の後にはRefreshRates()を呼ぶのが鉄則</strong>です。</p>
<h3><span id="toc8">使用例：Sleep()とRefreshRates()のセット</span></h3>
<pre><code class="language-mql4">// 注文失敗 → 待機 → 価格を更新してリトライ
Sleep(1000);
RefreshRates();  // Bid/Askを最新に更新

// 更新後のAskで再度注文
ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "Retry Order", 0, 0, clrBlue);
</code></pre>
<p>RefreshRates()を呼ばずに古いAsk値で注文を出すと、「Off quotes（無効な価格）」エラーが発生する原因になります。注意しましょう。</p>
<h2><span id="toc9">IsTradeAllowed() — 自動売買が許可されているか確認する</span></h2>
<h3><span id="toc10">基本構文</span></h3>
<pre><code class="language-mql4">bool IsTradeAllowed();
</code></pre>
<p><strong>IsTradeAllowed()</strong>は、EAによる自動売買が許可されている場合にtrueを返す関数です。具体的には、以下の2つの条件を確認しています。</p>
<ul>
<li>MetaTraderのツールバーにある<strong>「自動売買」ボタンがON</strong>になっているか</li>
<li>他のEAやスクリプトが取引コンテキストを使用中（ビジー状態）でないか</li>
</ul>
<h3><span id="toc11">使用例：注文前の安全チェック</span></h3>
<pre><code class="language-mql4">void OnTick()
{
    // 自動売買が許可されていなければ何もしない
    if(!IsTradeAllowed())
    {
        Print("自動売買が無効です。ツールバーの自動売買ボタンを確認してください。");
        return;
    }

    // ここから注文ロジック
    // ...
}
</code></pre>
<h3><span id="toc12">取引コンテキストのビジー状態に対処する</span></h3>
<p>同一のMetaTrader上で複数のEAが動作している場合、1つのEAが注文処理を行っている間は他のEAの取引がブロックされます。このような場合は、少し待ってから再確認するのが有効です。</p>
<pre><code class="language-mql4">// 取引コンテキストが空くまで待機（最大5秒）
for(int i = 0; i < 10; i++)
{
    if(IsTradeAllowed())
        break;

    Print("取引コンテキストがビジー状態です。待機中...");
    Sleep(500);
}
</code></pre>
<h2><span id="toc13">IsConnected() — サーバーとの接続状態を確認する</span></h2>
<h3><span id="toc14">基本構文</span></h3>
<pre><code class="language-mql4">bool IsConnected();
</code></pre>
<p><strong>IsConnected()</strong>は、MetaTraderクライアントがブローカーのサーバーと接続されている場合にtrueを返す関数です。</p>
<h3><span id="toc15">使用例：接続チェック</span></h3>
<pre><code class="language-mql4">void OnTick()
{
    if(!IsConnected())
    {
        Print("サーバーに接続されていません！ 注文処理をスキップします。");
        return;
    }

    // 接続確認OK → 通常の処理へ
}
</code></pre>
<h3><span id="toc16">VPS運用時に特に重要</span></h3>
<p>VPS（仮想専用サーバー）でEAを24時間稼働させている場合、ネットワークの一時的な切断は避けられません。IsConnected()で接続状態をチェックし、切断時にはアラートを出すなどの処理を組み込んでおくと安心です。</p>
<pre><code class="language-mql4">static datetime lastAlertTime = 0;

if(!IsConnected())
{
    // 5分に1回だけアラート
    if(TimeCurrent() - lastAlertTime > 300)
    {
        Alert("サーバー接続が切断されています！");
        lastAlertTime = TimeCurrent();
    }
    return;
}
</code></pre>
<h2><span id="toc17">IsTesting() — バックテスト中かどうかを判定する</span></h2>
<h3><span id="toc18">基本構文</span></h3>
<pre><code class="language-mql4">bool IsTesting();
</code></pre>
<p><strong>IsTesting()</strong>は、EAがストラテジーテスター上で動作しているときにtrueを返す関数です。ライブ口座やデモ口座で動かしている場合はfalseになります。</p>
<h3><span id="toc19">使用例：テスト時にGUI処理をスキップして高速化</span></h3>
<p>バックテスト（特にビジュアルモードではない通常テスト）では、画面への描画処理は不要です。Comment()やObjectCreate()といった描画関数をスキップすることで、テスト速度を大幅に向上できます。</p>
<pre><code class="language-mql4">// グローバル変数としてフラグを用意
bool gIsTesting = false;

int OnInit()
{
    // init()内で1度だけ判定すれば十分
    gIsTesting = IsTesting();
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    // テスト中でなければ画面に情報を表示
    if(!gIsTesting)
    {
        Comment("Bid: ", Bid, "  Ask: ", Ask, "  Spread: ", MarketInfo(Symbol(), MODE_SPREAD));
    }

    // 売買ロジック...
}
</code></pre>
<p><strong>ポイント：</strong>IsTesting()の戻り値はEAの実行中に変わることはありません。そのため、<code>OnInit()</code>内で一度だけ判定してグローバル変数に保存すれば十分です。OnTick()のたびに呼び出す必要はなく、わずかですがパフォーマンスの改善につながります。</p>
<h2><span id="toc20">IsOptimization() — 最適化モードかどうかを判定する</span></h2>
<h3><span id="toc21">基本構文</span></h3>
<pre><code class="language-mql4">bool IsOptimization();
</code></pre>
<p><strong>IsOptimization()</strong>は、ストラテジーテスターの最適化モードで実行されているときにtrueを返す関数です。最適化モードでは、パラメータの組み合わせを大量にテストするため、不要な処理を極力省くことが重要になります。</p>
<h3><span id="toc22">使用例：最適化中はPrint()を抑制する</span></h3>
<p>最適化モードでは、<strong>Print()による出力がログに記録されません</strong>。つまり、Print()を呼んでもデバッグの役に立たないだけでなく、処理のオーバーヘッドだけが残ります。</p>
<pre><code class="language-mql4">bool gIsOptimization = false;

int OnInit()
{
    gIsOptimization = IsOptimization();
    return(INIT_SUCCEEDED);
}

void OnTick()
{
    // 最適化中はデバッグ出力をスキップ
    if(!gIsOptimization)
    {
        Print("現在のBid: ", Bid, " Ask: ", Ask);
    }

    // 売買ロジック...
}
</code></pre>
<h3><span id="toc23">IsTesting()との使い分け</span></h3>
<p>IsTesting()とIsOptimization()の関係を整理しておきましょう。</p>
<table>
<thead>
<tr>
<th>状態</th>
<th>IsTesting()</th>
<th>IsOptimization()</th>
</tr>
</thead>
<tbody>
<tr>
<td>ライブ口座 / デモ口座</td>
<td>false</td>
<td>false</td>
</tr>
<tr>
<td>バックテスト（通常）</td>
<td>true</td>
<td>false</td>
</tr>
<tr>
<td>バックテスト（ビジュアルモード）</td>
<td>true</td>
<td>false</td>
</tr>
<tr>
<td>最適化モード</td>
<td>true</td>
<td>true</td>
</tr>
</tbody>
</table>
<p>最適化モードのときはIsTesting()もtrueになります。つまり「IsTesting()がtrueかつIsOptimization()がfalse」であれば通常のバックテスト、「両方ともtrue」であれば最適化中ということになります。</p>
<h2><span id="toc24">実践テンプレート：6つの関数を組み合わせた安全な注文処理</span></h2>
<p>ここまで紹介した6つの関数をすべて組み合わせた、実践的な注文処理テンプレートを紹介します。実際のEA開発でそのまま活用できる構成です。</p>
<pre><code class="language-mql4">//--- グローバル変数
bool gIsTesting      = false;
bool gIsOptimization = false;

//+------------------------------------------------------------------+
//| 初期化関数                                                         |
//+------------------------------------------------------------------+
int OnInit()
{
    // 実行環境フラグを初期化時に1度だけ判定
    gIsTesting      = IsTesting();
    gIsOptimization = IsOptimization();

    if(!gIsOptimization)
        Print("EA初期化完了 テストモード: ", gIsTesting);

    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 安全な買い注文関数                                                  |
//+------------------------------------------------------------------+
int SafeBuyOrder(double lots, int slippage, double sl, double tp, string comment)
{
    int maxRetry = 3;
    int ticket   = -1;

    //--- 1. サーバー接続チェック（テスト時はスキップ）
    if(!gIsTesting &amp;&amp; !IsConnected())
    {
        Print("エラー: サーバーに接続されていません");
        return(-1);
    }

    //--- 2. 自動売買許可チェック
    if(!IsTradeAllowed())
    {
        Print("エラー: 自動売買が許可されていません");
        return(-1);
    }

    //--- 3. リトライループ
    for(int i = 0; i < maxRetry; i++)
    {
        // 価格データを最新に更新
        RefreshRates();

        // 注文送信
        ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, slippage, sl, tp, comment, 0, 0, clrBlue);

        if(ticket > 0)
        {
            if(!gIsOptimization)
                Print("買い注文成功！ チケット: ", ticket, " 価格: ", Ask);
            break;
        }
        else
        {
            int err = GetLastError();
            if(!gIsOptimization)
                Print("買い注文失敗（", i + 1, "/", maxRetry, "） エラーコード: ", err);

            // 最後のリトライでなければ待機
            if(i < maxRetry - 1)
            {
                Sleep(1000);  // 1秒待機（テスターでは無視される）
            }
        }
    }

    return(ticket);
}

//+------------------------------------------------------------------+
//| ティックごとの処理                                                  |
//+------------------------------------------------------------------+
void OnTick()
{
    //--- 画面表示（通常バックテスト・最適化時はスキップ）
    if(!gIsTesting)
    {
        Comment("EA稼働中 | Bid: ", Bid, " | Ask: ", Ask,
                " | 接続: ", (IsConnected() ? "OK" : "NG"),
                " | 自動売買: ", (IsTradeAllowed() ? "許可" : "不可"));
    }

    //--- ここに売買条件を記述 ---
    // 例: 買い条件を満たしたら注文
    // int ticket = SafeBuyOrder(0.1, 3, 0, 0, "Sample EA");
}

//+------------------------------------------------------------------+
//| 終了処理                                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    if(!gIsOptimization)
        Print("EA停止 理由コード: ", reason);

    // テスト時でなければコメントをクリア
    if(!gIsTesting)
        Comment("");
}
</code></pre>
<p>このテンプレートでは、以下のポイントを押さえています。</p>
<ul>
<li><strong>OnInit()で環境フラグを一度だけ判定</strong> — IsTesting()やIsOptimization()を毎ティック呼ぶ無駄を排除</li>
<li><strong>注文前にIsConnected()とIsTradeAllowed()をチェック</strong> — 接続切れや自動売買無効時のエラーを未然に防止</li>
<li><strong>リトライ時にSleep() + RefreshRates()をセットで使用</strong> — 待機後に最新価格を取得してから再注文</li>
<li><strong>最適化中はPrint()をスキップ</strong> — 不要な処理のオーバーヘッドを削減</li>
<li><strong>テスト中はComment()による画面描画をスキップ</strong> — バックテストの高速化</li>
</ul>
<h2><span id="toc25">まとめ</span></h2>
<p>今回紹介した6つのユーティリティ関数を一覧表で振り返りましょう。</p>
<table>
<thead>
<tr>
<th>関数名</th>
<th>用途</th>
<th>主な使いどころ</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sleep()</td>
<td>処理の一時停止</td>
<td>注文リトライ時の待機</td>
</tr>
<tr>
<td>RefreshRates()</td>
<td>価格データの更新</td>
<td>Sleep()の後、重い計算の後</td>
</tr>
<tr>
<td>IsTradeAllowed()</td>
<td>自動売買許可の確認</td>
<td>注文送信前のチェック</td>
</tr>
<tr>
<td>IsConnected()</td>
<td>サーバー接続の確認</td>
<td>注文送信前、VPS運用時</td>
</tr>
<tr>
<td>IsTesting()</td>
<td>バックテスト判定</td>
<td>GUI処理のスキップ</td>
</tr>
<tr>
<td>IsOptimization()</td>
<td>最適化モード判定</td>
<td>Print()の抑制</td>
</tr>
</tbody>
</table>
<p>これらの関数は単体で使うだけでなく、<strong>組み合わせて使うことで真価を発揮</strong>します。特に「Sleep() → RefreshRates()」のセットや、「IsTesting() / IsOptimization()による処理の出し分け」は、EA開発における定番テクニックです。</p>
<p>今回紹介した実践テンプレートをベースに、ご自身のEAに安全で効率的な処理を組み込んでみてください。堅牢なEAを作る第一歩になるはずです。</p>
<p>投稿 <a href="https://mql-programing.com/archives/13065/%e3%80%90mql4%e3%80%91%e7%9f%a5%e3%81%a3%e3%81%a6%e3%81%8a%e3%81%8d%e3%81%9f%e3%81%84%e3%83%a6%e3%83%bc%e3%83%86%e3%82%a3%e3%83%aa%e3%83%86%e3%82%a3%e9%96%a2%e6%95%b06%e9%81%b8%ef%bc%81sleep%e3%83%bbr/">【MQL4】知っておきたいユーティリティ関数6選！Sleep・RefreshRates・IsTradeAllowed・IsConnected・IsTesting・IsOptimizationの使い方</a> は <a href="https://mql-programing.com">自動売買を作ろう！</a> に最初に表示されました。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【MQL4関数】OnTester関数とは？バックテスト終了時に実行されるイベント関数</title>
		<link>https://mql-programing.com/archives/3050/ontester/</link>
		
		<dc:creator><![CDATA[朝日奈りさ]]></dc:creator>
		<pubDate>Tue, 17 May 2022 08:02:17 +0000</pubDate>
				<category><![CDATA[関数]]></category>
		<category><![CDATA[【中級編】MQLプログラムの読み方・書き方]]></category>
		<category><![CDATA[【辞書】MQLリファレンス]]></category>
		<category><![CDATA[その他]]></category>
		<category><![CDATA[シャープレシオ]]></category>
		<category><![CDATA[MQL]]></category>
		<category><![CDATA[リファレンス]]></category>
		<category><![CDATA[自動売買]]></category>
		<category><![CDATA[イベント関数]]></category>
		<category><![CDATA[OnTester]]></category>
		<category><![CDATA[バックテスト]]></category>
		<guid isPermaLink="false">https://mql-programing.com/?p=3050</guid>

					<description><![CDATA[<p>OnTester関数の基本 OnTester()は、ストラテジーテスター（バックテスト）の終了時に自動的に呼び出されるイベント関数です。バックテスト完了後に独自の評価指標（カスタム最適化基準）を計算し、その結果を最適化プ [&#8230;]</p>
<p>投稿 <a href="https://mql-programing.com/archives/3050/ontester/">【MQL4関数】OnTester関数とは？バックテスト終了時に実行されるイベント関数</a> は <a href="https://mql-programing.com">自動売買を作ろう！</a> に最初に表示されました。</p>
]]></description>
										<content:encoded><![CDATA[<h2><span id="toc1">OnTester関数の基本</span></h2>
<p>OnTester()は、<strong>ストラテジーテスター（バックテスト）の終了時に自動的に呼び出されるイベント関数</strong>です。バックテスト完了後に独自の評価指標（カスタム最適化基準）を計算し、その結果を最適化プロセスに反映させることができます。</p>
<p>通常のバックテストでは、MetaTraderが用意するプロフィットファクターやシャープレシオなどの標準指標しか最適化基準に使えませんが、OnTester()を実装することで<strong>自分だけのオリジナル評価指標</strong>を最適化基準として使用できるようになります。</p>
<h2><span id="toc2">書式・引数・戻り値</span></h2>
<pre><code class="language-mql4">double OnTester(void);
</code></pre>
<h3><span id="toc3">引数</span></h3>
<p>引数はありません（void）。</p>
<h3><span id="toc4">戻り値</span></h3>
<table>
<thead>
<tr>
<th>型</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>double</td>
<td>カスタム最適化基準として使用される値。ストラテジーテスターの最適化時に「Custom max」を選択すると、この戻り値が最大化される方向で最適化が行われます。</td>
</tr>
</tbody>
</table>
<h3><span id="toc5">基本的な使い方</span></h3>
<p>OnTester()はEA（エキスパートアドバイザー）内に記述します。ストラテジーテスターでバックテストが完了した直後、OnDeinit()が呼ばれる前に実行されます。</p>
<pre><code class="language-mql4">// OnTester()の基本構造
double OnTester()
{
   // バックテスト結果を評価する処理
   double customCriterion = 0.0;
   
   // 何らかの計算を行う
   customCriterion = 計算結果;
   
   // 戻り値が「Custom max」最適化基準として使われる
   return(customCriterion);
}
</code></pre>
<h2><span id="toc6">呼び出しタイミング</span></h2>
<p>OnTester()が呼び出されるタイミングは以下の通りです。</p>
<ul>
<li><strong>バックテスト終了後</strong>、最後のティック処理が完了した直後</li>
<li><strong>OnDeinit()の前</strong>に呼ばれる</li>
<li><strong>ストラテジーテスター内でのみ</strong>動作する（ライブ取引では呼ばれない）</li>
<li>最適化実行時は<strong>各パス（パラメータの組み合わせ）ごと</strong>に呼ばれる</li>
</ul>
<p>呼び出し順序をまとめると以下のようになります。</p>
<pre><code class="language-mql4">// イベント関数の呼び出し順序（ストラテジーテスター）
// 1. OnInit()          ← テスト開始時
// 2. OnTick()          ← 各ティックごと（繰り返し）
// 3. OnTester()        ← テスト終了後
// 4. OnDeinit()        ← EA解放時
</code></pre>
<h2><span id="toc7">プログラム例1：基本的な損益ベースの評価指標</span></h2>
<p>最もシンプルな例として、<strong>総利益と総損失の比率から独自のスコアを算出</strong>するOnTester()を実装します。</p>
<pre><code class="language-mql4">//+------------------------------------------------------------------+
//| プログラム例1：基本的な損益評価指標                                  |
//| 総利益÷総損失（プロフィットファクター的な指標）を返す                    |
//+------------------------------------------------------------------+
#property strict

// EA本体のパラメータ（例）
input int    MAPeriod   = 20;     // 移動平均期間
input double LotSize    = 0.1;    // ロットサイズ
input int    TakeProfit = 100;    // 利確（ポイント）
input int    StopLoss   = 50;     // 損切り（ポイント）

//+------------------------------------------------------------------+
//| 初期化関数                                                         |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| ティック処理（簡易的なMA戦略の例）                                   |
//+------------------------------------------------------------------+
void OnTick()
{
   // 既存ポジションがあれば何もしない
   if(OrdersTotal() > 0) return;
   
   double ma = iMA(NULL, 0, MAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double prevClose = iClose(NULL, 0, 1);
   double prevClose2 = iClose(NULL, 0, 2);
   
   // 価格がMAを上抜けたら買い
   if(prevClose2 < ma &#038;&#038; prevClose > ma)
   {
      OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3,
                Ask - StopLoss * Point, Ask + TakeProfit * Point,
                "MA Cross Buy", 0, 0, clrBlue);
   }
   // 価格がMAを下抜けたら売り
   else if(prevClose2 > ma && prevClose < ma)
   {
      OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3,
                Bid + StopLoss * Point, Bid - TakeProfit * Point,
                "MA Cross Sell", 0, 0, clrRed);
   }
}

//+------------------------------------------------------------------+
//| バックテスト終了時に呼ばれるイベント関数                              |
//+------------------------------------------------------------------+
double OnTester()
{
   // TesterStatistics()でバックテスト結果の統計を取得
   double totalProfit = TesterStatistics(STAT_GROSS_PROFIT);  // 総利益
   double totalLoss   = TesterStatistics(STAT_GROSS_LOSS);    // 総損失（負の値）
   double totalTrades = TesterStatistics(STAT_TRADES);        // 総取引回数
   
   // 取引が一定回数以上ない場合は評価しない
   if(totalTrades < 10)
   {
      Print("取引回数が少なすぎます: ", totalTrades, " 回");
      return(0.0);
   }
   
   // 総損失が0の場合（全勝の場合）の処理
   if(totalLoss == 0.0)
   {
      Print("全勝！ 総利益: ", totalProfit);
      return(totalProfit);
   }
   
   // プロフィットファクター（総利益÷総損失の絶対値）を計算
   double profitFactor = totalProfit / MathAbs(totalLoss);
   
   Print("カスタム評価値（PF）: ", DoubleToStr(profitFactor, 2),
         " | 取引回数: ", totalTrades,
         " | 総利益: ", DoubleToStr(totalProfit, 2),
         " | 総損失: ", DoubleToStr(totalLoss, 2));
   
   // この値が「Custom max」最適化基準として使用される
   return(profitFactor);
}

//+------------------------------------------------------------------+
//| 終了処理                                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Print("EA終了 - 理由コード: ", reason);
}
</code></pre>
<p>この例では<code>TesterStatistics()</code>関数を使ってバックテスト結果の統計データを取得しています。取引回数が少なすぎる場合は0を返すことで、信頼性の低い結果を排除しています。</p>
<h2><span id="toc8">プログラム例2：リカバリーファクターによる評価</span></h2>
<p><strong>リカバリーファクター（純利益÷最大ドローダウン）</strong>は、リスクに対してどれだけ効率的に利益を上げているかを示す重要な指標です。この値をカスタム最適化基準として使います。</p>
<pre><code class="language-mql4">//+------------------------------------------------------------------+
//| プログラム例2：リカバリーファクターで最適化                            |
//| 純利益÷最大ドローダウンを評価指標として返す                           |
//+------------------------------------------------------------------+
#property strict

input int    FastMA = 10;   // 短期MA期間
input int    SlowMA = 30;   // 長期MA期間
input double Lots   = 0.1;  // ロットサイズ

//+------------------------------------------------------------------+
//| 初期化関数                                                         |
//+------------------------------------------------------------------+
int OnInit()
{
   // 短期MAが長期MAより大きくならないようにバリデーション
   if(FastMA >= SlowMA)
   {
      Print("FastMAはSlowMAより小さくしてください");
      return(INIT_PARAMETERS_INCORRECT);
   }
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| ティック処理（ゴールデンクロス・デッドクロス戦略）                     |
//+------------------------------------------------------------------+
void OnTick()
{
   if(OrdersTotal() > 0) return;
   
   double fastCurr = iMA(NULL, 0, FastMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double fastPrev = iMA(NULL, 0, FastMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   double slowCurr = iMA(NULL, 0, SlowMA, 0, MODE_EMA, PRICE_CLOSE, 1);
   double slowPrev = iMA(NULL, 0, SlowMA, 0, MODE_EMA, PRICE_CLOSE, 2);
   
   // ゴールデンクロス→買い
   if(fastPrev <= slowPrev &#038;&#038; fastCurr > slowCurr)
   {
      OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, 0, 0, "GC Buy", 12345, 0, clrBlue);
   }
   // デッドクロス→売り
   else if(fastPrev >= slowPrev && fastCurr < slowCurr)
   {
      OrderSend(Symbol(), OP_SELL, Lots, Bid, 3, 0, 0, "DC Sell", 12345, 0, clrRed);
   }
}

//+------------------------------------------------------------------+
//| バックテスト終了時：リカバリーファクターを計算して返す                  |
//+------------------------------------------------------------------+
double OnTester()
{
   // 各種統計値を取得
   double netProfit      = TesterStatistics(STAT_PROFIT);              // 純利益
   double maxDrawdown    = TesterStatistics(STAT_EQUITY_DD);           // 最大ドローダウン（金額）
   double totalTrades    = TesterStatistics(STAT_TRADES);              // 総取引回数
   double profitTrades   = TesterStatistics(STAT_PROFIT_TRADES);       // 勝ちトレード数
   double expectedPayoff = TesterStatistics(STAT_EXPECTED_PAYOFF);     // 期待利得
   
   // ログに詳細を出力
   Print("=== バックテスト結果サマリー ===");
   Print("純利益: ", DoubleToStr(netProfit, 2));
   Print("最大DD: ", DoubleToStr(maxDrawdown, 2));
   Print("取引数: ", (int)totalTrades);
   Print("勝率:   ", DoubleToStr(profitTrades / MathMax(totalTrades, 1) * 100, 1), "%");
   Print("期待利得: ", DoubleToStr(expectedPayoff, 2));
   
   // 最低取引回数のフィルター
   if(totalTrades < 30)
   {
      Print("取引回数不足（30回未満）→ 評価値0");
      return(0.0);
   }
   
   // 最大ドローダウンが0または利益がマイナスの場合
   if(maxDrawdown <= 0.0 || netProfit <= 0.0)
   {
      Print("利益なしまたはDD計算不可 → 評価値0");
      return(0.0);
   }
   
   // リカバリーファクター = 純利益 ÷ 最大ドローダウン
   double recoveryFactor = netProfit / maxDrawdown;
   
   Print("リカバリーファクター: ", DoubleToStr(recoveryFactor, 3));
   
   return(recoveryFactor);
}

//+------------------------------------------------------------------+
//| 終了処理                                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
</code></pre>
<p>リカバリーファクターが高いほど、少ないリスク（ドローダウン）で大きな利益を上げていることを意味します。最適化時に「Custom max」を選択すれば、この値が最大化されるパラメータの組み合わせが探索されます。</p>
<h2><span id="toc9">プログラム例3：複合スコアによる多角的評価</span></h2>
<p>実際のトレーディングでは、単一の指標だけでなく<strong>複数の指標を組み合わせた総合スコア</strong>で評価するのが効果的です。この例では、プロフィットファクター・勝率・取引回数・ドローダウン率を組み合わせた複合スコアを計算します。</p>
<pre><code class="language-mql4">//+------------------------------------------------------------------+
//| プログラム例3：複合スコアによる多角的評価                              |
//| 複数の評価指標を重み付けして総合スコアを算出                           |
//+------------------------------------------------------------------+
#property strict

input int    RSIPeriod  = 14;    // RSI期間
input int    RSIBuyLv   = 30;    // RSI買いレベル
input int    RSISellLv  = 70;    // RSI売りレベル
input double Lots       = 0.1;   // ロットサイズ
input int    SL_Points  = 200;   // 損切り幅（ポイント）
input int    TP_Points  = 300;   // 利確幅（ポイント）

//+------------------------------------------------------------------+
//| 初期化                                                             |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| ティック処理（RSI戦略の例）                                         |
//+------------------------------------------------------------------+
void OnTick()
{
   // 既にポジションがあれば何もしない
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(OrderSelect(i, SELECT_BY_POS) && OrderSymbol() == Symbol())
         return;
   }
   
   double rsiCurr = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 1);
   double rsiPrev = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 2);
   
   // RSIが売られすぎ水準から回復→買い
   if(rsiPrev < RSIBuyLv &#038;&#038; rsiCurr >= RSIBuyLv)
   {
      double sl = Ask - SL_Points * Point;
      double tp = Ask + TP_Points * Point;
      OrderSend(Symbol(), OP_BUY, Lots, Ask, 3, sl, tp, "RSI Buy", 0, 0, clrBlue);
   }
   // RSIが買われすぎ水準から下落→売り
   else if(rsiPrev > RSISellLv && rsiCurr <= RSISellLv)
   {
      double sl = Bid + SL_Points * Point;
      double tp = Bid - TP_Points * Point;
      OrderSend(Symbol(), OP_SELL, Lots, Bid, 3, sl, tp, "RSI Sell", 0, 0, clrRed);
   }
}

//+------------------------------------------------------------------+
//| 複合スコアを計算する関数                                            |
//+------------------------------------------------------------------+
double CalculateCompositeScore()
{
   // ---- 統計データの取得 ----
   double netProfit     = TesterStatistics(STAT_PROFIT);
   double grossProfit   = TesterStatistics(STAT_GROSS_PROFIT);
   double grossLoss     = TesterStatistics(STAT_GROSS_LOSS);     // 負の値
   double totalTrades   = TesterStatistics(STAT_TRADES);
   double profitTrades  = TesterStatistics(STAT_PROFIT_TRADES);
   double maxDDPercent  = TesterStatistics(STAT_EQUITY_DDREL_PERCENT); // 最大DD（%）
   double maxDD         = TesterStatistics(STAT_EQUITY_DD);
   double sharpRatio    = TesterStatistics(STAT_SHARPE_RATIO);
   
   // ---- フィルター条件 ----
   // 取引回数が少なすぎる結果を除外
   if(totalTrades < 20)
   {
      Print("フィルター: 取引回数不足 (", (int)totalTrades, " < 20)");
      return(-1000.0);  // 大きな負の値で排除
   }
   
   // 純利益がマイナスの結果を除外
   if(netProfit <= 0)
   {
      Print("フィルター: 純利益がマイナス (", DoubleToStr(netProfit, 2), ")");
      return(-500.0);
   }
   
   // 最大ドローダウンが大きすぎる結果を除外（30%超）
   if(maxDDPercent > 30.0)
   {
      Print("フィルター: DD過大 (", DoubleToStr(maxDDPercent, 1), "% > 30%)");
      return(-100.0);
   }
   
   // ---- 各指標のスコア計算 ----
   
   // 1. プロフィットファクター（0〜10にスケーリング）
   double pf = 0.0;
   if(MathAbs(grossLoss) > 0)
      pf = grossProfit / MathAbs(grossLoss);
   double pfScore = MathMin(pf, 5.0) * 2.0;  // PF=5以上は全て10点
   
   // 2. 勝率スコア（0〜10にスケーリング）
   double winRate = profitTrades / totalTrades * 100.0;
   double winRateScore = MathMin(winRate, 100.0) / 10.0;  // 勝率100%で10点
   
   // 3. 取引回数スコア（多いほど信頼性が高い）
   // 100回以上で満点（10点）
   double tradeCountScore = MathMin(totalTrades / 100.0, 1.0) * 10.0;
   
   // 4. ドローダウン抑制スコア（DDが小さいほど高評価）
   // DD 0%→10点、DD 30%→0点
   double ddScore = MathMax(0.0, (30.0 - maxDDPercent) / 30.0 * 10.0);
   
   // ---- 重み付けによる総合スコア ----
   double weightPF       = 0.35;  // プロフィットファクター重視
   double weightWinRate  = 0.20;  // 勝率
   double weightTrades   = 0.15;  // 取引回数（信頼性）
   double weightDD       = 0.30;  // ドローダウン抑制
   
   double compositeScore = pfScore       * weightPF
                         + winRateScore  * weightWinRate
                         + tradeCountScore * weightTrades
                         + ddScore       * weightDD;
   
   // ---- 結果のログ出力 ----
   Print("=== 複合スコア詳細 ===");
   Print("  PFスコア:     ", DoubleToStr(pfScore, 2), " (PF=", DoubleToStr(pf, 2), ")");
   Print("  勝率スコア:   ", DoubleToStr(winRateScore, 2), " (勝率=", DoubleToStr(winRate, 1), "%)");
   Print("  取引数スコア: ", DoubleToStr(tradeCountScore, 2), " (取引数=", (int)totalTrades, ")");
   Print("  DDスコア:     ", DoubleToStr(ddScore, 2), " (DD=", DoubleToStr(maxDDPercent, 1), "%)");
   Print("  ★総合スコア:  ", DoubleToStr(compositeScore, 3));
   
   return(compositeScore);
}

//+------------------------------------------------------------------+
//| バックテスト終了時のイベント                                         |
//+------------------------------------------------------------------+
double OnTester()
{
   return(CalculateCompositeScore());
}

//+------------------------------------------------------------------+
//| 終了処理                                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
</code></pre>
<p>この例のポイントは以下の通りです。</p>
<ul>
<li><strong>フィルター機能</strong>：取引回数が少ない、利益がマイナス、ドローダウンが大きすぎる結果を大きな負の値で排除</li>
<li><strong>スケーリング</strong>：各指標を0〜10のスコアに正規化してから合算</li>
<li><strong>重み付け</strong>：プロフィットファクターとドローダウンを重視した設計</li>
</ul>
<h2><span id="toc10">プログラム例4：取引履歴を直接分析してカスタム指標を算出</span></h2>
<p>TesterStatistics()だけでなく、<strong>決済済み注文の履歴を直接走査</strong>して、連続損失回数や最大連敗額など、より細かな分析を行う例です。</p>
<pre><code class="language-mql4">//+------------------------------------------------------------------+
//| プログラム例4：取引履歴を直接分析する高度な評価                        |
//| 連続損失・利益の安定性を評価指標に組み込む                             |
//+------------------------------------------------------------------+
#property strict

input int    Period1    = 12;
input int    Period2    = 26;
input double LotSize   = 0.1;

//+------------------------------------------------------------------+
//| 初期化                                                             |
//+------------------------------------------------------------------+
int OnInit()
{
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| ティック処理（省略：任意の売買ロジックを実装）                         |
//+------------------------------------------------------------------+
void OnTick()
{
   // ここには任意のトレードロジックを実装
   // （本例ではOnTester()の解説に焦点を当てるため省略）
}

//+------------------------------------------------------------------+
//| バックテスト終了時：取引履歴を直接分析                                |
//+------------------------------------------------------------------+
double OnTester()
{
   // 決済済み注文の履歴を分析
   int totalOrders = OrdersHistoryTotal();
   
   if(totalOrders == 0)
   {
      Print("取引履歴なし");
      return(0.0);
   }
   
   // ---- 分析用変数の初期化 ----
   double profits[];          // 各取引の損益を格納する配列
   int    tradeCount = 0;     // 有効な取引数
   int    maxConsecLoss = 0;  // 最大連敗数
   int    currentConsecLoss = 0;  // 現在の連敗カウント
   double maxConsecLossAmount = 0.0;  // 最大連敗時の損失額
   double currentConsecLossAmount = 0.0;
   double totalProfit = 0.0;  // 総利益
   
   // 配列サイズを事前確保
   ArrayResize(profits, totalOrders);
   
   // ---- 取引履歴をスキャンして損益データを収集 ----
   for(int i = 0; i < totalOrders; i++)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
         continue;
      
      // 対象シンボルの決済注文のみ処理
      if(OrderSymbol() != Symbol())
         continue;
      
      // OP_BUYまたはOP_SELLのみ（入金・出金等を除外）
      int type = OrderType();
      if(type != OP_BUY &#038;&#038; type != OP_SELL)
         continue;
      
      // 損益（手数料・スワップ込み）
      double orderProfit = OrderProfit() + OrderCommission() + OrderSwap();
      profits[tradeCount] = orderProfit;
      totalProfit += orderProfit;
      tradeCount++;
      
      // 連敗トラッキング
      if(orderProfit < 0)
      {
         currentConsecLoss++;
         currentConsecLossAmount += MathAbs(orderProfit);
         
         if(currentConsecLoss > maxConsecLoss)
         {
            maxConsecLoss = currentConsecLoss;
            maxConsecLossAmount = currentConsecLossAmount;
         }
      }
      else
      {
         // 勝ちトレードで連敗リセット
         currentConsecLoss = 0;
         currentConsecLossAmount = 0.0;
      }
   }
   
   // 有効な取引がない場合
   if(tradeCount < 10)
   {
      Print("有効取引数不足: ", tradeCount);
      return(0.0);
   }
   
   // 配列サイズを実際の取引数にリサイズ
   ArrayResize(profits, tradeCount);
   
   // ---- 利益の安定性（標準偏差）を計算 ----
   double meanProfit = totalProfit / tradeCount;
   double sumSqDiff = 0.0;
   
   for(int j = 0; j < tradeCount; j++)
   {
      double diff = profits[j] - meanProfit;
      sumSqDiff += diff * diff;
   }
   
   double stdDev = MathSqrt(sumSqDiff / tradeCount);
   
   // ---- カスタムシャープレシオ的な指標 ----
   // 平均利益 ÷ 標準偏差（大きいほど安定して利益を出している）
   double customSharpe = 0.0;
   if(stdDev > 0)
      customSharpe = meanProfit / stdDev;
   
   // ---- 連敗ペナルティを適用 ----
   // 最大連敗が多いほどペナルティを与える
   double consecLossPenalty = 1.0;
   if(maxConsecLoss > 5)
      consecLossPenalty = 5.0 / maxConsecLoss;  // 5連敗超でペナルティ
   
   // ---- 最終スコアの計算 ----
   // カスタムシャープ × 連敗ペナルティ × √取引回数（信頼性補正）
   double finalScore = customSharpe * consecLossPenalty * MathSqrt(tradeCount);
   
   // ---- 詳細ログ出力 ----
   Print("=== 取引履歴分析結果 ===");
   Print("有効取引数: ", tradeCount);
   Print("総損益: ", DoubleToStr(totalProfit, 2));
   Print("平均損益: ", DoubleToStr(meanProfit, 2));
   Print("損益の標準偏差: ", DoubleToStr(stdDev, 2));
   Print("カスタムシャープ: ", DoubleToStr(customSharpe, 4));
   Print("最大連敗数: ", maxConsecLoss, " (損失額: ", DoubleToStr(maxConsecLossAmount, 2), ")");
   Print("連敗ペナルティ: ", DoubleToStr(consecLossPenalty, 2));
   Print("★最終スコア: ", DoubleToStr(finalScore, 4));
   
   return(finalScore);
}

//+------------------------------------------------------------------+
//| 終了処理                                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
</code></pre>
<p>この例では、<code>OrdersHistoryTotal()</code>と<code>OrderSelect()</code>を使って決済済み注文を1件ずつ走査しています。TesterStatistics()では取得できない連敗数や損益の標準偏差といった情報を、取引履歴から直接計算している点がポイントです。</p>
<h2><span id="toc11">TesterStatistics()で取得できる主な統計定数</span></h2>
<p>OnTester()内で使用する<code>TesterStatistics()</code>関数で取得できる代表的な統計定数を一覧にまとめます。</p>
<table>
<thead>
<tr>
<th>定数名</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>STAT_PROFIT</td>
<td>純利益（総利益 - 総損失）</td>
</tr>
<tr>
<td>STAT_GROSS_PROFIT</td>
<td>総利益</td>
</tr>
<tr>
<td>STAT_GROSS_LOSS</td>
<td>総損失（負の値）</td>
</tr>
<tr>
<td>STAT_TRADES</td>
<td>総取引回数</td>
</tr>
<tr>
<td>STAT_PROFIT_TRADES</td>
<td>勝ちトレード数</td>
</tr>
<tr>
<td>STAT_LOSS_TRADES</td>
<td>負けトレード数</td>
</tr>
<tr>
<td>STAT_EXPECTED_PAYOFF</td>
<td>期待利得（1トレードあたりの平均損益）</td>
</tr>
<tr>
<td>STAT_EQUITY_DD</td>
<td>最大ドローダウン（金額）</td>
</tr>
<tr>
<td>STAT_EQUITY_DDREL_PERCENT</td>
<td>最大ドローダウン（%）</td>
</tr>
<tr>
<td>STAT_SHARPE_RATIO</td>
<td>シャープレシオ</td>
</tr>
<tr>
<td>STAT_PROFIT_FACTOR</td>
<td>プロフィットファクター</td>
</tr>
<tr>
<td>STAT_RECOVERY_FACTOR</td>
<td>リカバリーファクター</td>
</tr>
</tbody>
</table>
<h2><span id="toc12">最適化での使い方</span></h2>
<p>OnTester()の戻り値をカスタム最適化基準として使用するには、ストラテジーテスターの最適化設定で以下の手順を行います。</p>
<ol>
<li>ストラテジーテスターを開き、EAを選択する</li>
<li>「最適化」にチェックを入れる</li>
<li>最適化基準のドロップダウンから<strong>「Custom max」</strong>を選択する</li>
<li>最適化するパラメータの範囲を設定する</li>
<li>「スタート」を押して最適化を実行する</li>
</ol>
<p>「Custom max」を選択すると、OnTester()の戻り値が<strong>最大化</strong>される方向でパラメータの最適な組み合わせが探索されます。逆に最小化したい場合は、戻り値の符号を反転させる（負の値にする）ことで対応できます。</p>
<h2><span id="toc13">注意点・よくある間違い</span></h2>
<ul>
<li><strong>ライブ取引では呼ばれない</strong>：OnTester()はストラテジーテスター内でのみ動作します。ライブ取引や通常のチャートにEAをアタッチした場合は呼ばれません。</li>
<li><strong>戻り値の型はdouble</strong>：int型ではなくdouble型を返す必要があります。整数値を返しても問題はありませんが、関数の宣言はdoubleにしてください。</li>
<li><strong>TesterStatistics()はOnTester()内でのみ有効</strong>：この関数はバックテスト終了後にしか正確な値を返しません。OnTick()内で呼んでも意味のある値は取得できません。</li>
<li><strong>最適化時のパフォーマンス</strong>：OnTester()内で重い計算を行うと、最適化全体の速度に影響します。特に数千パスの最適化を行う場合は、計算量に注意してください。</li>
<li><strong>取引回数のフィルターは必須</strong>：取引回数が極端に少ない結果は統計的に信頼できません。必ず最低取引回数のチェックを入れてから評価値を計算しましょう。</li>
</ul>
<p>投稿 <a href="https://mql-programing.com/archives/3050/ontester/">【MQL4関数】OnTester関数とは？バックテスト終了時に実行されるイベント関数</a> は <a href="https://mql-programing.com">自動売買を作ろう！</a> に最初に表示されました。</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
