GitHub で見る
CoensioTrader1 V06 戦略
概要
CoensioTrader1 V06 は、元々 MetaTrader の Expert Advisor として配布されたトレンドフォローのブレイクアウト戦略です。StockSharp への移植では、裁量的なパターン認識ロジックを維持しつつ、MQL 実装からブローカーおよびインターネット固有の機能を削除しています。この戦略は単一の銘柄とタイムフレームで動作し、Bollinger Bands とダブル指数移動平均(DEMA)を使用して、トレンド再開に先行する枯渇の動きを特定します。
元のロボットは最大 6 つの通貨ペアを個別のパラメータセットで取引することを許可し、DLL ベースのライセンスをサポートし、最適化結果をリモートサーバーに報告していました。これらの補助サービスはこの移植版では意図的に省略されています。焦点は、スイング構造と DEMA の傾きによって確認された Bollinger Bands の拒絶に反応するコアのエントリーおよびエグジットワークフローです。
戦略ロジック
- データ購読 – 戦略は設定されたローソク足タイプ(デフォルト: 1 時間)を購読し、Bollinger Bands と DEMA をバインドします。
- Bollinger Bands 拒絶 – シグナルは最後に完全に閉じたローソク足で評価されます。
- ロングセットアップ
- ローソク足が前の下部 Bollinger Band より下で開き、再びその上で閉じた(失敗したブレイクダウン)。
- ローソク足が前のバーと比較してより高い安値を作り、その前のバーは前々バーと比較してより低い安値を作った(ダブルボトムスタイルの構造)。
- DEMA が最後の 3 つの観測値にわたって厳密に上昇している(現在値 > 前の値 > 2 つ前の値)。
- ショートセットアップ
- ローソク足が前の上部 Bollinger Band より上で開き、再びその下で閉じた(失敗したブレイクアウト)。
- ローソク足が前のバーと比較してより低い高値を作り、その前のバーは前々バーと比較してより高い高値を作った(ダブルトップ構造)。
- DEMA が最後の 3 つの観測値にわたって厳密に下落している。
- 注文実行 – 完成したローソク足でシグナルが確認された直後に成行注文が送信されます。反対のシグナルでオプションのポジションフラット化を有効にできます。
- リスク管理 – オプションのストップロスとテイクプロフィットの距離は
StartProtection を通じて提供されます。両方とも絶対的な価格オフセットです。元のエキスパートのトレーリングストップ機能は再現されていません。
パラメーター
| 名前 |
説明 |
デフォルト |
BollingerPeriod |
Bollinger Bands 計算の期間。 |
30 |
BollingerDeviation |
バンドの標準偏差乗数。 |
1.5 |
DemaPeriod |
トレンド確認に使用するダブル指数移動平均の長さ。 |
20 |
StopLossDistance |
StartProtection に渡される絶対ストップロスオフセット。無効にするにはゼロに設定します。 |
0 (absolute) |
TakeProfitDistance |
StartProtection に渡される絶対テイクプロフィットオフセット。無効にするにはゼロに設定します。 |
0 (absolute) |
CloseOnSignal |
反対方向に新しいポジションを開く前に現在のポジションを閉じます。 |
false |
CandleType |
ローソク足データタイプまたはタイムフレーム。デフォルトは 1 時間タイムフレームです。 |
1h |
使用上の注意
- StockSharp バージョンはプライマリ
Strategy.Security のみを取引します。元のエキスパートのマルチシンボル動作を模倣するには、異なるパラメータセットで個別の戦略インスタンスを起動してください。
- MQL のロットサイジングロジック(
RiskMax、LotSize、LotBalanceDivider)は移植されていません。ポートフォリオルールに従って、戦略の Volume またはリスクマネージャーを通じて設定してください。
- MQL ファイルに存在する DLL ベースのアクティベーション、リモート最適化ログ、UI 描画ルーチンは意図的に削除されています。
- ストップロスとテイクプロフィットの値は絶対的な価格距離です。戦略を設定する際に、銘柄のティックサイズまたはピップ値に合わせて調整してください。
- 元のトレーリングストップステップメカニズムは実装されていません。トレーリング管理が必要な場合は、この戦略の上に専用のリスクモジュールを重ねてください。
- すべてのコードコメントとロジックは要求通り英語で維持されており、README の翻訳は別途提供されます。
MQL バージョンとの違い
- マルチシンボル管理: 明確さとテストの容易さのために単一銘柄設計に置き換え。
- ネットワークとライセンス: 削除済み。外部 HTTP リクエストや DLL 呼び出しは実行されません。
- 注文サイジング: StockSharp の標準
Volume 処理に依存するよう簡略化。
- ビジュアルオブジェクト: MetaTrader からのチャートアノテーション(矢印、ラベル、カラーテーマ)は再現されません。視覚化が必要な場合は StockSharp チャートヘルパーを使用してください。
- トレーリングストップ: 移植されていません。初期の保護注文のみが設定されます。
このドキュメントは、元の MQL ソースコードを読む必要なしに移植版をレビュー、テスト、拡張できるよう包括的であることを目指しています。
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Port of the CoensioTrader1 V06 MQL strategy.
/// The strategy buys after a lower Bollinger Band rejection paired with a higher low pattern and bullish DEMA trend.
/// It sells after an upper Bollinger Band rejection with a lower high structure and bearish DEMA trend.
/// </summary>
public class CoensioTrader1V06Strategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerDeviation;
private readonly StrategyParam<int> _demaPeriod;
private readonly StrategyParam<Unit> _stopLossDistance;
private readonly StrategyParam<Unit> _takeProfitDistance;
private readonly StrategyParam<bool> _closeOnSignal;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevOpen;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevClose;
private decimal? _prev2High;
private decimal? _prev2Low;
private decimal? _prev3High;
private decimal? _prev3Low;
private decimal? _prevUpperBand;
private decimal? _prevLowerBand;
private decimal? _prevDema;
private decimal? _prev2Dema;
/// <summary>
/// Initializes a new instance of the <see cref="CoensioTrader1V06Strategy"/> class.
/// </summary>
public CoensioTrader1V06Strategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Bollinger Period", "Length of Bollinger Bands", "Indicators")
;
_bollingerDeviation = Param(nameof(BollingerDeviation), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Deviation", "Standard deviation multiplier", "Indicators")
;
_demaPeriod = Param(nameof(DemaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("DEMA Period", "Length of double exponential moving average", "Indicators")
;
_stopLossDistance = Param(nameof(StopLossDistance), new Unit(0m, UnitTypes.Absolute))
.SetDisplay("Stop Loss", "Absolute stop loss offset from entry", "Risk");
_takeProfitDistance = Param(nameof(TakeProfitDistance), new Unit(0m, UnitTypes.Absolute))
.SetDisplay("Take Profit", "Absolute take profit offset from entry", "Risk");
_closeOnSignal = Param(nameof(CloseOnSignal), false)
.SetDisplay("Close On Opposite Signal", "Close current trades when opposite setup appears", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Time frame for signal calculations", "General");
Volume = 0.01m;
}
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BollingerDeviation
{
get => _bollingerDeviation.Value;
set => _bollingerDeviation.Value = value;
}
/// <summary>
/// Double exponential moving average period.
/// </summary>
public int DemaPeriod
{
get => _demaPeriod.Value;
set => _demaPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance from entry price.
/// </summary>
public Unit StopLossDistance
{
get => _stopLossDistance.Value;
set => _stopLossDistance.Value = value;
}
/// <summary>
/// Take-profit distance from entry price.
/// </summary>
public Unit TakeProfitDistance
{
get => _takeProfitDistance.Value;
set => _takeProfitDistance.Value = value;
}
/// <summary>
/// Close current position when an opposite signal appears.
/// </summary>
public bool CloseOnSignal
{
get => _closeOnSignal.Value;
set => _closeOnSignal.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();
_prevOpen = null;
_prevHigh = null;
_prevLow = null;
_prevClose = null;
_prev2High = null;
_prev2Low = null;
_prev3High = null;
_prev3Low = null;
_prevUpperBand = null;
_prevLowerBand = null;
_prevDema = null;
_prev2Dema = null;
}
/// <inheritdoc />
private BollingerBands _bollinger;
private DoubleExponentialMovingAverage _dema;
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands
{
Length = BollingerPeriod,
Width = BollingerDeviation
};
_dema = new DoubleExponentialMovingAverage
{
Length = DemaPeriod
};
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;
var bollingerResult = _bollinger.Process(candle);
var demaResult = _dema.Process(candle);
if (bollingerResult.IsEmpty || demaResult.IsEmpty || !_bollinger.IsFormed || !_dema.IsFormed)
{
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_prevLow = candle.LowPrice;
_prevHigh = candle.HighPrice;
return;
}
var bollingerValue = (BollingerBandsValue)bollingerResult;
var upper = bollingerValue.UpBand ?? 0m;
var lower = bollingerValue.LowBand ?? 0m;
var demaValue = demaResult.GetValue<decimal>();
if (_prevOpen.HasValue && _prevClose.HasValue && _prevLow.HasValue && _prevHigh.HasValue &&
_prevLowerBand.HasValue && _prevUpperBand.HasValue && _prevDema.HasValue)
{
// Long setup: lower band rejection with rising DEMA.
var crossedLowerBand = _prevLow.Value <= _prevLowerBand.Value && _prevClose.Value > _prevLowerBand.Value;
var bullishTrend = demaValue > _prevDema.Value;
if (crossedLowerBand && bullishTrend)
{
if (CloseOnSignal && Position < 0)
{
if (Position > 0) SellMarket(Position);
else if (Position < 0) BuyMarket(-Position);
}
if (Position <= 0)
BuyMarket();
}
// Short setup: upper band rejection with falling DEMA.
var crossedUpperBand = _prevHigh.Value >= _prevUpperBand.Value && _prevClose.Value < _prevUpperBand.Value;
var bearishTrend = demaValue < _prevDema.Value;
if (crossedUpperBand && bearishTrend)
{
if (CloseOnSignal && Position > 0)
{
if (Position > 0) SellMarket(Position);
else if (Position < 0) BuyMarket(-Position);
}
if (Position >= 0)
SellMarket();
}
}
_prev3Low = _prev2Low;
_prev3High = _prev2High;
_prev2Low = _prevLow;
_prev2High = _prevHigh;
_prevLowerBand = lower;
_prevUpperBand = upper;
_prev2Dema = _prevDema;
_prevDema = demaValue;
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_prevLow = candle.LowPrice;
_prevHigh = candle.HighPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Indicators import BollingerBands, DoubleExponentialMovingAverage, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan
class coensio_trader1_v06_strategy(Strategy):
def __init__(self):
super(coensio_trader1_v06_strategy, self).__init__()
self._bollinger_period = self.Param("BollingerPeriod", 30)
self._bollinger_deviation = self.Param("BollingerDeviation", 1.5)
self._dema_period = self.Param("DemaPeriod", 20)
self._close_on_signal = self.Param("CloseOnSignal", False)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_open = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._prev_upper_band = None
self._prev_lower_band = None
self._prev_dema = None
self._bollinger = None
self._dema = None
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(coensio_trader1_v06_strategy, self).OnStarted2(time)
self._bollinger = BollingerBands()
self._bollinger.Length = self._bollinger_period.Value
self._bollinger.Width = self._bollinger_deviation.Value
self._dema = DoubleExponentialMovingAverage()
self._dema.Length = self._dema_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
civ1 = CandleIndicatorValue(self._bollinger, candle)
civ1.IsFinal = True
bb_result = self._bollinger.Process(civ1)
civ2 = CandleIndicatorValue(self._dema, candle)
civ2.IsFinal = True
dema_result = self._dema.Process(civ2)
if bb_result.IsEmpty or dema_result.IsEmpty or not self._bollinger.IsFormed or not self._dema.IsFormed:
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_low = float(candle.LowPrice)
self._prev_high = float(candle.HighPrice)
return
upper = float(bb_result.UpBand) if bb_result.UpBand is not None else 0.0
lower = float(bb_result.LowBand) if bb_result.LowBand is not None else 0.0
dema_value = float(dema_result)
if (self._prev_open is not None and self._prev_close is not None and
self._prev_low is not None and self._prev_high is not None and
self._prev_lower_band is not None and self._prev_upper_band is not None and
self._prev_dema is not None):
crossed_lower = self._prev_low <= self._prev_lower_band and self._prev_close > self._prev_lower_band
bullish_trend = dema_value > self._prev_dema
if crossed_lower and bullish_trend:
if self._close_on_signal.Value and self.Position < 0:
self.BuyMarket(abs(self.Position))
if self.Position <= 0:
self.BuyMarket()
crossed_upper = self._prev_high >= self._prev_upper_band and self._prev_close < self._prev_upper_band
bearish_trend = dema_value < self._prev_dema
if crossed_upper and bearish_trend:
if self._close_on_signal.Value and self.Position > 0:
self.SellMarket(self.Position)
if self.Position >= 0:
self.SellMarket()
self._prev_lower_band = lower
self._prev_upper_band = upper
self._prev_dema = dema_value
self._prev_open = float(candle.OpenPrice)
self._prev_close = float(candle.ClosePrice)
self._prev_low = float(candle.LowPrice)
self._prev_high = float(candle.HighPrice)
def OnReseted(self):
super(coensio_trader1_v06_strategy, self).OnReseted()
self._prev_open = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._prev_upper_band = None
self._prev_lower_band = None
self._prev_dema = None
def CreateClone(self):
return coensio_trader1_v06_strategy()