GitHub で見る
CCI MACD ダフ屋
概要
CCI MACD スカルパーは、MetaTrader 5 エキスパート アドバイザー「CCI + MACD スカルパー」を StockSharp の高レベル戦略 API に移植します。この変換では、資金管理ロジックを StockSharp の規則に変換しながら、元のインジケーター スタック(EMA トレンド フィルター、CCI ゼロライン トリガー、MACD 発散チェック)を維持します。注文はポートフォリオの資産から自動的に決定され、距離が近すぎる場合はストップが拒否され、オプションのトレーリングストップは最初の調整後にポジションを部分的にクローズすることで利益を確保できます。 5 つのキャンドルのクールダウンにより、実行直後にストラテジーが再入することがなくなり、MQL タイマーの動作が再現されます。
戦略ロジック
インジケーターとデータ処理
- ローソク足 – 1 つの構成可能な時間枠によってすべての計算が行われます。再描画を避けるために、信号は完成したキャンドルのみで評価されます。
- EMA(34) – 終値指数移動平均は方向性フィルターとして機能します。ロングの場合は最新の終値が以前の EMA の値を上回る必要があり、ショートの場合は終値がそれを下回っている必要があります。
- CCI(50) – 勢いトリガーとして使用されます。この戦略は、最後に完成した 2 つのローソク足で発生したゼロラインのクロスを待ちます (現在のローソク足は設定を確認しますが、論理比較には参加しません)。
- MACD(12,26,9) – MACD のメイン ラインとシグナル ラインは両方とも、前の 2 つのローソク足でゼロの同じ側に留まっている必要があります。エントリーするには、MACD シグナル ラインがこれら 2 つのバーの間のポジションを優先してメイン ラインを横切る必要があります (ロングの場合は強気のクロスオーバー、ショートの場合は弱気のクロスオーバー)。
- スイングバッファ – 最後の 5 つの終了したローソク足の高値と安値がストップロスの基準を形成します。ロングは最低安値に固定され、ショートは最高高値にアンカーされ、1 バーのシフトで MetaTrader
iLowest/iHighest の呼び出しと正確に一致します。
エントリールール
- セッション制御 – ローソク足の終了時間が現地ターミナル時間で
[MinHour, MaxHour] 以内にある場合にのみ取引が許可されます。
- クールダウン – 各エントリーが記入された後、システムは新しい取引を許可する前に 5 つのローソク足期間待機し、元のコードから
EventSetTimer をミラーリングします。
- 長いセットアップ
- アクティブなロングポジション (
Position <= 0) がありません。
- 終値が以前の EMA 値を上回っています。
- CCI は、直近の 2 つの閉じたローソク足でマイナスからプラスに転じました。
- 同じ 2 つのバーの間に、MACD のクロスオーバーがゼロ以下で発生しました (信号は MACD を超えました)。
- 直近のスイングローに位置するストップロスは、最小距離制約を満たします。
- 簡単なセットアップ
- アクティブなショートポジション (
Position >= 0) はありません。
- 終値が以前の EMA の値を下回っています。
- CCI は、完了した最後の 2 つのローソク足でプラスからマイナスに転じました。
- MACD クロスオーバーがゼロより上で発生しました (信号は MACD を下回りました)。
- スイングハイでのストップロスは、最小距離要件を尊重します。
リスクと取引の管理
- 動的なポジションサイジング – 取引サイズは、ポートフォリオ株式の設定された
RiskPercent から導出されます。契約ごとのリスクは、ストップロス距離、証券価格ステップおよびステップ値から計算されます。結果はインストゥルメントのボリュームステップにスナップされ、最小ボリュームと最大ボリュームの間にクランプされます。
- ストップロス / テイクプロフィット – ストップロスは選択された極端なスイングを使用し、距離が
MinimalStopLossPoints を下回る場合は拒否されます。テイクプロフィットは entry ± RiskReward × stopDistance に等しく、EA の報酬対リスクの計算と一致します。
- トレーリングストップ (オプション) – 有効にすると、価格が前のストップを十分に超えて終了すると、ストップが
TrailingStopPoints だけ移動します。最初のトレーリング調整は、元のボリュームの半分を閉じる部分的な終了をトリガーし、MetaTrader の実装を忠実にミラーリングします。
- 保護的エグジット – ロングの場合、価格がストップレベル (ローソク足の安値) を突破するか、テイクプロフィットレベル (ローソク足の高値) に達すると、ポジションがクローズされます。ショートは、ローソク足の高値と安値をそれぞれ使用したロジックを反映します。
パラメーター
| 名前 |
説明 |
デフォルト |
CandleType |
インジケーターの計算を駆動する時間枠。 |
15分キャンドル |
RiskPercent |
各取引でリスクを負うポートフォリオ資産の割合。 |
2% |
RiskReward |
テイクプロフィットレベルの報酬とリスクの乗数。 |
1.5 |
EmaPeriod |
EMA トレンド フィルターの長さ。 |
34 |
CciPeriod |
商品チャネルインデックスの長さ。 |
50 |
MinHour |
新しい取引が開始される可能性がある最も早い時間 (両端を含む)。 |
0 |
MaxHour |
新しい取引が開始される可能性のある最新の時間 (含む)。 |
24 |
MinimalStopLossPoints |
価格ポイントで表されるエントリーとストップロスの間の最小許容距離。 |
100 |
UseTrailingStop |
トレーリングストップモジュールと部分的なテイクプロフィットを有効にします。 |
障害者 |
TrailingStopPoints |
トレーリング ストップの距離は価格ポイントで測定されます。 |
100 |
追加の注意事項
- 価格ポイントの変換は証券の
PriceStep に依存します。有効なステップのないシンボルは、1 価格単位の距離にフォールバックします。
- ポートフォリオの資本は
Portfolio.CurrentValue から取得され、現在の評価額が利用できない場合は BeginValue に戻ります。両方が欠落している場合、戦略は基本の Volume プロパティに戻ります。
- この戦略には Python ポートはありません。 API パッケージには C# バージョンのみが含まれています。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "CCI MACD Scalper" MetaTrader expert.
/// Uses CCI zero-line crossover with EMA trend filter for scalping entries.
/// </summary>
public class CciMacdScalperStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _cciPeriod;
private ExponentialMovingAverage _ema;
private CommodityChannelIndex _cci;
private decimal? _prevCci;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
public CciMacdScalperStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for scalping", "General");
_emaPeriod = Param(nameof(EmaPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA trend filter period", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "CCI period for zero-line crosses", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCci = null;
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
_cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, _cci, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal cciValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_cci.IsFormed)
{
_prevCci = cciValue;
return;
}
if (_prevCci is null)
{
_prevCci = cciValue;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
var close = candle.ClosePrice;
// CCI crosses back above the oversold zone with trend confirmation -> buy
var cciCrossUp = _prevCci.Value <= -50m && cciValue > -50m;
// CCI crosses back below the overbought zone with trend confirmation -> sell
var cciCrossDown = _prevCci.Value >= 50m && cciValue < 50m;
if (cciCrossUp && close > emaValue)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (cciCrossDown && close < emaValue)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevCci = cciValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_ema = null;
_cci = null;
_prevCci = null;
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.Indicators import ExponentialMovingAverage, CommodityChannelIndex
from StockSharp.Algo.Strategies import Strategy
class cci_macd_scalper_strategy(Strategy):
def __init__(self):
super(cci_macd_scalper_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._ema_period = self.Param("EmaPeriod", 21)
self._cci_period = self.Param("CciPeriod", 14)
self._prev_cci = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def CciPeriod(self):
return self._cci_period.Value
@CciPeriod.setter
def CciPeriod(self, value):
self._cci_period.Value = value
def OnReseted(self):
super(cci_macd_scalper_strategy, self).OnReseted()
self._prev_cci = None
def OnStarted2(self, time):
super(cci_macd_scalper_strategy, self).OnStarted2(time)
self._prev_cci = None
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
cci = CommodityChannelIndex()
cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema, cci, self._process_candle).Start()
def _process_candle(self, candle, ema_value, cci_value):
if candle.State != CandleStates.Finished:
return
ema_val = float(ema_value)
cci_val = float(cci_value)
close = float(candle.ClosePrice)
if self._prev_cci is None:
self._prev_cci = cci_val
return
# CCI crosses back above oversold zone with trend confirmation -> buy
cci_cross_up = self._prev_cci <= -50.0 and cci_val > -50.0
# CCI crosses back below overbought zone with trend confirmation -> sell
cci_cross_down = self._prev_cci >= 50.0 and cci_val < 50.0
if cci_cross_up and close > ema_val:
if self.Position <= 0:
self.BuyMarket()
elif cci_cross_down and close < ema_val:
if self.Position >= 0:
self.SellMarket()
self._prev_cci = cci_val
def CreateClone(self):
return cci_macd_scalper_strategy()