GitHub で見る
TenKijun クロス アラート戦略 (ID 3562)
概要
この戦略は、MetaTrader エキスパート アドバイザー TenKijun.mq4 の StockSharp の高レベル API 移植です。オリジナルの EA は、Ichimoku インジケーターのみを監視し、転換線 (変換ライン) が基準線 (基準線) を横切ったときにプッシュ通知を送信します。 C# バージョンはアラートのみの性質を維持していますが、StockSharp インフラストラクチャ、チャート バインディング、パラメータ化、安全なセッション処理を備えた実装をアップグレードしています。
このロジックは、設定可能な時間枠の完了したローソク足に対して機能します。アクティブな取引時間内に新しいローソク足が閉じると、この戦略は古典的な 9/26/52 期間で計算された Ichimoku インジケーターを評価し、最新の Tenkan/Kijun 値を記録します。 Tenkan が Kijun を上回った場合、強気のクロスを示す情報メッセージが記録されます。テンカンがキジュンを下回った場合、弱気アラートが記録されます。取引は実行されません。この戦略はシグナル生成、または外部自動化との組み合わせを目的としています。
インジケーターとデータフロー
- インジケーター – StockSharp
Ichimoku インジケーター。個別にパラメーター化されたテンカン、キジュン、センコウのスパン B の長さ。 Tenkan 行と Kijun 行のみが意思決定に使用され、オリジナルの EA を反映しています。
- データ サブスクリプション –
SubscribeCandles と構成可能な CandleType を使用します。デフォルトでは、30 分のタイムフレームのローソク足が要求されます。
- バインディング –
BindEx が採用されているため、型指定された IchimokuValue は、GetValue を手動で呼び出すことなくハンドラーに配信されます。
- チャート – アラートを素早く視覚的に検証できるように、ローソク足と Ichimoku インジケーターが戦略チャートに自動的に追加されます。
取引セッションフィルター
MetaTrader スクリプトは、アラートをユーザー定義のセッション ウィンドウに制限しました。このポートは、次の 2 つのパラメータを介して同じ機能を公開します。
StartHour – アクティブなウィンドウの開始点を含みます (デフォルトは 0)。 0 ~ 23 を受け入れます。
LastHour – アクティブなウィンドウの終わりを含みます (デフォルトは 20)。 0 ~ 23 を受け入れます。
StartHour が LastHour 以下の場合、アラートは 1 日のその 2 時間の間に生成されます。開始時間が終了時間より大きい場合、ウィンドウは夜間として扱われます (たとえば、20 → 6 は深夜から早朝のセッションをカバーします)。
パラメーター
| パラメータ |
説明 |
デフォルト |
注意事項 |
StartHour |
アラートを開始できる時間。 |
0 |
0 ~ 23 の範囲。 |
LastHour |
アラートが停止する時間。 |
20 |
0 ~ 23 の範囲。 |
TenkanPeriod |
コンバージョンラインの振り返り。 |
9 |
最適化可能。 |
KijunPeriod |
ベースラインの振り返り。 |
26 |
最適化可能。 |
SenkouSpanBPeriod |
主要なスパン B の振り返り。 |
52 |
アラートはクラウドに依存していませんが、完全性を保つために提供されています。 |
CandleType |
インジケーターに使用されているキャンドルシリーズ。 |
30分の時間枠 |
TimeSpan ベースの時間枠を選択します。 |
アラートロジック
- 最初に完成したキャンドルがテンカンとキジュンの履歴を初期化するまで待ちます。
- 取引ウィンドウ内で後続の終了したローソク足ごとに次のようになります。
- Ichimoku インジケーターから Tenkan と Kijun の値を抽出します。
- 前回のテンカンが前回の基準以下で、現在のテンカンが現在の基準より大きい場合、強気のクロスを検出します。
- 前回のテンカンが前回の基準以上で、現在のテンカンが現在の基準より小さい場合、弱気クロスを検出します。
- クロスの方向、価格、タイムスタンプを説明する情報ログ エントリを出力します。
使用のヒント
- 戦略ログをサブスクライブするか、カスタム通知コードを使用して
ProcessCandle メソッドを拡張することにより、この戦略を StockSharp 通知アダプター (電子メール、電報、サウンド) と組み合わせます。
- 自動取引を推進するには、
TenKijunCrossStrategy から継承して ProcessCandle をオーバーライドして、ログメッセージの代わりに、またはログメッセージに加えて注文を出します。
- アラートを揃えるために、EA で使用される元の MetaTrader チャートと一致するようにローソク足の時間枠を調整します。
オリジナルの EA との違い
- MetaTrader
SendNotification の代わりに StockSharp ログを使用します。この動作はアラートのみですが、プラットフォームのメッセージ パイプラインに依存します。
- 完全なパラメーター メタデータ (
SetDisplay、範囲、最適化フラグ) を追加して、デザイナー/オプティマイザー ツールで戦略を準備できるようにします。
- 利用可能な場合は、StockSharp チャート ウィンドウにローソク足と Ichimoku インジケーターを自動的に描画します。
ファイル
CS/TenKijunCrossStrategy.cs – アラート ロジックのメインの C# 実装。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Tenkan/Kijun cross strategy based on Ichimoku indicator.
/// Buys when Tenkan crosses above Kijun, sells when Tenkan crosses below Kijun.
/// Uses SMA proxies for Tenkan (short) and Kijun (long) since Ichimoku complex type
/// requires BindEx and special value handling.
/// </summary>
public class TenKijunCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly Queue<decimal> _highsTenkan = new();
private readonly Queue<decimal> _lowsTenkan = new();
private readonly Queue<decimal> _highsKijun = new();
private readonly Queue<decimal> _lowsKijun = new();
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 TenKijunCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for Ichimoku calculations", "General");
_tenkanPeriod = Param(nameof(TenkanPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Tenkan-sen conversion line period", "Indicators");
_kijunPeriod = Param(nameof(KijunPeriod), 34)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Kijun-sen base line period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevTenkan = null;
_prevKijun = null;
_highsTenkan.Clear();
_lowsTenkan.Clear();
_highsKijun.Clear();
_lowsKijun.Clear();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Compute Tenkan-sen = (highest high + lowest low) / 2 over TenkanPeriod
_highsTenkan.Enqueue(candle.HighPrice);
_lowsTenkan.Enqueue(candle.LowPrice);
if (_highsTenkan.Count > TenkanPeriod)
{
_highsTenkan.Dequeue();
_lowsTenkan.Dequeue();
}
// Compute Kijun-sen = (highest high + lowest low) / 2 over KijunPeriod
_highsKijun.Enqueue(candle.HighPrice);
_lowsKijun.Enqueue(candle.LowPrice);
if (_highsKijun.Count > KijunPeriod)
{
_highsKijun.Dequeue();
_lowsKijun.Dequeue();
}
if (_highsTenkan.Count < TenkanPeriod || _highsKijun.Count < KijunPeriod)
return;
var highsTenkan = _highsTenkan.ToArray();
var lowsTenkan = _lowsTenkan.ToArray();
var highsKijun = _highsKijun.ToArray();
var lowsKijun = _lowsKijun.ToArray();
var tenkan = (Max(highsTenkan) + Min(lowsTenkan)) / 2;
var kijun = (Max(highsKijun) + Min(lowsKijun)) / 2;
if (_prevTenkan is null || _prevKijun is null)
{
_prevTenkan = tenkan;
_prevKijun = kijun;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var crossUp = _prevTenkan.Value <= _prevKijun.Value && tenkan > kijun;
var crossDown = _prevTenkan.Value >= _prevKijun.Value && tenkan < kijun;
if (crossUp)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevTenkan = tenkan;
_prevKijun = kijun;
}
private static decimal Max(IEnumerable<decimal> values)
{
decimal max = decimal.MinValue;
foreach (var v in values)
if (v > max) max = v;
return max;
}
private static decimal Min(IEnumerable<decimal> values)
{
decimal min = decimal.MaxValue;
foreach (var v in values)
if (v < min) min = v;
return min;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevTenkan = null;
_prevKijun = null;
_highsTenkan.Clear();
_lowsTenkan.Clear();
_highsKijun.Clear();
_lowsKijun.Clear();
base.OnReseted();
}
}
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.Strategies import Strategy
class ten_kijun_cross_strategy(Strategy):
def __init__(self):
super(ten_kijun_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._tenkan_period = self.Param("TenkanPeriod", 12)
self._kijun_period = self.Param("KijunPeriod", 34)
self._highs_tenkan = []
self._lows_tenkan = []
self._highs_kijun = []
self._lows_kijun = []
self._prev_tenkan = None
self._prev_kijun = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def TenkanPeriod(self):
return self._tenkan_period.Value
@TenkanPeriod.setter
def TenkanPeriod(self, value):
self._tenkan_period.Value = value
@property
def KijunPeriod(self):
return self._kijun_period.Value
@KijunPeriod.setter
def KijunPeriod(self, value):
self._kijun_period.Value = value
def OnReseted(self):
super(ten_kijun_cross_strategy, self).OnReseted()
self._highs_tenkan = []
self._lows_tenkan = []
self._highs_kijun = []
self._lows_kijun = []
self._prev_tenkan = None
self._prev_kijun = None
def OnStarted2(self, time):
super(ten_kijun_cross_strategy, self).OnStarted2(time)
self._highs_tenkan = []
self._lows_tenkan = []
self._highs_kijun = []
self._lows_kijun = []
self._prev_tenkan = None
self._prev_kijun = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
tenkan_period = self.TenkanPeriod
kijun_period = self.KijunPeriod
self._highs_tenkan.append(high)
self._lows_tenkan.append(low)
while len(self._highs_tenkan) > tenkan_period:
self._highs_tenkan.pop(0)
self._lows_tenkan.pop(0)
self._highs_kijun.append(high)
self._lows_kijun.append(low)
while len(self._highs_kijun) > kijun_period:
self._highs_kijun.pop(0)
self._lows_kijun.pop(0)
if len(self._highs_tenkan) < tenkan_period or len(self._highs_kijun) < kijun_period:
return
tenkan = (max(self._highs_tenkan) + min(self._lows_tenkan)) / 2.0
kijun = (max(self._highs_kijun) + min(self._lows_kijun)) / 2.0
if self._prev_tenkan is None or self._prev_kijun is None:
self._prev_tenkan = tenkan
self._prev_kijun = kijun
return
cross_up = self._prev_tenkan <= self._prev_kijun and tenkan > kijun
cross_down = self._prev_tenkan >= self._prev_kijun and tenkan < kijun
if cross_up:
if self.Position <= 0:
self.BuyMarket()
elif cross_down:
if self.Position >= 0:
self.SellMarket()
self._prev_tenkan = tenkan
self._prev_kijun = kijun
def CreateClone(self):
return ten_kijun_cross_strategy()