Pinbarリバーサル戦略
Pinbarは価格の突然の拒絶を浮き彫りにし、短期的な転換点を示すことができます。この戦略はロウソクのヒゲの長さをボディとの比で測定し、最近の値動きから突き出た長い影を探します。移動平均フィルターが基礎トレンドの方向で取引する助けをします。
テストでは年平均リターン約82%を示しています。株式市場で最もよく機能します。
各ロウソクの更新で、システムは上下の影を計算してボディサイズと比較します。価格が移動平均より上にある場合、長い下ヒゲを持つ強気Pinbarがロングエントリーをトリガーする可能性があります。同様に、延びた上ヒゲを持つ弱気Pinbarが下降トレンドでショートポジションを開始できます。ストップはエントリーから固定パーセントに置かれます。
オープンポジションに対して反対のPinbarが現れるか、保護的なストップに達するとトレードは閉じられます。Pinbarロジックとトレンドフィルターを組み合わせることで、逆トレンドのセットアップを避けて信頼性が向上します。
詳細
- エントリー条件: 長い尾と小さな反対影を持つPinbar、トレンドで確認。
- ロング/ショート: 両方。
- エグジット条件: 反対のPinbarまたはストップロス。
- ストップ: はい、パーセントベース。
- デフォルト値:
TailToBodyRatio= 2OppositeTailRatio= 0.5MAPeriod= 20CandleType= 15 minuteStopLossPercent= 1
- フィルター:
- カテゴリ: パターン
- 方向: 両方
- インジケーター: Candlestick, MA
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Enters in the SMA-aligned direction of a formed pinbar and exits on the opposite pinbar or stop.
/// </summary>
public class PinbarReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _tailToBodyRatio;
private readonly StrategyParam<decimal> _oppositeTailRatio;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLossPercent;
private Order _pendingOrder;
public decimal TailToBodyRatio { get => _tailToBodyRatio.Value; set => _tailToBodyRatio.Value = value; }
public decimal OppositeTailRatio { get => _oppositeTailRatio.Value; set => _oppositeTailRatio.Value = value; }
public int MAPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public decimal StopLossPercent { get => _stopLossPercent.Value; set => _stopLossPercent.Value = value; }
public PinbarReversalStrategy()
{
_tailToBodyRatio = Param(nameof(TailToBodyRatio), 2m).SetRange(1m, 10m)
.SetDisplay("Tail/Body Ratio", "Minimum dominant shadow relative to the body", "Pattern");
_oppositeTailRatio = Param(nameof(OppositeTailRatio), 0.5m).SetRange(0m, 2m)
.SetDisplay("Opposite Tail Ratio", "Maximum opposite shadow relative to the body", "Pattern");
_maPeriod = Param(nameof(MAPeriod), 20).SetGreaterThanZero()
.SetDisplay("MA Period", "Close SMA trend filter", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Pinbar and MA timeframe", "General");
_stopLossPercent = Param(nameof(StopLossPercent), 1m).SetNotNegative()
.SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection");
OrderRegistering += order => _pendingOrder = order;
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
protected override void OnReseted()
{
base.OnReseted();
_pendingOrder = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pendingOrder = null;
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var sma = new SimpleMovingAverage { Length = MAPeriod };
var candles = SubscribeCandles(CandleType);
candles.Bind(sma, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, candles);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// Native actual-fill protection evaluates executable quotes between signal candles.
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished || !IsFormedAndOnlineAndAllowTrading() ||
_pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed))
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
if (body <= 0m)
return;
var lower = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice;
var upper = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice);
var bullish = lower >= body * TailToBodyRatio && upper <= body * OppositeTailRatio;
var bearish = upper >= body * TailToBodyRatio && lower <= body * OppositeTailRatio;
if (Position > 0m && bearish)
SellMarket(Position);
else if (Position < 0m && bullish)
BuyMarket(Math.Abs(Position));
else if (Position == 0m && bullish && candle.ClosePrice > smaValue)
BuyMarket(Volume);
else if (Position == 0m && bearish && candle.ClosePrice < smaValue)
SellMarket(Volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, OrderStates, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class pinbar_reversal_strategy(Strategy):
"""
Enters in the SMA-aligned direction of a pinbar and exits on the opposite pinbar or stop.
"""
def __init__(self):
super(pinbar_reversal_strategy, self).__init__()
self._tail_to_body_ratio = self.Param("TailToBodyRatio", 2.0).SetRange(1.0, 10.0).SetDisplay("Tail/Body Ratio", "Minimum dominant shadow relative to the body", "Pattern")
self._opposite_tail_ratio = self.Param("OppositeTailRatio", 0.5).SetRange(0.0, 2.0).SetDisplay("Opposite Tail Ratio", "Maximum opposite shadow relative to the body", "Pattern")
self._ma_period = self.Param("MAPeriod", 20).SetGreaterThanZero().SetDisplay("MA Period", "Close SMA trend filter", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Pinbar and MA timeframe", "General")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss (%)", "Actual-fill percent stop; zero disables it.", "Protection")
self._pending_order = None
self.OrderRegistering += self._track_pending
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type), (self.Security, DataType.Level1)]
def _track_pending(self, order):
self._pending_order = order
def OnReseted(self):
super(pinbar_reversal_strategy, self).OnReseted()
self._pending_order = None
def OnStarted2(self, time):
super(pinbar_reversal_strategy, self).OnStarted2(time)
self._pending_order = None
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
candles = self.SubscribeCandles(self.candle_type)
candles.Bind(sma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, candles)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# Native actual-fill protection evaluates executable quotes between signal candles.
pass
def _process_candle(self, candle, sma):
if candle.State != CandleStates.Finished or not self.IsFormedAndOnlineAndAllowTrading():
return
if self._pending_order is not None and self._pending_order.State not in (OrderStates.Done, OrderStates.Failed):
return
body = Math.Abs(candle.ClosePrice - candle.OpenPrice)
if body <= Decimal(0):
return
lower = Math.Min(candle.OpenPrice, candle.ClosePrice) - candle.LowPrice
upper = candle.HighPrice - Math.Max(candle.OpenPrice, candle.ClosePrice)
tail_ratio = Decimal(self._tail_to_body_ratio.Value)
opposite_ratio = Decimal(self._opposite_tail_ratio.Value)
bullish = lower >= body * tail_ratio and upper <= body * opposite_ratio
bearish = upper >= body * tail_ratio and lower <= body * opposite_ratio
if self.Position > 0 and bearish:
self.SellMarket(self.Position)
elif self.Position < 0 and bullish:
self.BuyMarket(Math.Abs(self.Position))
elif self.Position == 0 and bullish and candle.ClosePrice > sma:
self.BuyMarket(self.Volume)
elif self.Position == 0 and bearish and candle.ClosePrice < sma:
self.SellMarket(self.Volume)
def CreateClone(self):
return pinbar_reversal_strategy()