GitHub で見る
Expert Ichimoku ストラテジー
概要
Expert Ichimoku ストラテジーは、StockSharp の高レベル API を使用して元の MQL5 エキスパートアドバイザー "Expert Ichimoku" のロジックを再現します。このシステムは、Ichimoku Kinko Hyo インジケーターの複数のコンポーネントを価格行動フィルターおよびオプションのマーティンゲール式ポジションサイジングモジュールと組み合わせた方向性トレンドフォローモデルです。
ストラテジーは設定可能な時間軸の完成したローソク足でシグナルを評価します。ロングとショートの取引は相互に排他的です — ストラテジーは単一のネットポジションを維持し、既存のエクスポージャーをクローズした後にのみ方向を変更します。すべてのインジケーター値はサブスクライブされたローソク足シリーズで計算されます。外部データは必要ありません。
コアロジック
インジケーター設定
- Tenkan-sen(転換線): クロス検出に使用する高速移動平均。
- Kijun-sen(基準線): クロスパートナーを形成する低速移動平均。
- Senkou Span A / Senkou Span B: 強気または弱気の市場構造を確認するために前のバーで評価される雲の境界線。
- Chikou Span(遅行スパン): 遅行価格ブレイクアウト条件によるモメンタム確認。
インジケーターの長さはユーザーが設定可能で、MQL5 エキスパートのデフォルト値(9 / 26 / 52)に一致します。
エントリールール
以下のすべての条件が満たされたときにロングポジションが開かれます:
- モメンタムトリガー: いずれか
- 最近閉じたバーで Tenkan-sen が Kijun-sen を上抜けした(Tenkant-1 ≤ Kijunt-1 かつ Tenkant > Kijunt)、または
- Chikou Span が過去の価格を上抜けした(Chikout-1 ≤ Closet-11 かつ Chikout > Closet-10)、
- 雲フィルター: 現在の終値が前のバーの両方の Senkou スパンを上回っている(価格が雲の完全に上)、
- 価格行動フィルター: 前のローソク足が強気で閉じた(Closet-1 > Opent-1)、
- ポジションフィルター: 現在アクティブなロングエクスポージャーがない。ショートポジションが存在する場合、まず閉じられます。新しいロングはショートがフラットになった後にのみ送信されます。
ショートポジションは対称的な条件で開かれます:
- モメンタムトリガー: いずれか
- Tenkan-sen が Kijun-sen を下抜けした(Tenkant-1 ≥ Kijunt-1 かつ Tenkant < Kijunt)、または
- Chikou Span が過去の価格を下抜けした(Chikout-1 ≥ Opent-11 かつ Chikout < Opent-10)、
- 雲フィルター: 現在の終値が前のバーの両方の Senkou スパンを下回っている、
- 価格行動フィルター: 前のローソク足が弱気で閉じた(Closet-1 < Opent-1)、
- ポジションフィルター: ショートを開く前に既存のロングエクスポージャーが閉じられます。
ポジションサイジングとマーティンゲールオプション
- ベース注文サイズはストラテジーの
Volume プロパティと等しい。
- Use Martingale が有効な場合、前の完了した取引が損失でクローズした場合、次のエントリーサイズが2倍になります。利益またはブレークイーブンの取引は乗数をリセットします。
- 結果の注文サイズは
Volume × Max Position Multiplier によって制限され、元の EA の最大ポジション数の保護を反映しています。
リスク管理
- 静的ストップロス / テイクプロフィット: オプションの絶対価格オフセットが各新規ポジションに適用されます。終値がストップまたはターゲットに達した場合、ポジションは市場でクローズされます。
- トレーリングストップ:
Trailing Stop Offset と Trailing Step の両方が正の場合、ストップレベルは価格がエントリーから offset + step を超えて進んだ後にのみ引き締められ、MQL5 バージョンの増分トレーリングロジックをエミュレートします。
- ストラテジーは1つのネットポジションを取引します。エグジット(ストップ、ターゲット、トレーリング、または反転による)時、実現 PnL が評価されて次のシグナルのマーティンゲールフラグを更新します。
パラメーター
| 名前 |
説明 |
デフォルト |
| Tenkan Period |
Tenkan-sen ラインの長さ。 |
9 |
| Kijun Period |
Kijun-sen ラインの長さ。 |
26 |
| Senkou Span B Period |
Senkou Span B ラインの長さ。 |
52 |
| Stop Loss Offset |
エントリー価格と保護ストップの間の絶対距離。無効化するには 0 に設定。 |
0 |
| Take Profit Offset |
エントリー価格と利益目標の間の絶対距離。無効化するには 0 に設定。 |
0 |
| Trailing Stop Offset |
起動後に適用されるベーストレーリング距離。 |
0 |
| Trailing Step |
トレーリングストップを引き締める前に必要な追加移動。 |
0 |
| Max Position Multiplier |
有効な注文サイズの上限(Volume に対する相対値)。 |
5 |
| Use Martingale |
負け取引の後に次の取引サイズを2倍にするかどうか。 |
true |
| Candle Type |
計算に使用するローソク足シリーズ。 |
1時間足の時間軸 |
実践的な注意事項
- ストラテジーはすべての条件が評価できるようになるまで、少なくとも12本の完成したローソク足を必要とします(Chikou の比較は最大11バー前の価格を参照します)。
- StockSharp ストラテジーはネットポジションで動作するため、
Max Position Multiplier パラメーターは複数の独立したチケットを管理する代わりに最大契約サイズを制限します。これによりMQL5実装のエクスポージャー制限に合わせた動作が維持されます。
- トレーリングストップロジックは EA を反映します:ストップは価格が
Trailing Stop Offset + Trailing Step だけ進んだ場合にのみ移動されます。いずれかのパラメーターをゼロに設定するとトレーリング調整が無効になります。
- ログ記録はすべてのエントリーとエグジットを報告し、市場データを再生するときの意思決定ポイントの監査を容易にします。
使用ワークフロー
StrategyContainer またはデザイナーテンプレートで希望するローソク足タイプとインストゥルメントを設定します。
- ベース
Volume を設定し、シンボルのボラティリティに応じてリスクパラメーターを調整します(例:元の EA のpipベースの距離を選択した市場の価格単位に変換する)。
- ストラテジーを開始します。インジケーターに十分な履歴が蓄積されると、完成した各バーでクロスオーバーと遅行線の確認を評価し、エグジットとマーティンゲールサイジングを自動的に管理します。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ichimoku strategy using Tenkan/Kijun crossover (midline of short/long channels).
/// </summary>
public class ExpertIchimokuStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private decimal? _prevTenkan;
private decimal? _prevKijun;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
public ExpertIchimokuStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Short channel period", "Indicators");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Long channel period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevTenkan = null;
_prevKijun = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevTenkan = null;
_prevKijun = null;
// Tenkan: midline of short highest/lowest
var tenkanHigh = new Highest { Length = TenkanPeriod };
var tenkanLow = new Lowest { Length = TenkanPeriod };
// Kijun: midline of long highest/lowest
var kijunHigh = new Highest { Length = KijunPeriod };
var kijunLow = new Lowest { Length = KijunPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(tenkanHigh, tenkanLow, kijunHigh, kijunLow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal tHigh, decimal tLow, decimal kHigh, decimal kLow)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var tenkan = (tHigh + tLow) / 2;
var kijun = (kHigh + kLow) / 2;
if (_prevTenkan == null || _prevKijun == null)
{
_prevTenkan = tenkan;
_prevKijun = kijun;
return;
}
// Tenkan crosses above Kijun → buy
if (_prevTenkan.Value <= _prevKijun.Value && tenkan > kijun)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// Tenkan crosses below Kijun → sell
else if (_prevTenkan.Value >= _prevKijun.Value && tenkan < kijun)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevTenkan = tenkan;
_prevKijun = kijun;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class expert_ichimoku_strategy(Strategy):
def __init__(self):
super(expert_ichimoku_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._tenkan_period = self.Param("TenkanPeriod", 9) \
.SetDisplay("Tenkan Period", "Short channel period", "Indicators")
self._kijun_period = self.Param("KijunPeriod", 26) \
.SetDisplay("Kijun Period", "Long channel period", "Indicators")
self._prev_tenkan = None
self._prev_kijun = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def TenkanPeriod(self):
return self._tenkan_period.Value
@property
def KijunPeriod(self):
return self._kijun_period.Value
def OnReseted(self):
super(expert_ichimoku_strategy, self).OnReseted()
self._prev_tenkan = None
self._prev_kijun = None
def OnStarted2(self, time):
super(expert_ichimoku_strategy, self).OnStarted2(time)
self._prev_tenkan = None
self._prev_kijun = None
tenkan_high = Highest()
tenkan_high.Length = self.TenkanPeriod
tenkan_low = Lowest()
tenkan_low.Length = self.TenkanPeriod
kijun_high = Highest()
kijun_high.Length = self.KijunPeriod
kijun_low = Lowest()
kijun_low.Length = self.KijunPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(tenkan_high, tenkan_low, kijun_high, kijun_low, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, t_high_value, t_low_value, k_high_value, k_low_value):
if candle.State != CandleStates.Finished:
return
th = float(t_high_value)
tl = float(t_low_value)
kh = float(k_high_value)
kl = float(k_low_value)
tenkan = (th + tl) / 2.0
kijun = (kh + kl) / 2.0
if self._prev_tenkan is None or self._prev_kijun is None:
self._prev_tenkan = tenkan
self._prev_kijun = kijun
return
# Tenkan crosses above Kijun
if self._prev_tenkan <= self._prev_kijun and tenkan > kijun:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# Tenkan crosses below Kijun
elif self._prev_tenkan >= self._prev_kijun and tenkan < kijun:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_tenkan = tenkan
self._prev_kijun = kijun
def CreateClone(self):
return expert_ichimoku_strategy()