ホーム
/
戦略のサンプル
GitHub で見る
Ang Zad C Time MM Recovery戦略
概要
Ang Zad C Time MM Recovery戦略は、MetaTrader 5エキスパートアドバイザーExp_Ang_Zad_C_Tm_MMRecのC#ポートです。この戦略は、カスタムAng_Zad_Cチャンネルインジケーターと設定可能なトレードセッションフィルター、そして設定可能な数の負けトレード後にリスクを削減する適応型ポジションサイズモデルを組み合わせています。
インジケーターロジック
Ang_Zad_Cインジケーターは価格の周りに2つの適応型エンベロープを構築します。各エンベロープは、現在と前のローソク足の選択された適用価格を比較し、平滑化係数Ki で新しい価格に向かって移動することで更新されます。上線と下線は、未完成のローソク足での行動を避けるため、Signal Bar で定義された過去のバーで評価されます。
トレードルール
ロングエントリー – 前の参照バーで上線が下線の上にあり、最新の参照バーで下線を下に交差するか触れた場合。これが起こると、新しいロングポジションを開く前に、開いているショートポジションが閉じられます(有効な場合)。
ショートエントリー – 前の参照バーで上線が下線の下にあり、最新の参照バーで下線を上に交差するか触れた場合。開いているロングポジションが新しいショートポジションを開く前に閉じられます(有効な場合)。
ロングエグジット – 前の参照バーで上線が下線の下にある場合。エグジットはEnable Long Exit で無効にできます。
ショートエグジット – 前の参照バーで上線が下線の上にある場合。エグジットはEnable Short Exit で無効にできます。
マネーマネジメントと保護
Use Time Filter が有効な場合、設定された時間ウィンドウ内でのみトレードが許可されます。以前に開かれたポジションはセッション終了時に閉じられます。
トレードボリュームは、各サイドで発生した負けトレードの数に応じてNormal Volume とSmall Volume の間で選択されます。Buy Loss Trigger 回のロング負けトレード(またはSell Loss Trigger 回のショート負けトレード)の後、利益が出るトレードがカウンターをリセットするまで、減少したボリュームが使用されます。
オプションのストップロスとテイクプロフィットレベルは、Stop Loss Steps とTake Profit Steps で定義された価格ステップ距離を使用して登録されます。
パラメーター
名前
説明
Candle Type
インジケーターとシグナルが使用するローソク足のタイムフレーム。
Ki
Ang_Zad_Cエンベロープの平滑化係数。
Applied Price
インジケーターに入力されるローソク足価格。
Signal Bar
シグナル評価に使用するバーの遡り数(1 = 前の終値バー)。
Use Time Filter / Trade Start / Trade End
セッションベースのトレードを有効にし、セッションの開始時刻と終了時刻を設定する。
Enable Long/Short Entry
新しいロングまたはショートトレードの開始を許可する。
Enable Long/Short Exit
インジケーターの逆転時にポジションを閉じることを戦略に許可する。
Buy/Sell Loss Trigger
減少したボリュームが適用される前の負けトレードの数。
Small Volume / Normal Volume
減少したリスクと通常リスクに使用される注文サイズ。
Stop Loss Steps / Take Profit Steps
価格ステップで表現された保護注文の距離。
変換ノート
ロジックは、方向性クロスチェックと時間ウィンドウ動作を含む、オリジナルのMQL5コードに従っています。
適応型マネーマネジメントは、方向ごとの実現損益を追跡し、設定された損失数の後に減少したボリュームに切り替えることで実装されています。
インジケーターの計算はバッファへの直接アクセスを避け、StockSharpの高レベルAPIを使用して完成したローソク足で処理されます。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ang_Zad_C adaptive channel strategy.
/// Trades when the upper/lower lines cross, indicating trend reversal.
/// </summary>
public class AngZadCTimeMMRecoveryStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _ki;
private bool _hasState;
private decimal _upperLine;
private decimal _lowerLine;
private decimal _previousPrice;
private decimal? _prevUp;
private decimal? _prevDn;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal Ki
{
get => _ki.Value;
set => _ki.Value = value;
}
public AngZadCTimeMMRecoveryStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_ki = Param(nameof(Ki), 4.000001m)
.SetDisplay("Ki", "Smoothing coefficient", "Indicator")
.SetGreaterThanZero();
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_hasState = false;
_upperLine = 0m;
_lowerLine = 0m;
_previousPrice = 0m;
_prevUp = null;
_prevDn = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasState = false;
_prevUp = null;
_prevDn = null;
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;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var price = candle.ClosePrice;
var (upper, lower) = UpdateIndicator(price);
if (_prevUp == null || _prevDn == null)
{
_prevUp = upper;
_prevDn = lower;
return;
}
var prevUp = _prevUp.Value;
var prevDn = _prevDn.Value;
// Buy signal: previous upper was below lower, now crossing above
if (prevUp <= prevDn && upper > lower)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
// Sell signal: previous upper was above lower, now crossing below
else if (prevUp >= prevDn && upper < lower)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
_prevUp = upper;
_prevDn = lower;
}
private (decimal Up, decimal Down) UpdateIndicator(decimal price)
{
if (!_hasState)
{
_upperLine = price;
_lowerLine = price;
_previousPrice = price;
_hasState = true;
return (_upperLine, _lowerLine);
}
var ki = Ki;
if (price > _upperLine && price > _previousPrice)
_upperLine += (price - _upperLine) / ki;
if (price < _upperLine && price < _previousPrice)
_upperLine += (price - _upperLine) / ki;
if (price > _lowerLine && price < _previousPrice)
_lowerLine += (price - _lowerLine) / ki;
if (price < _lowerLine && price > _previousPrice)
_lowerLine += (price - _lowerLine) / ki;
_previousPrice = price;
return (_upperLine, _lowerLine);
}
}
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 ang_zad_c_time_mm_recovery_strategy(Strategy):
def __init__(self):
super(ang_zad_c_time_mm_recovery_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._ki = self.Param("Ki", 4.000001) \
.SetDisplay("Ki", "Smoothing coefficient", "Indicator")
self._has_state = False
self._upper_line = 0.0
self._lower_line = 0.0
self._previous_price = 0.0
self._prev_up = None
self._prev_dn = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Ki(self):
return self._ki.Value
def OnReseted(self):
super(ang_zad_c_time_mm_recovery_strategy, self).OnReseted()
self._has_state = False
self._upper_line = 0.0
self._lower_line = 0.0
self._previous_price = 0.0
self._prev_up = None
self._prev_dn = None
def OnStarted2(self, time):
super(ang_zad_c_time_mm_recovery_strategy, self).OnStarted2(time)
self._has_state = False
self._prev_up = None
self._prev_dn = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _update_indicator(self, price):
if not self._has_state:
self._upper_line = price
self._lower_line = price
self._previous_price = price
self._has_state = True
return (self._upper_line, self._lower_line)
ki = float(self.Ki)
if price > self._upper_line and price > self._previous_price:
self._upper_line += (price - self._upper_line) / ki
if price < self._upper_line and price < self._previous_price:
self._upper_line += (price - self._upper_line) / ki
if price > self._lower_line and price < self._previous_price:
self._lower_line += (price - self._lower_line) / ki
if price < self._lower_line and price > self._previous_price:
self._lower_line += (price - self._lower_line) / ki
self._previous_price = price
return (self._upper_line, self._lower_line)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
upper, lower = self._update_indicator(price)
if self._prev_up is None or self._prev_dn is None:
self._prev_up = upper
self._prev_dn = lower
return
prev_up = self._prev_up
prev_dn = self._prev_dn
# Buy signal: previous upper was below lower, now crossing above
if prev_up <= prev_dn and upper > lower:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# Sell signal: previous upper was above lower, now crossing below
elif prev_up >= prev_dn and upper < lower:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_up = upper
self._prev_dn = lower
def CreateClone(self):
return ang_zad_c_time_mm_recovery_strategy()