クロス MQL/8186 戦略
Kloss MQL/8186 戦略は、MetaTrader 4 エキスパート アドバイザー Kloss.mq4 を直接変換したものです。これは、商品チャネル指数 (CCI)、Stochastic オシレーター、シフトされた典型的な価格フィルターを組み合わせて、単一ポジションの反転を時間指定します。 StockSharp バージョンは、高レベルのキャンドル サブスクリプション API を使用しながら、元のエントリーしきい値、ストップロスとテイクプロフィットの距離、ボリューム ロジック (固定ロット サイズまたはパーセンテージ ベースのサイジング) を維持します。
取引ロジック
- データ: 設定された時間枠 (デフォルトは 5 分) の完了したローソク足。インジケーターは同じ系列で計算されます。
- 指標:
- CCI、期間は 10。絶対値は
±CciThreshold(デフォルトは 120) と比較されます。 %K=5、%D=3を備えた Stochastic オシレーター、=3を平滑化します。メインの%Kラインは、売られすぎ/買われすぎのバンドに対してチェックされます。- 典型的な価格 ((高値 + 安値 + 終値) / 3 ) は、エキスパートアドバイザーからのシフトされた LWMA を複製するために、完成した 5 本のローソク足によって遅延されます。
- CCI、期間は 10。絶対値は
- ロングエントリー:
- CCI <=
-CciThreshold。 - Stochastic %K <
StochasticOversold(デフォルトは 30)。 - 以前のローソク足の始値 > 5 つ前のローソク足の通常価格。
- 既存のロングポジション (
Position <= 0) はありません。オープンなショートはすべてクローズされ、単一の成行注文でロングに反転されます。
- CCI <=
- 簡単なエントリー:
- CCI >=
CciThreshold。 - Stochastic %K >
StochasticOverbought(デフォルトは 70)。 - 前回のローソク足の終値 < 5 つ前のローソク足の通常価格。
- 既存のショートポジション (
Position >= 0) はありません。オープンしているロングはすべてクローズされ、1 つの成行注文でショートに反転されます。
- CCI >=
- ポジション管理: StockSharp の
StartProtectionは、指定されたポイント距離を使用してストップロス注文と利食い注文を自動的に発行します。それ以外の場合、戦略は常に単一のポジション (フラット、ロング、またはショート) を保持します。
ポジションサイズ
- 固定ボリューム:
FixedVolume > 0の場合、ストラテジーは常にその正確なボリュームを取引します (商品のVolumeStepおよびMinVolumeに合わせた後)。 - リスク パーセント:
FixedVolume = 0の場合、ストラテジーは口座価値のRiskPercent(デフォルト 0.2) を最新の終値で割って注文サイズを推定します。ボリュームはMaxVolume(デフォルトは 5) によってクランプされ、楽器のステップに丸められます。 - 安全策: 口座情報が欠落している場合、または計算された値が正でない場合、このメソッドは最小取引可能量に戻ります。
パラメーター
| 名前 | 説明 | デフォルト |
|---|---|---|
CciPeriod |
商品チャネル指数の計算に使用されるローソク足の数。 | 10 |
CciThreshold |
エントリをトリガーする絶対的な CCI レベル。 | 120 |
StochasticKPeriod |
Stochastic オシレーターの %K 周期。 | 5 |
StochasticDPeriod |
%D 平滑化期間。 | 3 |
StochasticSmooth |
追加の平滑化が信号の前の %K に適用されます。 | 3 |
StochasticOversold |
長いエントリを確認するための %K しきい値。 | 30 |
StochasticOverbought |
短いエントリを確認するための %K しきい値。 | 70 |
StopLossPoints |
保護停止の価格ポイントの距離。 | 48 |
TakeProfitPoints |
利益目標の価格点の距離。 | 152 |
FixedVolume |
正の値を指定すると、取引量が固定されます。 | 0 |
RiskPercent |
FixedVolume がゼロの場合に、ポートフォリオの割合を体積に変換します。 |
0.2 |
MaxVolume |
許可される最大取引量。 | 5 |
CandleType |
インジケーター計算のローソク足の種類/時間枠。 | 5分の時間枠 |
実行メモ
- 単一ポジション: 1 つのポジションのみがオープンのままです。反転では、既存のポジションをクローズし、単一の成行注文で新しいポジションをオープンします。
- インジケーターの同期: 価格シフトには、最後に完了した 5 つのローソク足が使用されます。最初の取引が表示される前に、少なくとも 6 つのローソク足が処理される必要があります。
- ストップ/ターゲット:
StartProtectionは、商品のPriceStepを使用して、ポイントベースの距離を絶対価格オフセットに変換します。PriceStepが不明な場合は、生のポイント値が適用されます。 - データ要件: OHLC キャンドルを提供するあらゆる機器で動作します。ボリューム アライメントは、利用可能な場合は
MinVolumeとVolumeStepを尊重します。 - MT4 との違い: MetaTrader の証拠金の計算は、アカウントの資本 (
Portfolio.CurrentValue) によって概算されます。株式データが利用できない場合、戦略は最小取引可能量に戻ります。
使用のヒント
CandleTypeを、MetaTrader で使用されるマーケット セッションに合わせて調整します (元のテンプレートでは M5)。- ティックサイズに応じた停止距離を確認します。ポイントから価格への変換は自動的に行われますが、非外国為替商品に合わせて値を調整する必要がある場合があります。
- 固定契約サイズの場合は、
FixedVolumeを希望のロットに設定し、RiskPercentをゼロに設定します。 - 新しいシンボルの戦略を調整するときに、インジケーターのしきい値の最適化を有効にします。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy converted from the MetaTrader 4 "Kloss" expert advisor (MQL/8186).
/// Implements the original combination of CCI and Stochastic oscillators with a price shift filter.
/// </summary>
public class KlossMql8186Strategy : Strategy
{
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciThreshold;
private readonly StrategyParam<int> _stochasticKPeriod;
private readonly StrategyParam<int> _stochasticDPeriod;
private readonly StrategyParam<int> _stochasticSmooth;
private readonly StrategyParam<decimal> _stochasticOversold;
private readonly StrategyParam<decimal> _stochasticOverbought;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _fixedVolume;
private readonly StrategyParam<decimal> _riskPercent;
private readonly StrategyParam<decimal> _maxVolume;
private readonly StrategyParam<DataType> _candleType;
private CommodityChannelIndex _cci = null!;
private StochasticOscillator _stochastic = null!;
private decimal? _previousOpen;
private decimal? _previousClose;
private readonly decimal?[] _typicalHistory = new decimal?[5];
/// <summary>
/// Initializes a new instance of the <see cref="KlossMql8186Strategy"/> class.
/// </summary>
public KlossMql8186Strategy()
{
_cciPeriod = Param(nameof(CciPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Number of candles for the CCI calculation", "Indicators")
.SetOptimize(5, 40, 5);
_cciThreshold = Param(nameof(CciThreshold), 150m)
.SetGreaterThanZero()
.SetDisplay("CCI Threshold", "Absolute CCI level that triggers entries", "Indicators")
.SetOptimize(80m, 200m, 10m);
_stochasticKPeriod = Param(nameof(StochasticKPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
.SetOptimize(3, 15, 1);
_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "SMA length of the %D line", "Indicators")
.SetOptimize(1, 10, 1);
_stochasticSmooth = Param(nameof(StochasticSmooth), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic Smoothing", "Smoothing applied to the %K calculation", "Indicators")
.SetOptimize(1, 10, 1);
_stochasticOversold = Param(nameof(StochasticOversold), 45m)
.SetNotNegative()
.SetDisplay("Stochastic Oversold", "Threshold under which %K confirms a long signal", "Signals")
.SetOptimize(10m, 40m, 5m);
_stochasticOverbought = Param(nameof(StochasticOverbought), 55m)
.SetNotNegative()
.SetDisplay("Stochastic Overbought", "Threshold above which %K confirms a short signal", "Signals")
.SetOptimize(60m, 90m, 5m);
_stopLossPoints = Param(nameof(StopLossPoints), 48m)
.SetNotNegative()
.SetDisplay("Stop Loss (pts)", "Stop loss distance expressed in price points", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 152m)
.SetNotNegative()
.SetDisplay("Take Profit (pts)", "Take profit distance expressed in price points", "Risk");
_fixedVolume = Param(nameof(FixedVolume), 0m)
.SetNotNegative()
.SetDisplay("Fixed Volume", "If greater than zero this volume will be used for orders", "Trading");
_riskPercent = Param(nameof(RiskPercent), 0.2m)
.SetNotNegative()
.SetDisplay("Risk Percent", "Fraction of portfolio value used when Fixed Volume is zero", "Trading");
_maxVolume = Param(nameof(MaxVolume), 5m)
.SetGreaterThanZero()
.SetDisplay("Max Volume", "Upper limit for the trade volume", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for indicator calculations", "General");
}
/// <summary>
/// CCI lookback period.
/// </summary>
public int CciPeriod { get => _cciPeriod.Value; set => _cciPeriod.Value = value; }
/// <summary>
/// Absolute CCI level that triggers entries.
/// </summary>
public decimal CciThreshold { get => _cciThreshold.Value; set => _cciThreshold.Value = value; }
/// <summary>
/// Stochastic %K period.
/// </summary>
public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
/// <summary>
/// Stochastic %D period.
/// </summary>
public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
/// <summary>
/// Stochastic smoothing factor.
/// </summary>
public int StochasticSmooth { get => _stochasticSmooth.Value; set => _stochasticSmooth.Value = value; }
/// <summary>
/// Stochastic oversold threshold.
/// </summary>
public decimal StochasticOversold { get => _stochasticOversold.Value; set => _stochasticOversold.Value = value; }
/// <summary>
/// Stochastic overbought threshold.
/// </summary>
public decimal StochasticOverbought { get => _stochasticOverbought.Value; set => _stochasticOverbought.Value = value; }
/// <summary>
/// Stop loss distance in points.
/// </summary>
public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
/// <summary>
/// Take profit distance in points.
/// </summary>
public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
/// <summary>
/// Fixed trading volume.
/// </summary>
public decimal FixedVolume { get => _fixedVolume.Value; set => _fixedVolume.Value = value; }
/// <summary>
/// Fraction of the portfolio value converted into volume when <see cref="FixedVolume"/> is zero.
/// </summary>
public decimal RiskPercent { get => _riskPercent.Value; set => _riskPercent.Value = value; }
/// <summary>
/// Maximum volume allowed per trade.
/// </summary>
public decimal MaxVolume { get => _maxVolume.Value; set => _maxVolume.Value = value; }
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousOpen = null;
_previousClose = null;
Array.Clear(_typicalHistory, 0, _typicalHistory.Length);
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cci = new CommodityChannelIndex { Length = CciPeriod };
_stochastic = new StochasticOscillator();
_stochastic.K.Length = StochasticKPeriod;
_stochastic.D.Length = StochasticDPeriod;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
// Configure automatic position protection for stop loss and take profit.
StartProtection(
takeProfit: CreatePriceUnit(TakeProfitPoints),
stopLoss: CreatePriceUnit(StopLossPoints));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var cciResult = _cci.Process(candle).ToNullableDecimal();
var stochResult = _stochastic.Process(candle);
UpdateHistory(candle);
if (cciResult is null)
return;
if (!_stochastic.IsFormed)
return;
var stochValue = (StochasticOscillatorValue)stochResult;
if (stochValue.K is not decimal stochMain)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var cci = cciResult.Value;
if (_previousOpen is decimal prevOpen &&
_previousClose is decimal prevClose &&
_typicalHistory[4] is decimal shiftedTypical)
{
var buySignal = cci <= -CciThreshold && stochMain < StochasticOversold && prevOpen > shiftedTypical;
var sellSignal = cci >= CciThreshold && stochMain > StochasticOverbought && prevClose < shiftedTypical;
if (buySignal && Position <= 0)
BuyMarket();
else if (sellSignal && Position >= 0)
SellMarket();
}
}
private void UpdateHistory(ICandleMessage candle)
{
// Shift stored typical prices to keep five previous values available.
for (var i = _typicalHistory.Length - 1; i > 0; i--)
_typicalHistory[i] = _typicalHistory[i - 1];
_typicalHistory[0] = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
// Store previous candle prices for the next iteration.
_previousOpen = candle.OpenPrice;
_previousClose = candle.ClosePrice;
}
private Unit CreatePriceUnit(decimal points)
{
if (points <= 0)
return new Unit(0m, UnitTypes.Absolute);
if (Security?.PriceStep is decimal priceStep && priceStep > 0)
return new Unit(points * priceStep, UnitTypes.Absolute);
return new Unit(points, UnitTypes.Absolute);
}
}
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, UnitTypes, Unit
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import CommodityChannelIndex, StochasticOscillator, CandleIndicatorValue
class kloss_mql8186_strategy(Strategy):
def __init__(self):
super(kloss_mql8186_strategy, self).__init__()
self._cci_period = self.Param("CciPeriod", 10) \
.SetDisplay("CCI Period", "Number of candles for the CCI calculation", "Indicators")
self._cci_threshold = self.Param("CciThreshold", 150.0) \
.SetDisplay("CCI Threshold", "Absolute CCI level that triggers entries", "Indicators")
self._stochastic_k_period = self.Param("StochasticKPeriod", 5) \
.SetDisplay("Stochastic %K", "Period of the %K line", "Indicators")
self._stochastic_d_period = self.Param("StochasticDPeriod", 3) \
.SetDisplay("Stochastic %D", "SMA length of the %D line", "Indicators")
self._stochastic_smooth = self.Param("StochasticSmooth", 3) \
.SetDisplay("Stochastic Smoothing", "Smoothing applied to the %K calculation", "Indicators")
self._stochastic_oversold = self.Param("StochasticOversold", 45.0) \
.SetDisplay("Stochastic Oversold", "Threshold under which %K confirms a long signal", "Signals")
self._stochastic_overbought = self.Param("StochasticOverbought", 55.0) \
.SetDisplay("Stochastic Overbought", "Threshold above which %K confirms a short signal", "Signals")
self._stop_loss_points = self.Param("StopLossPoints", 48.0) \
.SetDisplay("Stop Loss (pts)", "Stop loss distance expressed in price points", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 152.0) \
.SetDisplay("Take Profit (pts)", "Take profit distance expressed in price points", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Timeframe used for indicator calculations", "General")
self._cci = None
self._stochastic = None
self._previous_open = None
self._previous_close = None
self._typical_history = [None] * 5
@property
def CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
@property
def CciThreshold(self):
return self._cci_threshold.Value
@CciThreshold.setter
def CciThreshold(self, value):
self._cci_threshold.Value = value
@property
def StochasticKPeriod(self):
return self._stochastic_k_period.Value
@StochasticKPeriod.setter
def StochasticKPeriod(self, value):
self._stochastic_k_period.Value = value
@property
def StochasticDPeriod(self):
return self._stochastic_d_period.Value
@StochasticDPeriod.setter
def StochasticDPeriod(self, value):
self._stochastic_d_period.Value = value
@property
def StochasticSmooth(self):
return self._stochastic_smooth.Value
@StochasticSmooth.setter
def StochasticSmooth(self, value):
self._stochastic_smooth.Value = value
@property
def StochasticOversold(self):
return self._stochastic_oversold.Value
@StochasticOversold.setter
def StochasticOversold(self, value):
self._stochastic_oversold.Value = value
@property
def StochasticOverbought(self):
return self._stochastic_overbought.Value
@StochasticOverbought.setter
def StochasticOverbought(self, value):
self._stochastic_overbought.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(kloss_mql8186_strategy, self).OnStarted2(time)
self._cci = CommodityChannelIndex()
self._cci.Length = self.CciPeriod
self._stochastic = StochasticOscillator()
self._stochastic.K.Length = self.StochasticKPeriod
self._stochastic.D.Length = self.StochasticDPeriod
self._previous_open = None
self._previous_close = None
self._typical_history = [None] * 5
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
tp_points = float(self.TakeProfitPoints)
sl_points = float(self.StopLossPoints)
tp_unit = Unit(tp_points, UnitTypes.Absolute) if tp_points > 0 else Unit(0, UnitTypes.Absolute)
sl_unit = Unit(sl_points, UnitTypes.Absolute) if sl_points > 0 else Unit(0, UnitTypes.Absolute)
self.StartProtection(takeProfit=tp_unit, stopLoss=sl_unit)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
cci_input = CandleIndicatorValue(self._cci, candle)
cci_input.IsFinal = True
cci_result = self._cci.Process(cci_input)
stoch_input = CandleIndicatorValue(self._stochastic, candle)
stoch_input.IsFinal = True
stoch_result = self._stochastic.Process(stoch_input)
self._update_history(candle)
if not cci_result.IsFormed:
return
if not self._stochastic.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
cci_val = float(cci_result)
stoch_k_val = float(stoch_result.K) if stoch_result.K is not None else None
if stoch_k_val is None:
return
if self._previous_open is not None and self._previous_close is not None and self._typical_history[4] is not None:
prev_open = self._previous_open
prev_close = self._previous_close
shifted_typical = self._typical_history[4]
buy_signal = cci_val <= -float(self.CciThreshold) and stoch_k_val < float(self.StochasticOversold) and prev_open > shifted_typical
sell_signal = cci_val >= float(self.CciThreshold) and stoch_k_val > float(self.StochasticOverbought) and prev_close < shifted_typical
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
def _update_history(self, candle):
for i in range(len(self._typical_history) - 1, 0, -1):
self._typical_history[i] = self._typical_history[i - 1]
self._typical_history[0] = (float(candle.HighPrice) + float(candle.LowPrice) + float(candle.ClosePrice)) / 3.0
self._previous_open = float(candle.OpenPrice)
self._previous_close = float(candle.ClosePrice)
def OnReseted(self):
super(kloss_mql8186_strategy, self).OnReseted()
self._previous_open = None
self._previous_close = None
self._typical_history = [None] * 5
def CreateClone(self):
return kloss_mql8186_strategy()