GitHub で見る
ACB1戦略
概要
ACB1 戦略 は、MQL/8586/ACB1.MQ4 として配布されている MetaTrader エキスパート アドバイザーの StockSharp ポートです。オリジナルのシステムは EURUSD ペアを取引し、市場に参入する前に日々の強いブレイクアウトを待ちます。この変換は、StockSharp の高レベル プリミティブを使用して同じ決定プロセスを再現します。
- 日次ローソク足 (
SignalCandleType) はブレイクアウトの方向を定義し、ストップとテイクプロフィットのアンカーを提供します。
- H4 ローソク足 (
TrailCandleType) は、TrailFactor を乗じたトレーリング距離を決定します。
- ブレイクアウト条件が満たされると注文は市場で執行され、戦略は 1 つのネット ポジションのみを維持し、MQL コードの
OrdersTotal() チェックを反映します。
- ストップロスとテイクプロフィットは内部で管理されます。この戦略は最良の買値/売値を監視し、仮想保護レベルを突破したときに成行注文でポジションをクローズします。
取引ルール
長いセットアップ
- 以前完成したデイリーキャンドルを使用してください。
Close > (High + Low) / 2 および 現在の売値が以前の高値を上回っている場合は、市場でロングポジションをオープンします。
- ストップロスは前の安値に設定されます(商品価格ステップに丸められます)。
- テイクプロフィットはエントリー価格に
(High − Low) × TakeFactor を加えたものに等しくなります。
簡単なセットアップ
Close < (High + Low) / 2 および 現在の入札価格が以前の安値を下回っている場合は、空売りポジションをオープンします。
- ストップロスは前の高値に設定されます。テイクプロフィットはエントリー価格から
(High − Low) × TakeFactor を差し引きます。
トレーリングストップ
- 最近完成した
TrailCandleType のキャンドル供給品 (High − Low) × TrailFactor。
- ロングポジションの場合、ストップは
Bid − TrailDistance に従いますが、価格はテイクプロフィットからブローカーのストップレベルを差し引いた値を下回ったままです。
- ショートポジションの場合、ストップは
Ask + TrailDistance に従いますが、価格はテイクプロフィットとブローカーのストップレベルを上回ったままです。
リスクガード
- この戦略は、観測されたポートフォリオの最大資本を追跡します。元のアドバイザーとまったく同じように、現在の資産がピークの 50% を下回ると取引が停止します。
- 5 秒間のクールダウン (
CooldownSeconds) により、新しい注文ができなくなったり、更新が頻繁に停止されたりして、MQL からの TimeLocal() スロットルが再現されます。
ポジションサイジングとリスク管理
- 取引あたりの出来高は
Portfolio.CurrentValue × RiskFraction から導出されます。
- 契約ごとの金銭リスクは、停止距離とセキュリティ メタデータ (
PriceStep および StepPrice) から計算されます。
- 結果のサイズは
Security.VolumeStep に調整され、[Security.MinVolume, Security.MaxVolume] にクランプされ、その後、MaxVolume パラメーターによって制限されます (デフォルトは 5 ロット)。
- 正規化された数量がゼロの場合、または停止距離が
MinStopDistancePoints に違反する場合、注文はスキップされます。これは、MetaTrader MODE_STOPLEVEL チェックをエミュレートします。
パラメーター
| パラメータ |
デフォルト |
説明 |
SignalCandleType |
毎日 |
ブレイクアウト検出に使用されるローソク式。 |
TrailCandleType |
4時間 |
トレーリングストップ距離を提供するローソク式。 |
TakeFactor |
0.8 |
テイクプロフィットを計算するために日次レンジに適用される乗数。 |
TrailFactor |
10 |
ストップを更新するときにトレーリングレンジに適用される乗数。 |
RiskFraction |
0.05 |
各取引でリスクがかかるポートフォリオの株式の割合 (5%)。 |
MaxVolume |
5 |
最終注文量のハードキャップ。 |
MinStopDistancePoints |
0 |
価格帯で表される最小停止/テイク距離。それをブローカー MODE_STOPLEVEL に設定します。 |
CooldownSeconds |
5 |
連続する取引アクション間の最小遅延。 |
実装メモ
- この戦略には適切な商品メタデータが必要です:
Security.PriceStep、Security.StepPrice、Security.VolumeStep、Security.MinVolume、および (利用可能な場合) Security.MaxVolume。
- 保護レベルは仮想です。 StockSharp は、ビッド/アスクが計算されたストップロスまたはテイクプロフィットに達したときに、成行注文を通じてポジションをクローズします。
- 株式追跡では
Portfolio.CurrentValue を使用します。コネクタがこのフィールドを提供しない場合、リスク ガードは利用可能になるまで取引を無効のままにします。
- 単一のネット ポジションのみが維持されます。取引がアクティブな間の反対のシグナルは、ポジションが完全にクローズされるまで無視されます。
- Python ポートは含まれていません。このディレクトリには、C# 実装とドキュメントのみが含まれます。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
/// Enters on breakouts above previous candle high / below previous candle low,
/// with trailing stop based on ATR.
/// </summary>
public class Acb1Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _takeFactor;
private readonly StrategyParam<decimal> _trailFactor;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _prevClose;
private decimal _prevMid;
private decimal _entryPrice;
private decimal _stopPrice;
private bool _hasPrev;
public Acb1Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators");
_takeFactor = Param(nameof(TakeFactor), 2m)
.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution");
_trailFactor = Param(nameof(TrailFactor), 1.5m)
.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public decimal TakeFactor
{
get => _takeFactor.Value;
set => _takeFactor.Value = value;
}
public decimal TrailFactor
{
get => _trailFactor.Value;
set => _trailFactor.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = 0;
_prevLow = 0;
_prevClose = 0;
_prevMid = 0;
_entryPrice = 0;
_stopPrice = 0;
_hasPrev = false;
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (atrValue <= 0)
return;
// Manage open position
if (Position != 0)
{
if (Position > 0)
{
// Trailing stop for long
var newStop = candle.ClosePrice - atrValue * TrailFactor;
if (newStop > _stopPrice)
_stopPrice = newStop;
// Check stop hit
if (candle.LowPrice <= _stopPrice)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Check take profit
else if (_entryPrice > 0 && candle.HighPrice >= _entryPrice + atrValue * TakeFactor)
{
SellMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
else
{
// Trailing stop for short
var newStop = candle.ClosePrice + atrValue * TrailFactor;
if (_stopPrice == 0 || newStop < _stopPrice)
_stopPrice = newStop;
// Check stop hit
if (candle.HighPrice >= _stopPrice)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
// Check take profit
else if (_entryPrice > 0 && candle.LowPrice <= _entryPrice - atrValue * TakeFactor)
{
BuyMarket();
_entryPrice = 0;
_stopPrice = 0;
}
}
}
// Entry logic after managing position
if (_hasPrev && Position == 0)
{
if (_prevClose > _prevMid && candle.ClosePrice > _prevHigh)
{
// Bullish breakout
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _prevLow;
}
else if (_prevClose < _prevMid && candle.ClosePrice < _prevLow)
{
// Bearish breakout
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _prevHigh;
}
}
// Store for next candle
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevClose = candle.ClosePrice;
_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
_hasPrev = true;
}
}
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 CandleStates
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
class acb1_strategy(Strategy):
"""
Breakout strategy converted from the "ACB1" MetaTrader expert advisor.
Enters on breakouts above previous candle high / below previous candle low,
with trailing stop based on ATR.
"""
def __init__(self):
super(acb1_strategy, self).__init__()
self._candle_type = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Timeframe for breakout detection.", "General")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "Period for ATR indicator used in trailing.", "Indicators")
self._take_factor = self.Param("TakeFactor", 2.0) \
.SetDisplay("Take Factor", "ATR multiplier for take profit distance.", "Execution")
self._trail_factor = self.Param("TrailFactor", 1.5) \
.SetDisplay("Trail Factor", "ATR multiplier for trailing stop distance.", "Execution")
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
@property
def TakeFactor(self):
return self._take_factor.Value
@TakeFactor.setter
def TakeFactor(self, value):
self._take_factor.Value = value
@property
def TrailFactor(self):
return self._trail_factor.Value
@TrailFactor.setter
def TrailFactor(self, value):
self._trail_factor.Value = value
def OnReseted(self):
super(acb1_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(acb1_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._prev_close = 0.0
self._prev_mid = 0.0
self._entry_price = 0.0
self._stop_price = 0.0
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
if atr_value <= 0:
return
# Manage open position
if self.Position != 0:
if self.Position > 0:
# Trailing stop for long
new_stop = float(candle.ClosePrice) - atr_value * self.TrailFactor
if new_stop > self._stop_price:
self._stop_price = new_stop
# Check stop hit
if float(candle.LowPrice) <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Check take profit
elif self._entry_price > 0 and float(candle.HighPrice) >= self._entry_price + atr_value * self.TakeFactor:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = 0.0
else:
# Trailing stop for short
new_stop = float(candle.ClosePrice) + atr_value * self.TrailFactor
if self._stop_price == 0 or new_stop < self._stop_price:
self._stop_price = new_stop
# Check stop hit
if float(candle.HighPrice) >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Check take profit
elif self._entry_price > 0 and float(candle.LowPrice) <= self._entry_price - atr_value * self.TakeFactor:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = 0.0
# Entry logic after managing position
if self._has_prev and self.Position == 0:
if self._prev_close > self._prev_mid and float(candle.ClosePrice) > self._prev_high:
# Bullish breakout
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._prev_low
elif self._prev_close < self._prev_mid and float(candle.ClosePrice) < self._prev_low:
# Bearish breakout
self.SellMarket()
self._entry_price = float(candle.ClosePrice)
self._stop_price = self._prev_high
# Store for next candle
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_mid = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
self._has_prev = True
def CreateClone(self):
"""!! REQUIRED!! Creates a new instance of the strategy."""
return acb1_strategy()