Levels With Revolve Strategy
This strategy opens trades when the market price crosses a user defined level. A buy order is placed when price rises through the level and a sell order when price falls through it. The system can optionally reverse an existing position if the opposite signal appears. It also supports optional stop loss and take profit distances measured in price units.
The strategy subscribes to candles and reacts only when a candle is fully formed. All calculations are performed on the close price of each finished candle. When reversal mode is enabled the current position is closed and a new position in the opposite direction is opened on the next signal.
Details
- Entry Criteria:
- Long: close price crosses above
LevelPrice. - Short: close price crosses below
LevelPrice.
- Long: close price crosses above
- Long/Short: Both directions.
- Reversal: Optional, controlled by
EnableReversal. - Stops: Optional stop loss and take profit in price units.
- Default Values:
LevelPrice= 100.StopLoss= 0 (disabled).TakeProfit= 0 (disabled).EnableReversal= false.CandleType= 1 minute time frame.
- Filters:
- Category: Breakout
- Direction: Both
- Indicators: None
- Stops: Optional
- Complexity: Simple
- Timeframe: Short-term
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that opens a position when price crosses a moving average level.
/// Reverses the position when price crosses in the opposite direction.
/// </summary>
public class LevelsWithRevolveStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _stopPct;
private readonly StrategyParam<decimal> _takePct;
private decimal _prevPrice;
private decimal _prevMa;
private bool _hasPrev;
private decimal _entryPrice;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public decimal StopPct
{
get => _stopPct.Value;
set => _stopPct.Value = value;
}
public decimal TakePct
{
get => _takePct.Value;
set => _takePct.Value = value;
}
public LevelsWithRevolveStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
_maPeriod = Param(nameof(MaPeriod), 50)
.SetDisplay("MA Period", "Moving average period for the level", "Parameters");
_stopPct = Param(nameof(StopPct), 1.5m)
.SetDisplay("Stop %", "Stop loss percent from entry", "Risk");
_takePct = Param(nameof(TakePct), 3m)
.SetDisplay("Take %", "Take profit percent from entry", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevPrice = 0;
_prevMa = 0;
_hasPrev = false;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevPrice = 0;
_prevMa = 0;
_hasPrev = false;
_entryPrice = 0;
var ma = new ExponentialMovingAverage { Length = MaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
// Check stop/take
if (Position > 0 && _entryPrice > 0)
{
var pnlPct = (price - _entryPrice) / _entryPrice * 100m;
if (pnlPct >= TakePct || pnlPct <= -StopPct)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0 && _entryPrice > 0)
{
var pnlPct = (_entryPrice - price) / _entryPrice * 100m;
if (pnlPct >= TakePct || pnlPct <= -StopPct)
{
BuyMarket();
_entryPrice = 0;
}
}
if (!_hasPrev)
{
_prevPrice = price;
_prevMa = maValue;
_hasPrev = true;
return;
}
// Cross above MA - buy (or reverse short)
if (_prevPrice < _prevMa && price >= maValue)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
{
BuyMarket();
_entryPrice = price;
}
}
// Cross below MA - sell (or reverse long)
else if (_prevPrice > _prevMa && price <= maValue)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
{
SellMarket();
_entryPrice = price;
}
}
_prevPrice = price;
_prevMa = maValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class levels_with_revolve_strategy(Strategy):
def __init__(self):
super(levels_with_revolve_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._ma_period = self.Param("MaPeriod", 50) \
.SetDisplay("MA Period", "Moving average period for the level", "Parameters")
self._stop_pct = self.Param("StopPct", 1.5) \
.SetDisplay("Stop %", "Stop loss percent from entry", "Risk")
self._take_pct = self.Param("TakePct", 3.0) \
.SetDisplay("Take %", "Take profit percent from entry", "Risk")
self._prev_price = 0.0
self._prev_ma = 0.0
self._has_prev = False
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@property
def ma_period(self):
return self._ma_period.Value
@property
def stop_pct(self):
return self._stop_pct.Value
@property
def take_pct(self):
return self._take_pct.Value
def OnReseted(self):
super(levels_with_revolve_strategy, self).OnReseted()
self._prev_price = 0.0
self._prev_ma = 0.0
self._has_prev = False
self._entry_price = 0.0
def OnStarted2(self, time):
super(levels_with_revolve_strategy, self).OnStarted2(time)
self._prev_price = 0.0
self._prev_ma = 0.0
self._has_prev = False
self._entry_price = 0.0
ma = ExponentialMovingAverage()
ma.Length = self.ma_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ma, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def process_candle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
ma_value = float(ma_value)
price = float(candle.ClosePrice)
stop = float(self.stop_pct)
take = float(self.take_pct)
# Check stop/take
if self.Position > 0 and self._entry_price > 0:
pnl_pct = (price - self._entry_price) / self._entry_price * 100.0
if pnl_pct >= take or pnl_pct <= -stop:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0 and self._entry_price > 0:
pnl_pct = (self._entry_price - price) / self._entry_price * 100.0
if pnl_pct >= take or pnl_pct <= -stop:
self.BuyMarket()
self._entry_price = 0.0
if not self._has_prev:
self._prev_price = price
self._prev_ma = ma_value
self._has_prev = True
return
# Cross above MA - buy (or reverse short)
if self._prev_price < self._prev_ma and price >= ma_value:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._entry_price = price
# Cross below MA - sell (or reverse long)
elif self._prev_price > self._prev_ma and price <= ma_value:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._entry_price = price
self._prev_price = price
self._prev_ma = ma_value
def CreateClone(self):
return levels_with_revolve_strategy()