Bearish Wick Reversal
Regras de negociação
Reversão do pavio inferior, apenas comprada, em velas concluídas de um minuto. Sem posição, comprar Volume se Close < Open e 100 * (Low - Close) / Close <= Threshold. O padrão -1 exige pavio inferior de pelo menos um por cento do fechamento.
O horário UTC de abertura deve pertencer ao intervalo inclusivo StartTime–EndTime. Com UseEmaFilter ativo, também se exige EMA(EmaPeriod) formada e Close > EMA; desativado, o aquecimento da EMA não atrasa entradas.
Fechar toda a posição comprada executada quando uma vela completa fechar acima da máxima da vela completa anterior. A janela de entrada e a EMA não bloqueiam saídas. Não há vendas descobertas, espera oculta em barras, take-profit ou stop-loss automáticos. Uma ordem ativa evita duplicações.
Parâmetros
| Parâmetro | Padrão |
|---|---|
Threshold |
-1 |
UseEmaFilter |
false |
EmaPeriod |
200 |
CandleType |
1m |
StartTime |
2014-01-01T00:00:00Z |
EndTime |
2099-01-01T00:00:00Z |
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>Long-only bearish lower-wick reversal with independent previous-high exits.</summary>
public class BearishWickReversalStrategy : Strategy
{
private readonly StrategyParam<decimal> _threshold;
private readonly StrategyParam<bool> _useEmaFilter;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DateTime> _startTime;
private readonly StrategyParam<DateTime> _endTime;
private EMA _ema;
private decimal _previousHigh;
private Order _pending;
private DateTime _lastTime;
/// <summary>Negative lower wick percentage threshold.</summary>
public decimal Threshold
{
get => _threshold.Value;
set => _threshold.Value = value;
}
/// <summary>Require close above the formed EMA.</summary>
public bool UseEmaFilter
{
get => _useEmaFilter.Value;
set => _useEmaFilter.Value = value;
}
/// <summary>Optional EMA length.</summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>Finished signal candle interval.</summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>First eligible UTC entry time.</summary>
public DateTime StartTime
{
get => _startTime.Value;
set => _startTime.Value = value;
}
/// <summary>Last eligible UTC entry time.</summary>
public DateTime EndTime
{
get => _endTime.Value;
set => _endTime.Value = value;
}
/// <summary>Initializes the published wick, optional trend and UTC entry-window parameters.</summary>
public BearishWickReversalStrategy()
{
_threshold = Param(nameof(Threshold), -1m).SetRange(-100m, 0m)
.SetDisplay("Threshold", "Negative lower wick percentage threshold", "Parameters");
_useEmaFilter = Param(nameof(UseEmaFilter), false)
.SetDisplay("UseEmaFilter", "Require close above the formed EMA", "Parameters");
_emaPeriod = Param(nameof(EmaPeriod), 200).SetGreaterThanZero()
.SetDisplay("EmaPeriod", "Optional EMA length", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("CandleType", "Finished signal candle interval", "Parameters");
_startTime = Param(nameof(StartTime), new DateTime(2014, 1, 1, 0, 0, 0, DateTimeKind.Utc))
.SetDisplay("StartTime", "First eligible UTC entry time", "Parameters");
_endTime = Param(nameof(EndTime), new DateTime(2099, 1, 1, 0, 0, 0, DateTimeKind.Utc))
.SetDisplay("EndTime", "Last eligible UTC entry time", "Parameters");
OrderRegistering += order => _pending = order;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_previousHigh = 0m;
_pending = null;
_lastTime = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
if (StartTime.Kind != DateTimeKind.Utc || EndTime.Kind != DateTimeKind.Utc || StartTime > EndTime)
throw new InvalidOperationException("StartTime and EndTime must form an ordered UTC entry window.");
base.OnStarted2(time);
var candles = SubscribeCandles(CandleType);
if (UseEmaFilter)
{
_ema = new EMA { Length = EmaPeriod };
candles.Bind(_ema, ProcessFilteredCandle).Start();
}
else candles.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area is not null)
{
DrawCandles(area, candles);
if (_ema is not null) DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessFilteredCandle(ICandleMessage candle, decimal average) => ProcessCandle(candle);
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished || candle.ClosePrice <= 0m || candle.OpenTime <= _lastTime)
return;
var previousHigh = _previousHigh;
_previousHigh = candle.HighPrice;
_lastTime = candle.OpenTime;
if (!IsFormedAndOnlineAndAllowTrading() || _pending is not null && _pending.State is not (OrderStates.Done or OrderStates.Failed))
return;
if (Position > 0m)
{
if (previousHigh > 0m && candle.ClosePrice > previousHigh)
SellMarket(Position);
return;
}
if (Position != 0m || candle.OpenTime < StartTime || candle.OpenTime > EndTime
|| candle.ClosePrice >= candle.OpenPrice || 100m * (candle.LowPrice - candle.ClosePrice) / candle.ClosePrice > Threshold)
return;
if (_ema is not null && (!_ema.IsFormed || candle.ClosePrice <= _ema.GetCurrentValue()))
return;
BuyMarket(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")
import math
from collections import deque
from System import TimeSpan, DateTime, DateTimeKind, Decimal, Nullable
from StockSharp.Messages import DataType, CandleStates, Sides, OrderStates, Unit, UnitTypes
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
from System import Array
from StockSharp.Algo.Indicators import EMA, IIndicator
class bearish_wick_reversal_strategy(Strategy):
def __init__(self):
super(bearish_wick_reversal_strategy, self).__init__()
self._threshold = self.Param("Threshold", -1.0).SetRange(-100.0, 0.0).SetDisplay(
"Threshold", "Negative lower wick percentage threshold", "Parameters")
self._use_ema_filter = self.Param("UseEmaFilter", False).SetDisplay(
"UseEmaFilter", "Require close above the formed EMA", "Parameters")
self._ema_period = self.Param("EmaPeriod", 200).SetGreaterThanZero().SetDisplay(
"EmaPeriod", "Optional EMA length", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay(
"CandleType", "Finished signal candle interval", "Parameters")
self._start_time = self.Param("StartTime", DateTime(2014, 1, 1, 0, 0, 0, DateTimeKind.Utc)).SetDisplay(
"StartTime", "First eligible UTC entry time", "Parameters")
self._end_time = self.Param("EndTime", DateTime(2099, 1, 1, 0, 0, 0, DateTimeKind.Utc)).SetDisplay(
"EndTime", "Last eligible UTC entry time", "Parameters")
self._reset_state()
self.OrderRegistering += self._remember_order
@property
def Threshold(self):
return self._threshold.Value
@Threshold.setter
def Threshold(self, value):
self._threshold.Value = value
@property
def UseEmaFilter(self):
return self._use_ema_filter.Value
@UseEmaFilter.setter
def UseEmaFilter(self, value):
self._use_ema_filter.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StartTime(self):
return self._start_time.Value
@StartTime.setter
def StartTime(self, value):
self._start_time.Value = value
@property
def EndTime(self):
return self._end_time.Value
@EndTime.setter
def EndTime(self, value):
self._end_time.Value = value
def _reset_state(self):
self._ema = None
self._previous_high = Decimal(0)
self._pending = None
self._last_time = DateTime.MinValue
def _remember_order(self, order):
self._pending = order
def OnReseted(self):
super(bearish_wick_reversal_strategy, self).OnReseted()
self._reset_state()
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
if self.StartTime.Kind != DateTimeKind.Utc or self.EndTime.Kind != DateTimeKind.Utc or self.StartTime > self.EndTime:
raise ValueError("StartTime and EndTime must form an ordered UTC entry window.")
super(bearish_wick_reversal_strategy, self).OnStarted2(time)
candles = self.SubscribeCandles(self.CandleType)
if self.UseEmaFilter:
self._ema = EMA()
self._ema.Length = self.EmaPeriod
candles.BindEx(Array[IIndicator]([self._ema]), self._process_filtered).Start()
else:
candles.Bind(self._process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, candles)
if self._ema is not None:
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _process_filtered(self, candle, values):
if values[0].IsFormed:
self._process(candle, values[0].GetValue[Decimal](None))
def _process(self, candle, average=None):
if candle.State != CandleStates.Finished or candle.ClosePrice <= 0 or candle.OpenTime <= self._last_time:
return
previous_high = self._previous_high
self._previous_high = candle.HighPrice
self._last_time = candle.OpenTime
if not self.IsFormedAndOnlineAndAllowTrading() or (self._pending is not None and self._pending.State not in (OrderStates.Done, OrderStates.Failed)):
return
if self.Position > 0:
if previous_high > 0 and candle.ClosePrice > previous_high:
self.SellMarket(self.Position)
return
if self.Position != 0 or candle.OpenTime < self.StartTime or candle.OpenTime > self.EndTime:
return
if candle.ClosePrice >= candle.OpenPrice or 100.0 * (float(candle.LowPrice) - float(candle.ClosePrice)) / float(candle.ClosePrice) > float(self.Threshold):
return
if self._ema is not None and (not self._ema.IsFormed or average is None or candle.ClosePrice <= average):
return
self.BuyMarket(self.Volume)
def CreateClone(self):
return bearish_wick_reversal_strategy()