GitHub で見る
決済注文戦略を送信する
Send Close Order は、Vladimir Hlystov による 2009 MetaTrader 4 エキスパート アドバイザー「SendCloseOrder」の移植版です。元のスクリプトは、Bill Williams フラクタルに基づいて 4 つの手動トレンドラインを描画し、価格がそれらの予測レベルのいずれかに達するたびに成行注文を開始または終了します。 StockSharp バージョンは、完全に自動化されたライン管理を使用して意思決定ロジックを複製し、プラットフォームが提供するあらゆるキャンドル シリーズで動作します。
取引ロジック
- フラクタル検出 – 完成したキャンドルごとに 5 バーのスライディング ウィンドウが更新されます。ウィンドウがいっぱいになると、中央のローソク足が Bill Williams フラクタル条件に対してチェックされます。確認された高値と安値は時系列に保存されます。
- トレンドラインの再構築
- 売りライン は、下向きフラクタルによって分離された最新の 2 つの上向きフラクタルを接続し、レジスタンス スロープを形成します。
- 終値 #1 は、
15 の価格ステップ (15 × Security.PriceStep) だけ上方にシフトされた売りラインであり、長い出口レールとして機能します。
- 買いライン は、上向きフラクタルによって分離された最新の 2 つの下向きフラクタルを接続し、サポート スロープを形成します。
- 終値 #2 は、
15 の価格ステップ分下方にシフトした買いラインで、短い出口レールとして機能します。
- シグナル評価 – 4 つのラインは、完成したローソク足のタイムスタンプに外挿されます。予測価格がローソク足の高値/安値の範囲内にある場合 (2 つの価格ステップの小さな許容誤差を伴う)、対応するアクションがトリガーされます。
- 注文管理
- Close #1 または Close #2 をタッチすると、
ClosePosition() を介してポジション全体が即座にクローズされます。
- 売りラインまたは買いラインをタッチすると、結果の絶対ポジションが
MaxOrders × TradeVolume を超えない限り、出来高 TradeVolume の成行注文が開きます。反対のポジションが存在する場合、注文はまずそれを相殺してから、ヘッジ口座の動作を反映して新しいエントリーを積み上げます。
パラメーター
| 名前 |
デフォルト |
説明 |
EnableSellLine |
true |
予想されるレジスタンスラインに達したときに取引を許可します。 |
EnableBuyLine |
true |
予想されるサポートラインに達したときに取引を許可します。 |
EnableCloseLongLine |
true |
シフトされたレジスタンス ラインでロング ポジションを閉じることができます (クローズ #1)。 |
EnableCloseShortLine |
true |
シフトされたサポートラインでショートポジションをクローズすることを許可します(クローズ#2)。 |
MaxOrders |
1 |
現在の方向にスタックされるエントリの最大数。 |
TradeVolume |
0.1 |
個々の成行注文の量。 |
CandleType |
1h 時間枠 |
フラクタル計算に使用されるキャンドル シリーズ。 |
- StockSharp ポートは、新しいフラクタルが表示されるたびに 4 つのラインを再計算します。 MetaTrader では、ユーザーはトレンドラインを手動で削除して再描画する必要がありました。
- 約定は集計されたネットポジションに基づいて行われます。同時ロングバスケットとショートバスケットは、StockSharp のデフォルトのポートフォリオ モデルではサポートされていません。
- タッチ検出では、ティックからの瞬時の買値/売値の代わりに、価格ステップ許容範囲を備えた完成したローソク足の高値/安値を使用します。
- チャート オブジェクト (近似曲線とラベル) は作成されません。焦点は取引シグナルにあります。
使用上の注意
- このストラテジーは、ローソク足と有効な
PriceStep を提供するあらゆる商品で実行できます。 Security.PriceStep がゼロの場合、コードは 0.0001 に戻ります。
- 元の EA のスタッキング動作をエミュレートするには、
MaxOrders を増やします。四捨五入を避けるため、TradeVolume を商品のロットサイズに合わせてください。
- ライン オフセットは、過去の値の 15 ポイントに固定されています。 MetaTrader 入力が変更された場合は、ソース コードを調整します。
C# 実装のみが提供されます。必要に応じて、Python 翻訳が別途追加されます。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// SendCloseOrder: Fractal high/low breakout with EMA filter and ATR stops.
/// </summary>
public class SendCloseOrderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _fractalHigh;
private decimal _fractalLow;
private decimal _prev2High;
private decimal _prev1High;
private decimal _prev2Low;
private decimal _prev1Low;
private int _barCount;
public SendCloseOrderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 50)
.SetDisplay("EMA Length", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_fractalHigh = 0;
_fractalLow = 0;
_prev2High = 0;
_prev1High = 0;
_prev2Low = 0;
_prev1Low = 0;
_barCount = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
// Detect fractal high: prev1High > prev2High and prev1High > current high
if (_barCount > 3 && _prev1High > _prev2High && _prev1High > high)
_fractalHigh = _prev1High;
// Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if (_barCount > 3 && _prev1Low < _prev2Low && _prev1Low < low)
_fractalLow = _prev1Low;
_prev2High = _prev1High;
_prev1High = high;
_prev2Low = _prev1Low;
_prev1Low = low;
if (_fractalHigh == 0 || _fractalLow == 0 || atrVal <= 0)
return;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > _fractalHigh && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < _fractalLow && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
}
}
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
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class send_close_order_strategy(Strategy):
def __init__(self):
super(send_close_order_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "Trend filter.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(send_close_order_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
self._bar_count += 1
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
# Detect fractal high: prev1High > prev2High and prev1High > current high
if self._bar_count > 3 and self._prev1_high > self._prev2_high and self._prev1_high > high:
self._fractal_high = self._prev1_high
# Detect fractal low: prev1Low < prev2Low and prev1Low < current low
if self._bar_count > 3 and self._prev1_low < self._prev2_low and self._prev1_low < low:
self._fractal_low = self._prev1_low
self._prev2_high = self._prev1_high
self._prev1_high = high
self._prev2_low = self._prev1_low
self._prev1_low = low
if self._fractal_high == 0 or self._fractal_low == 0 or av <= 0:
return
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close > self._fractal_high and close > ev:
self._entry_price = close
self.BuyMarket()
elif close < self._fractal_low and close < ev:
self._entry_price = close
self.SellMarket()
def OnReseted(self):
super(send_close_order_strategy, self).OnReseted()
self._entry_price = 0.0
self._fractal_high = 0.0
self._fractal_low = 0.0
self._prev2_high = 0.0
self._prev1_high = 0.0
self._prev2_low = 0.0
self._prev1_low = 0.0
self._bar_count = 0
def CreateClone(self):
return send_close_order_strategy()