GitHub で見る
KSRobot 1.5 戦略
概要
KSRobot 1.5 戦略 は、MetaTrader 4 Expert Advisor KSRobot_1_5_h1_v1.mq4 を C# に変換したものです。 StockSharp バージョンは、20 期間の線形加重移動平均 (LWMA) によって確認される基準線の価格ブレイクを取引するという元のアイデアを維持しながら、厳格な取引ウィンドウと多層的なリスク管理を強化します。デフォルトではすべての計算は 30 分足のローソク足で実行されますが、時間枠はパラメーターを通じて変更できます。
市場データと指標
- Ichimoku デフォルトではテンカン/キジュン/センコウ スパン B 期間 6/12/24 のインジケーター。
- 長さ 20 の線形加重移動平均 (LWMA) を使用して、傾きと最小距離フィルターを測定します。
- 時間枠付きキャンドル シグナル生成用に
CandleType (デフォルトは M30) によって定義されます。
取引ロジック
長いワークフロー
- ローソクは下から基順線と相互作用する必要があります。次のいずれかがあれば十分です。ローソク足が下で始まり上で閉じる、前の終値が下で新しい終値が上である、またはローソクの安値がレベルを突き抜けている。
- 最新の基準値は横ばい、または 2 バーを超えて上昇しており、基準線の即時の下落に対する取引が妨げられています。
- LWMA は少なくとも
MaFilterPips(価格単位に換算)下にあります。これは、移動平均がベースラインを数ピップ下回るという要件を再現します。
- LWMA の傾きは正です (現在の LWMA が前のバーより大きい)。
- セットアップは、スロープ条件が満たされるまで保留状態として保存されます。 MQL の
longcross/shortcross フラグを模倣して、常に一方の側のみを保留にすることができます。
- すべての基準が一致し、正味ロングエクスポージャーが存在しない場合、成行買い注文が送信されます。戦略によってキャッシュされたエントリー価格は、ストップ、損益分岐点、およびトレーリング管理の基礎となります。
短いワークフロー
ミラー条件が適用されます。
- ローソクは上からキジュンと相互作用します(上で開いて下で閉じる、上で前の終値と下で現在の終値、または高値がレベルに触れます)。
- ギジュンは横ばいか、2 バーよりも下がっています。
- LWMA はキジュンの上の
MaFilterPips に位置しています。
- LWMA の傾きは前のバーと比較して負です。
- オリジナルのエキスパートと同様に、保留中のショートは 1 つだけ追跡され、ロングシグナルが表示されるとクリアされます。
- 満足し、口座がまだショートしていない場合、成行売り注文が送信されます。
撤退ルールとリスク管理
- 時間枠 – 新しい取引は、ローソク足の開始時間が
[TradingStartHour, TradingEndHour) (デフォルトの為替時間 07:00 ~ 19:00) 内にある間のみ考慮されます。
- 初期ストップロス –
StopLossPips をエントリー価格より上下に設定します (商品のピップ サイズによって換算)。ゼロの場合、初期停止は追跡されません。
- 損益分岐点移動 – 未実現利益が
BreakEvenPips を超えるとすぐに、ストップはエントリー価格にロングの場合は 1 ピップを加えた値 (ショートの場合はマイナス 1 ピップ) に移動します。この動作は、MT4 の「BE+1 への移動」ロジックをエミュレートするために _breakEvenStep によって制御されます。
- トレーリングストップ – 価格が
TrailingStopPips だけ進むと、ストップは有利な方向にのみその距離でトレーリングします。
- テイクプロフィット –
TakeProfitPips で定義されたオプションの固定ターゲット距離。無効にするには、ゼロに設定します。
- スロープエグジット – ストップがエントリーを通過する前に LWMA が取引に反対した場合、ポジションは直ちにクローズされます。これは、MQL スクリプトからの「MA が間違っている」終了をキャプチャしています。
- 優先 – 同じローソク足内でストップロスとテイクプロフィットの両方がタッチされる場合、ローソク足データを保守的に保つためにストップロスが優先されます。
パラメーター
| パラメータ |
デフォルト |
説明 |
TenkanPeriod |
6 |
Ichimoku インジケーターの Tenkan-sen の長さ。 |
KijunPeriod |
12 |
基順線の長さ(メイントリガー)。 |
SenkouSpanBPeriod |
24 |
閃光スパンBの長さ。 |
LwmaPeriod |
20 |
LWMA 確認の期間。 |
MaFilterPips |
6 |
LWMA と Kijun 間の最小ピップ距離。 |
StopLossPips |
50 |
初期保護停止距離。 |
BreakEvenPips |
9 |
損益分岐点にストップを移動する前に必要な利益。 |
TrailingStopPips |
10 |
価格が利益に転じた後のトレーリングストップ距離。 |
TakeProfitPips |
120 |
オプションの固定テイクプロフィットディスタンス。 |
TradingStartHour |
7 |
新しい取引の処理を開始するための包括的な時間。 |
TradingEndHour |
19 |
新規エントリーを停止する特別な時間。 |
CandleType |
30分の時間枠 |
キャンドルのサブスクリプションに使用されるデータ型。 |
すべての pip ベースのパラメータは、Security.PriceStep (または MinPriceStep) を使用して価格単位に変換されます。 10 進数 3 桁または 5 桁でクォートされた商品には、標準の FX ピップ サイズを再作成するために、自動的に ×10 の乗数が適用されます。
実装メモ
- この戦略は、
SubscribeCandles().BindEx(...) を介して Ichimoku と LWMA インジケーターの両方をバインドし、手動で収集することなく値がインジケーター パイプラインから直接取得されるようにします。
- ポジション管理は MT4 エキスパートを反映しています。保留レベルは
longcross/shortcross フラグを置き換え、取引がトリガーされるとクリアされます。
- プロテクションレベルはエントリ後にキャッシュされるため、個々の注文が更新されなくても、損益分岐点とトレーリングの決定はローソク足レベルのデータで機能します。
- すべての保護アクションが戦略コード内で処理され、特注の MT4 ロジックに一致するため、
StartProtection はゼロ距離で呼び出されます。
- 成行注文のみが使用されます。元の指値対市場の選択は、ローソク足ベースのバックテストでは利用できない買い/売りティックに依存していました。
使用法
- ストラテジー インスタンスを作成し、
Security、Portfolio、Volume を割り当て、StockSharp 環境内で開始します。
- 必要に応じて、特定の楽器の pip ベースのパラメーターを調整します。 MQL コメント (GBPUSD、EURUSD) からの最適化されたプリセットは、実行前にデフォルトを変更することで再現できます。
- ログ出力に注目してください。エントリ、損益分岐点移動、トレーリング調整、緊急出口は、
LogInfo 呼び出しを通じて報告されます。
- 生成されたチャート領域 (ローソク足、Ichimoku、LWMA、独自の取引) をデザイナーまたはバックテスターに接続して、取引フローを視覚化します。
C# バージョンのみが提供されます。要件に従って Python フォルダーは作成されません。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class KsRobotV15Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose;
private decimal _prevEma;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public KsRobotV15Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA lookback", "Indicators");
_smaPeriod = Param(nameof(SmaPeriod), 50).SetDisplay("SMA Period", "SMA filter", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose = default;
_prevEma = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevClose = 0;
_prevEma = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var sma = new SimpleMovingAverage { Length = SmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, sma, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal sma)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = close;
_prevEma = ema;
return;
}
if (_prevClose <= _prevEma && close > ema && close > sma && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevClose >= _prevEma && close < ema && close < sma && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevClose = close;
_prevEma = ema;
}
}
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 ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ks_robot_v15_strategy(Strategy):
def __init__(self):
super(ks_robot_v15_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 50) \
.SetDisplay("EMA Period", "EMA lookback", "Indicators")
self._sma_period = self.Param("SmaPeriod", 50) \
.SetDisplay("SMA Period", "SMA filter", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200) \
.SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self):
return self._ema_period.Value
@property
def sma_period(self):
return self._sma_period.Value
@property
def cooldown_candles(self):
return self._cooldown_candles.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ks_robot_v15_strategy, self).OnReseted()
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(ks_robot_v15_strategy, self).OnStarted2(time)
self._prev_close = 0.0
self._prev_ema = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
sma = SimpleMovingAverage()
sma.Length = self.sma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, sma, self.process_candle).Start()
def process_candle(self, candle, ema, sma):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
sma_val = float(sma)
if not self._has_prev:
self._prev_close = close
self._prev_ema = ema_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_ema = ema_val
return
if self._prev_close <= self._prev_ema and close > ema_val and close > sma_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_close >= self._prev_ema and close < ema_val and close < sma_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_close = close
self._prev_ema = ema_val
def CreateClone(self):
return ks_robot_v15_strategy()