GitHub で見る
スイングトレーダー戦略
概要
SwingTrader 戦略 は、MetaTrader 4 エキスパート アドバイザー SwingTrader.mq4 の StockSharp 移植です。元の EA は次のものを探します
Bollinger バンドの反転: 価格が外側のバンドから反発し、次のバーが中央線を横切ると、アドバイザーは
位置を決め、マーチンゲール形式の平均グリッドの構築を開始します。翻訳された戦略は、同じ高レベルの動作を再現します。
StockSharp 個のキャンドル、StockSharp.Algo.Indicators の Bollinger 個のバンド、およびフレームワークの順序ヘルパー (BuyMarket、
SellMarket)。取引量を尊重しながら、ボリュームスケーリング、グリッドの幅、清算ルールはMT4コードを反映しています。
Security メタデータによって提供される制限。
取引ロジック
- 設定されたタイムフレーム (
CandleType) をサブスクライブし、BollingerPeriod の長さと
標準偏差乗数 2 を固定しました。
- 完成したキャンドルのみを使用してください。インジケーターのコールバックは、MT4
IsNewCandle() を複製するために部分的に形成されたバーを無視します。
警備員。
- 前のローソク足が上部バンドまたは下部バンドに触れたかどうかを追跡します。ブール値ペア
_upTouch / _downTouch が続きます。
反対側のバンドがタッチされるまで片側のみをアクティブに保つ独自のトグルロジック。
- 開いているバスケットがない場合:
- 最後に完了したバーが、以前に下部バンドに触れた後、中央バンドの上を横切った場合、ロングポジションをオープンします。
- バーがアッパーバンドに触れた後、ミドルバンドの下を横切った場合はショートポジションをオープンします。
最初の注文量は
InitialVolume (為替四捨五入後) に等しく、初期グリッド幅は最新の距離に等しい
上部と下部の Bollinger バンドの間。
- バスケットが存在する場合は、最初のフィルから 1 バンド幅全体の逆方向の動きに注意してください。
- ロングの場合、ローソク足の安値がアンカー価格より少なくとも 1 バンド幅下にある場合は、サイズを乗算した別のスライスを購入します。
新しいレベルごとに
Multiplier ずつ。
- ショートの場合、ローソク足の高値がアンカー価格を 1 バンド幅上回っている場合、同じ値を使用して追加のスライスを売ります。
乗算ロジック。
- 利益または最大許容損失の目標に達するまで、新しい注文を集計し続けます。
資金管理と出口
- ヘルパー
CalculateUnrealizedProfit は、価格差を価格に変換することで MT4 変動損益計算を再現します。
ステップ (Security.PriceStep) とステップ値 (Security.StepPrice)。
- 投下資本プロキシでは元の式
Lots * Price / TickSize * TickValue / 30 が使用されます。ここで、Lots は合計になります。
グリッド ボリュームとティック パラメータは Security から供給されます。
- 変動利益が
TakeProfitFactor * invested capital を超えたら、バスケット全体を閉じます。
- 浮動損失が
10 * TakeProfitFactor * invested capital に達した場合に緊急清算を強制します(浮動損失と同じ比率)
MT4コード)。
- すべての決済は反対方向の成行注文で実行されます。一度フラットになると、グリッドの状態がリセットされ、新しいタッチが必要になります。
別のエントリがトリガーされる前に検出されます。
パラメーター
| 名前 |
種類 |
デフォルト |
説明 |
TakeProfitFactor |
decimal |
0.05 |
利益目標を定義するために投資資本に適用される乗数。 |
Multiplier |
decimal |
1.5 |
追加の平均化注文ごとのボリューム乗数。 |
BollingerPeriod |
int |
20 |
Bollinger バンド インジケーターで使用されるローソク足の数。 |
InitialVolume |
decimal |
1 |
新しいバスケットの最初の取引の基本ボリューム (会場制限に四捨五入)。 |
CandleType |
DataType |
15分の時間枠 |
シグナル生成に使用されるタイムフレーム。 |
オリジナルの EA との違い
- StockSharp はネット ポジションで機能します。この戦略は、MT4 のチケットベースの注文をエミュレートするために、グリッド エントリの明示的なリストを維持します。
取り扱い。
- 代わりに、Exchange ボリューム フィルタ (
Security.MinVolume、Security.VolumeStep、Security.MaxVolume) が自動的に適用されます
CheckVolumeValue を手動で呼び出す方法。
- シグナルは閉じたローソク足で評価されます。 MT4 バージョンのバー内トリガーは、ローソク足の高値と安値を使用して近似されます。
決定を平均化するため。
- 注文は常に市場指示として送信されますが、MT4 では明示的な買値/売値パラメーターを含む
OrderSend が使用されていました。
使用上の注意
- 取引商品の現実的なメタデータを提供します:
PriceStep、StepPrice、MinVolume、VolumeStep、および MaxVolume は必須です
MT4 の動作に一致するように、利益、損失、および出来高の計算用に設定されます。
- 平均グリッドは幾何学的にスケールするため、履歴データで構成をテストし、ブローカーのマージンを考慮してください。
ライブで実行する前の要件。
- グリッド幅は現在の Bollinger バンド幅と同じです。
BollingerPeriod を変更すると、エントリーのタイミングとグリッドの両方に直接影響します
間隔。最適化中に感度を検証します。
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 "SwingTrader" MetaTrader expert.
/// Uses Bollinger Band touches to detect swing direction, then enters on middle-band cross.
/// </summary>
public class SwingTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerWidth;
private BollingerBands _bollinger;
private bool _upTouch;
private bool _downTouch;
private decimal? _prevClose;
private decimal? _prevMiddle;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
public SwingTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signals", "General");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerWidth };
_upTouch = false;
_downTouch = false;
_prevClose = null;
_prevMiddle = null;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bbValue.IsFinal)
return;
if (bbValue is not BollingerBandsValue bbVal)
return;
if (bbVal.UpBand is not decimal upper || bbVal.LowBand is not decimal lower || bbVal.MovingAverage is not decimal middle)
return;
if (!_bollinger.IsFormed)
{
_prevClose = candle.ClosePrice;
_prevMiddle = middle;
return;
}
var close = candle.ClosePrice;
// Track Bollinger touches
if (candle.HighPrice > upper)
{
_upTouch = true;
_downTouch = false;
}
if (candle.LowPrice < lower)
{
_downTouch = true;
_upTouch = false;
}
if (_prevClose is null || _prevMiddle is null)
{
_prevClose = close;
_prevMiddle = middle;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Buy: had a lower band touch, now price crosses above middle
var buySignal = _downTouch && _prevClose.Value < _prevMiddle.Value && close > middle;
// Sell: had an upper band touch, now price crosses below middle
var sellSignal = _upTouch && _prevClose.Value > _prevMiddle.Value && close < middle;
if (buySignal)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
if (Position <= 0)
BuyMarket(volume);
_downTouch = false;
}
else if (sellSignal)
{
if (Position > 0)
SellMarket(Position);
if (Position >= 0)
SellMarket(volume);
_upTouch = false;
}
_prevClose = close;
_prevMiddle = middle;
}
/// <inheritdoc />
protected override void OnReseted()
{
_bollinger = null;
_upTouch = false;
_downTouch = false;
_prevClose = null;
_prevMiddle = 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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class swing_trader_strategy(Strategy):
"""Bollinger Band touch swing: buy on lower touch + middle cross up, sell on upper touch + middle cross down."""
def __init__(self):
super(swing_trader_strategy, self).__init__()
self._bb_period = self.Param("BollingerPeriod", 20).SetGreaterThanZero().SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._bb_width = self.Param("BollingerWidth", 2.0).SetGreaterThanZero().SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe for signals", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(swing_trader_strategy, self).OnReseted()
self._up_touch = False
self._down_touch = False
self._prev_close = 0
self._prev_middle = 0
def OnStarted2(self, time):
super(swing_trader_strategy, self).OnStarted2(time)
self._up_touch = False
self._down_touch = False
self._prev_close = 0
self._prev_middle = 0
self._bb = BollingerBands()
self._bb.Length = self._bb_period.Value
self._bb.Width = self._bb_width.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(self._bb, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, self._bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_val):
if candle.State != CandleStates.Finished:
return
if not self._bb.IsFormed:
return
upper = float(bb_val.UpBand)
lower = float(bb_val.LowBand)
middle = float(bb_val.MovingAverage)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
# Track Bollinger touches
if high > upper:
self._up_touch = True
self._down_touch = False
if low < lower:
self._down_touch = True
self._up_touch = False
if self._prev_close == 0 or self._prev_middle == 0:
self._prev_close = close
self._prev_middle = middle
return
# Buy: had lower band touch, now price crosses above middle
buy_signal = self._down_touch and self._prev_close < self._prev_middle and close > middle
# Sell: had upper band touch, now price crosses below middle
sell_signal = self._up_touch and self._prev_close > self._prev_middle and close < middle
if buy_signal:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._down_touch = False
elif sell_signal:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._up_touch = False
self._prev_close = close
self._prev_middle = middle
def CreateClone(self):
return swing_trader_strategy()