- 1 1. MQL5のOnInit・OnDeinitとは何か
- 2 2. OnInitの使い方と基本構文
- 3 3. OnDeinitの使い方と終了処理の実践
- 4 4. OnInitとOnDeinitを組み合わせた実践設計
- 5 5. 初心者がつまずくポイントと対処法
- 6 6. 実務で使える完成テンプレートと設計まとめ
- 7 7. FAQ
1. MQL5のOnInit・OnDeinitとは何か
MQL5における OnInit と OnDeinit は、EA(自動売買)やインジケーターの「ライフサイクル(起動〜終了)」を制御する最も基本的な関数です。
結論から言うと、役割は次の通りです。
- OnInit:起動時に1回だけ実行される「初期化処理」
- OnDeinit:停止時に実行される「終了処理」
この2つを正しく理解していないと、以下のような問題が発生します。
- インジケーターが正しく動かない
- メモリやハンドルの解放漏れ
- VPS再起動時に不具合が発生
- EAが意図せず停止する
つまり、EAの安定性・再現性に直結する最重要ポイントです。
1.1 OnInitの役割(初期化処理)
OnInitは、EAやインジケーターがチャートにセットされた瞬間に「1回だけ」実行される関数です。
主に以下の処理を担当します。
- インジケーターハンドルの作成(例:iMA)
- 配列や変数の初期化
- 入力パラメータのチェック
- 初期状態の設定
基本構造(最小例)
int OnInit()
{
Print("初期化処理開始");
return(INIT_SUCCEEDED);
}
重要ポイント(初心者が見落とす点)
- 必ず int型で戻り値を返す
- 正常時:
INIT_SUCCEEDED - 異常時:
INIT_FAILED
よくある失敗
- 戻り値を書かない → コンパイルエラーまたは不安定動作
- エラー時でも
INIT_SUCCEEDEDを返してしまう - 初期化に失敗しているのにそのままOnTickが動く
👉 実務では「初期化に失敗したら即停止」が基本です
1.2 OnDeinitの役割(終了処理)
OnDeinitは、EAやインジケーターが停止されるときに実行される関数です。
主に以下のタイミングで呼ばれます。
- EAをチャートから削除したとき
- MT5を終了したとき
- 再コンパイルしたとき
- パラメータ変更時
- チャート変更時(通貨ペア・時間足)
基本構造
void OnDeinit(const int reason)
{
Print("終了処理: reason=", reason);
}
役割
- インジケーターハンドルの解放
- オブジェクト削除(ライン・テキストなど)
- ログ出力
よくある失敗
- 何も書かず放置 → リソースリーク(メモリ解放漏れ)
- ハンドルを解放しない → 長期稼働で不具合
- reasonを無視 → デバッグ困難
👉 特にVPS運用では、終了処理の品質が安定性を左右します
1.3 OnTickとの違い(初心者が混乱するポイント)
初心者が最も混乱するのが OnTickとの違い です。
| 関数 | 実行タイミング | 用途 |
|---|---|---|
| OnInit | 起動時に1回 | 初期化 |
| OnTick | 毎Tick | 売買ロジック |
| OnDeinit | 終了時 | 後処理 |
イメージ(重要)
起動 → OnInit(1回)
↓
相場動作中 → OnTick(何千回も)
↓
停止 → OnDeinit(1回)
よくある誤解
- OnInitでトレード処理を書く → NG
- OnTickで毎回インジケーター生成 → 非効率・バグの原因
- OnDeinitを使わない → 長期運用で崩壊
👉 正しい設計は
「OnInitで準備 → OnTickで処理 → OnDeinitで後片付け」
このセクションの重要まとめ
- OnInit:初期化(1回)
- OnDeinit:終了処理(1回)
- OnTick:メイン処理(毎Tick)
- 3つの役割を分離することで、安定したEAになる
2. OnInitの使い方と基本構文
OnInitは「EAの起動時に1回だけ実行される初期化関数」です。
ここでの設計ミスは、そのまま EAの不具合・誤作動・停止原因 に直結します。
このセクションでは、実務レベルで使える形まで具体化します。
2.1 基本構文(サンプルコード)
OnInitの基本構文は以下です。
int OnInit()
{
// 初期化処理
return(INIT_SUCCEEDED);
}
戻り値の意味(重要)
| 戻り値 | 意味 |
|---|---|
| INIT_SUCCEEDED | 正常に初期化完了 → EAが動作する |
| INIT_FAILED | 初期化失敗 → EAは停止する |
実務上の重要ポイント
- 戻り値でEAの起動可否を制御できる
- エラー時は必ず
INIT_FAILEDを返す - これにより「壊れた状態で動くEA」を防ぐ
NG例(よくある失敗)
int OnInit()
{
// 失敗しているのに…
return(INIT_SUCCEEDED);
}
👉 これは最も危険なバグの一つ
(原因不明の挙動を引き起こす)
2.2 初期化で行う処理の具体例
OnInitで行う処理は「準備」です。
主に以下の3つに分類できます。
① インジケーターハンドルの生成
int ma_handle;
int OnInit()
{
ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("iMAハンドル取得失敗");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
ポイント
- iMAなどは「ハンドル(識別子)」を返す
- 失敗時は
INVALID_HANDLE - 必ずチェックする
② 配列・変数の初期化
double buffer[];
int OnInit()
{
ArraySetAsSeries(buffer, true); // 時系列配列にする
ArrayResize(buffer, 100); // サイズ確保
return(INIT_SUCCEEDED);
}
ポイント
- 配列は初期化しないとバグの原因になる
- 時系列配列(最新がindex=0)を意識
③ 入力パラメータの検証
input int period = 20;
int OnInit()
{
if(period <= 0)
{
Print("periodは1以上である必要があります");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
ポイント
- 不正パラメータは即停止
- 「異常値で動くEA」を防ぐ
2.3 よくある失敗(重要)
OnInitは「バグの温床」になりやすいです。
特に以下は頻出です。
① ハンドル取得失敗を無視
ma_handle = iMA(...);
// チェックしない → 後でクラッシュ
👉 結果:CopyBufferでエラー発生
② Printログを出さない
// エラー原因がわからない
👉 最低限これを入れる
Print("OnInit開始");
③ 初期化が重すぎる
- 大量ループ
- 重い計算
- 外部処理
👉 結果
- 起動遅延
- VPS環境で不安定
👉 対策
- 重い処理はOnTickへ分散
④ INIT_FAILEDを使わない
👉 これが最も多い
// 失敗しても動かしてしまう
👉 正解
if(error)
{
return(INIT_FAILED);
}
2.4 実務テンプレート(そのまま使える)
int ma_handle;
int OnInit()
{
Print("OnInit開始");
// ハンドル生成
ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("エラー:iMAハンドル取得失敗");
return(INIT_FAILED);
}
Print("OnInit成功");
return(INIT_SUCCEEDED);
}
👉 この形が「最低限の安全設計」です
このセクションの重要まとめ
- OnInitは「起動時の1回だけの準備処理」
- 戻り値でEAの生死が決まる
- ハンドル取得・パラメータチェックは必須
- エラー時は必ず
INIT_FAILED
3. OnDeinitの使い方と終了処理の実践
OnDeinitは「EAやインジケーターが停止する瞬間に実行される関数」です。
軽視されがちですが、実務では 安定稼働・再起動耐性・バグ回避 に直結します。
結論として、OnDeinitの役割は次の3つです。
- 使用したリソースの解放(メモリ・ハンドル)
- チャートオブジェクトの削除
- 停止理由のログ取得(デバッグ)
3.1 基本構文(reasonの意味を理解する)
void OnDeinit(const int reason)
{
Print("OnDeinit実行: reason=", reason);
}
reasonとは何か
停止理由を表すコードです。
| 定数 | 内容 |
|---|---|
| REASON_REMOVE | チャートから削除 |
| REASON_RECOMPILE | 再コンパイル |
| REASON_CHARTCHANGE | 通貨ペア・時間足変更 |
| REASON_PARAMETERS | パラメータ変更 |
| REASON_ACCOUNT | 口座変更 |
| REASON_CLOSE | MT5終了 |
実務上のポイント
- reasonをログに残すことで「異常停止の原因特定」が可能
- VPS運用では特に重要(再起動・切断の検知)
3.2 ハンドル解放(最重要)
OnInitで生成したハンドルは、必ずOnDeinitで解放します。
例:インジケーターハンドル
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
Print("iMAハンドル解放完了");
}
}
なぜ必要か
- 解放しないとメモリに残る
- 長期稼働で不具合・クラッシュの原因
- VPS環境で顕著に影響
👉 特に「EA再起動を繰り返す環境」で重要
3.3 チャートオブジェクトの削除
EAでラインやテキストを表示した場合、削除しないと残ります。
例:オブジェクト削除
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, 0, -1);
Print("オブジェクト削除完了");
}
よくある問題
- チャートにゴミが残る
- 再起動時に表示が重複
- 視覚的バグ
👉 「表示系は必ず掃除」が原則
3.4 よくある失敗(実務で多発)
① OnDeinitを空にする
void OnDeinit(const int reason)
{
// 何もしない
}
👉 問題
- メモリリーク
- オブジェクト残留
- デバッグ不能
② ハンドルを解放しない
👉 結果
- CopyBufferエラー増加
- 長期運用で不安定化
③ reasonを無視する
👉 問題
- 「なぜ停止したか」がわからない
- VPSトラブルの原因追跡不能
④ 毎回全部削除してしまう
ObjectsDeleteAll(0, 0, -1);
👉 注意点
- 他のインジケーターのオブジェクトも消す可能性あり
👉 対策
- 名前プレフィックスで管理
- 自分が作ったものだけ削除
3.5 実務テンプレート(安全設計)
void OnDeinit(const int reason)
{
Print("OnDeinit開始 reason=", reason);
// ハンドル解放
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
Print("ハンドル解放完了");
}
// オブジェクト削除(必要に応じて)
// ObjectsDeleteAll(0, 0, -1);
Print("OnDeinit終了");
}
👉 この構造で「最低限の安定性」は確保できます
3.6 VPS運用での重要性(実務視点)
OnDeinitは特に以下で重要です。
ケース
- VPS再起動
- MT5クラッシュ
- 回線切断 → 再接続
起きる問題
- リソース解放されない
- 状態不整合
- EA再起動時の異常
👉 対策
- OnInit / OnDeinitをセットで設計
- 状態リセットを前提にする
このセクションの重要まとめ
- OnDeinitは「終了時の後処理」
- ハンドル解放は必須(最重要)
- reasonで停止原因を把握
- 放置すると長期運用で破綻する
4. OnInitとOnDeinitを組み合わせた実践設計
OnInitとOnDeinitは単体で理解するだけでは不十分です。
実務では「セットで設計する」ことで、初めて安定したEAになります。
結論として、基本設計は以下の通りです。
- OnInit:リソース確保・初期状態構築
- OnTick:ロジック実行
- OnDeinit:リソース解放・状態リセット
この3点を分離することで、再現性・安定性・保守性が大きく向上します。
4.1 基本設計パターン(テンプレート)
まずは、実務でそのまま使える構成を提示します。
int ma_handle;
// 初期化
int OnInit()
{
Print("OnInit開始");
ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("エラー:ハンドル取得失敗");
return(INIT_FAILED);
}
Print("OnInit成功");
return(INIT_SUCCEEDED);
}
// メイン処理
void OnTick()
{
double buffer[];
if(CopyBuffer(ma_handle, 0, 0, 1, buffer) <= 0)
{
Print("CopyBuffer失敗");
return;
}
double ma = buffer[0];
// 売買ロジック(例)
if(ma > Close[0])
{
// 売り条件など
}
}
// 終了処理
void OnDeinit(const int reason)
{
Print("OnDeinit開始 reason=", reason);
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
}
Print("OnDeinit終了");
}
4.2 なぜこの設計が重要か(構造理解)
この設計は単なる形式ではなく、リスク管理そのものです。
悪い設計(よくある)
void OnTick()
{
int handle = iMA(...); // 毎回生成
}
👉 問題
- 無限にハンドル生成
- メモリ圧迫
- 不安定化
良い設計
- OnInitで1回だけ生成
- OnTickでは使うだけ
- OnDeinitで解放
👉 結果
- 負荷低減
- 再現性向上
- バグ減少
4.3 状態管理の考え方(重要)
EA設計では「状態(state)」を意識します。
状態とは
- ハンドル
- 配列
- ポジション情報
- フラグ変数
原則
- OnInit → 状態を作る
- OnTick → 状態を使う
- OnDeinit → 状態を破棄する
例:フラグ管理
bool is_initialized = false;
int OnInit()
{
is_initialized = true;
return(INIT_SUCCEEDED);
}
void OnTick()
{
if(!is_initialized) return;
}
👉 初期化未完了状態での誤動作を防ぐ
4.4 よくある設計ミス(実務頻出)
① OnTickに初期化を書く
👉 問題
- 毎Tick処理 → 無駄・重い・バグ
② OnDeinitを考慮しない
👉 問題
- 再起動で状態が壊れる
- VPSで異常動作
③ グローバル変数を乱用
👉 問題
- 状態が不明確
- バグ追跡困難
④ 再初期化ケースを考慮しない
以下のケースでOnInitは再実行されます。
- パラメータ変更
- 時間足変更
- 再コンパイル
👉 つまり
- 「何度でも初期化される前提」で設計する
4.5 実務で重要な設計指針(再現性重視)
① 初期化失敗=即停止
👉 不正状態で動かさない
② リソースは必ず対で管理
- 生成 → 解放
- allocate → release
③ 副作用を減らす
- OnTickは「純粋な処理」に近づける
④ VPS前提で設計
- 再起動
- 切断
- 再接続
👉 全て耐える構造にする
4.6 設計の本質(上級視点)
OnInit / OnDeinitは単なる関数ではなく、
👉 「EAのライフサイクル管理」そのもの
です。
この理解があるかどうかで
- バグ率
- 再現性
- 運用安定性
が大きく変わります。
このセクションの重要まとめ
- OnInit / OnTick / OnDeinitは分離設計が必須
- リソースは「生成と解放」で管理
- 状態管理を意識する
- 再初期化前提で設計する
5. 初心者がつまずくポイントと対処法
OnInit・OnDeinitは構文自体はシンプルですが、実務では「動かない」「原因不明のバグ」といった問題が頻発します。
ここでは、初心者〜中級者が実際にハマるポイントを、原因・影響・対処のセットで整理します。
5.1 OnInitが失敗しているのに気づかない
症状
- EAが動かない(OnTickが実行されない)
- 何もエラーが出ていないように見える
原因
- OnInitが
INIT_FAILEDを返している - または内部でエラーが発生している
典型例
int OnInit()
{
int handle = iMA(...);
if(handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
👉 ハンドル取得失敗 → EAは起動しない
対処
Print("OnInit開始");
Print("handle=", handle);
👉 必ずログを出す
実務ポイント
- 「動かない=OnInit失敗」をまず疑う
- Expertsログを必ず確認
5.2 OnDeinitが呼ばれていることを知らない
症状
- EAが勝手にリセットされる
- 状態が消える
- 設定が反映されない
原因
以下の操作でOnDeinitが発生します。
- 時間足変更
- 通貨ペア変更
- パラメータ変更
- 再コンパイル
対処
void OnDeinit(const int reason)
{
Print("OnDeinit reason=", reason);
}
👉 「いつ・なぜ停止したか」を可視化
実務ポイント
👉 EAは「何度でも再起動される前提」で設計する
5.3 CopyBufferエラーの原因がわからない
症状
- CopyBufferが失敗する
- 値が取得できない
原因(9割これ)
- OnInitでハンドル取得に失敗している
- ハンドルが無効(INVALID_HANDLE)
NG例
CopyBuffer(ma_handle, 0, 0, 1, buffer);
👉 ma_handleが無効でも実行している
対処
if(ma_handle == INVALID_HANDLE)
{
Print("無効なハンドル");
return;
}
実務ポイント
👉 CopyBufferエラーは「OnInitを疑う」
5.4 リソース解放忘れによる不具合
症状
- 長時間運用で不安定
- EAが重くなる
- MT5がクラッシュ
原因
- IndicatorReleaseしていない
- ハンドルが増え続ける
対処
void OnDeinit(const int reason)
{
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
}
}
実務ポイント
👉 VPS運用では「ほぼ必須対応」
5.5 OnTickに初期化処理を書いてしまう
症状
- 動作が重い
- 不安定
- 意味不明なバグ
NG例
void OnTick()
{
int handle = iMA(...);
}
問題
- 毎Tickでハンドル生成
- メモリ圧迫
- 非効率
対処
👉 OnInitに移動する
実務ポイント
👉 「初期化はOnInit、処理はOnTick」で分離
5.6 reasonを無視してデバッグ不能
症状
- EAが止まる理由が不明
- VPSでの問題が追えない
対処
Print("終了理由=", reason);
実務ポイント
- REASON_RECOMPILE → 再コンパイル
- REASON_CHARTCHANGE → チャート変更
👉 ログを見れば原因が特定できる
5.7 再初期化を考慮していない設計
症状
- パラメータ変更で壊れる
- EA再起動で動かない
原因
- 初期化前提が崩れている
- 状態が残っている
対処
- OnInitで必ず初期状態に戻す
- OnDeinitで完全にリセット
実務ポイント
👉 「何度でも起動・停止される」前提で設計
このセクションの重要まとめ
- 「動かない=OnInitを疑う」
- CopyBufferエラーはハンドル問題が多い
- OnDeinitは頻繁に呼ばれる
- 初期化と処理を分離しないと破綻する
- VPS前提で設計する
6. 実務で使える完成テンプレートと設計まとめ
ここまでの内容を踏まえ、実務でそのまま使える「安全設計テンプレート」を提示します。
目的は以下です。
- 初期化ミスを防ぐ
- 長期運用(VPS)でも安定させる
- デバッグ可能な構造にする
6.1 完成テンプレート(そのまま使用可能)
int ma_handle = INVALID_HANDLE;
// 初期化
int OnInit()
{
Print("OnInit開始");
// インジケーターハンドル生成
ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
if(ma_handle == INVALID_HANDLE)
{
Print("エラー:iMAハンドル取得失敗");
return(INIT_FAILED);
}
Print("OnInit成功");
return(INIT_SUCCEEDED);
}
// メイン処理
void OnTick()
{
// ハンドルチェック
if(ma_handle == INVALID_HANDLE)
{
Print("無効なハンドル");
return;
}
double buffer[];
// データ取得
if(CopyBuffer(ma_handle, 0, 0, 1, buffer) <= 0)
{
Print("CopyBuffer失敗");
return;
}
double ma = buffer[0];
// サンプルロジック
if(ma > Close[0])
{
// 売り条件(例)
}
}
// 終了処理
void OnDeinit(const int reason)
{
Print("OnDeinit開始 reason=", reason);
// ハンドル解放
if(ma_handle != INVALID_HANDLE)
{
IndicatorRelease(ma_handle);
ma_handle = INVALID_HANDLE;
}
Print("OnDeinit終了");
}
6.2 このテンプレートの設計意図(重要)
① 状態の一元管理
int ma_handle = INVALID_HANDLE;
👉 状態をグローバルで管理
👉 無効状態を明示(INVALID_HANDLE)
② 初期化失敗で停止
return(INIT_FAILED);
👉 不正状態での稼働を防ぐ
👉 再現性を担保
③ OnTickでの防御コード
if(ma_handle == INVALID_HANDLE)
{
return;
}
👉 想定外の状態でも安全に停止
④ 解放とリセット
IndicatorRelease(ma_handle);
ma_handle = INVALID_HANDLE;
👉 ダブル解放防止
👉 再初期化対応
6.3 設計チェックリスト(実務用)
EAを作る際は、以下を満たしているか確認します。
初期化(OnInit)
- ハンドル取得チェックをしている
- パラメータ検証をしている
- エラー時にINIT_FAILEDを返している
実行(OnTick)
- 初期化済みかチェックしている
- CopyBufferの戻り値を確認している
- 無駄な初期化処理をしていない
終了(OnDeinit)
- ハンドルを解放している
- reasonをログ出力している
- 状態をリセットしている
6.4 よくある設計崩壊パターン
パターン①:動くけど不安定
👉 原因
- エラーチェック不足
- ハンドル未解放
パターン②:再起動で壊れる
👉 原因
- 状態が残る設計
- OnDeinit未対応
パターン③:原因不明の停止
👉 原因
- reason未ログ
- OnInit失敗を見逃し
6.5 長期運用(VPS)視点での最適設計
必須要件
- 再起動耐性(OnInit再実行前提)
- メモリ管理(解放必須)
- ログ出力(原因追跡可能)
実務での判断基準
- 3日以上連続稼働で安定するか
- 再起動後に同じ挙動になるか(再現性)
- エラー時に原因が特定できるか
6.6 本質まとめ(上級視点)
OnInit / OnDeinitは単なる関数ではなく、
👉 「EAのライフサイクル制御」
です。
この設計を誤ると
- バックテストは動くが実運用で崩壊
- VPSで不安定
- 原因不明の不具合
になります。
逆にここを正しく設計すると
- 安定性が一段上がる
- デバッグが容易になる
- 実運用に耐えるEAになる
このセクションの重要まとめ
- テンプレートをベースに設計する
- 初期化・実行・終了を分離する
- 状態管理とリソース管理を徹底する
- VPS前提で設計する
7. FAQ
Q1. OnInitが実行されないことはありますか?
A. 基本的には必ず実行されます。
ただし、OnInit内で INIT_FAILED を返した場合、その後のOnTickは実行されません。
👉 「動かない=OnInit失敗」をまず疑うのが実務判断です。
Q2. OnDeinitはどのタイミングで呼ばれますか?
A. 停止・変更時に必ず呼ばれます。
主なケースは以下です。
- EA削除
- MT5終了
- 再コンパイル
- パラメータ変更
- チャート変更
👉 想像以上に頻繁に呼ばれるため、軽視は危険です。
Q3. OnInitで重い処理をしても問題ありませんか?
A. 基本的には非推奨です。
理由:
- 起動が遅くなる
- VPS環境で不安定になる可能性
👉 重い処理はOnTickなどに分散するのが安全です。
Q4. OnDeinitで何も処理しなくても動きますか?
A. 短期的には動きますが、長期的には問題になります。
- メモリリーク
- ハンドル増加
- 不安定化
👉 特にVPS運用では必ず実装すべきです。
Q5. OnInitとOnTickの違いは何ですか?
A. 実行回数と役割が完全に異なります。
- OnInit:1回(初期化)
- OnTick:毎Tick(ロジック処理)
👉 OnInitに処理を書きすぎたり、OnTickで初期化するのは設計ミスです。
Q6. CopyBufferが失敗する原因は何ですか?
A. 多くの場合、OnInitの失敗が原因です。
- ハンドルがINVALID_HANDLE
- 初期化不備
👉 CopyBufferエラーは「OnInitから見直す」のが最短ルートです。
Q7. OnDeinitのreasonは必ず使うべきですか?
A. 実務ではほぼ必須です。
理由:
- 停止原因の特定ができる
- VPSトラブル対応に有効
👉 ログに出すだけでも価値があります。
Q8. OnInitは何度も実行されることがありますか?
A. はい、あります。
以下のケースで再実行されます。
- パラメータ変更
- 時間足変更
- 再コンパイル
👉 「1回だけ」と思い込むのは危険
👉 再初期化前提で設計する必要があります
FAQまとめ(実務判断)
- 動かない → OnInitを疑う
- 不安定 → OnDeinitを疑う
- CopyBufferエラー → ハンドル確認
- VPS運用 → ライフサイクル設計が必須