using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Cash Machine strategy: combines DeMarker and Stochastic oscillator crossovers
/// with staged profit protection using ATR-based distances.
/// </summary>
public class CashMachine5minStrategy : Strategy
{
private readonly StrategyParam<decimal> _tpAtrMult;
private readonly StrategyParam<decimal> _slAtrMult;
private readonly StrategyParam<decimal> _trailAtrMult;
private readonly StrategyParam<int> _deMarkerLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevDeMarker;
private decimal? _prevRsi;
private decimal _entryPrice;
private decimal? _stopPrice;
public decimal TpAtrMult { get => _tpAtrMult.Value; set => _tpAtrMult.Value = value; }
public decimal SlAtrMult { get => _slAtrMult.Value; set => _slAtrMult.Value = value; }
public decimal TrailAtrMult { get => _trailAtrMult.Value; set => _trailAtrMult.Value = value; }
public int DeMarkerLength { get => _deMarkerLength.Value; set => _deMarkerLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public CashMachine5minStrategy()
{
_tpAtrMult = Param(nameof(TpAtrMult), 2.5m)
.SetGreaterThanZero()
.SetDisplay("TP ATR Mult", "Take profit ATR multiplier", "Risk");
_slAtrMult = Param(nameof(SlAtrMult), 1.5m)
.SetGreaterThanZero()
.SetDisplay("SL ATR Mult", "Stop loss ATR multiplier", "Risk");
_trailAtrMult = Param(nameof(TrailAtrMult), 1.0m)
.SetGreaterThanZero()
.SetDisplay("Trail ATR Mult", "Trailing stop ATR multiplier", "Risk");
_deMarkerLength = Param(nameof(DeMarkerLength), 14)
.SetGreaterThanZero()
.SetDisplay("DeMarker Length", "DeMarker period", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR calculation period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevDeMarker = null;
_prevRsi = null;
_entryPrice = 0;
_stopPrice = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var deMarker = new DeMarker { Length = DeMarkerLength };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(deMarker, rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, deMarker);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal deMarker, decimal rsi, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
// Manage position
if (Position > 0)
{
// Trailing stop
var trail = close - TrailAtrMult * atr;
if (_stopPrice == null || trail > _stopPrice)
_stopPrice = trail;
// Take profit
var tp = _entryPrice + TpAtrMult * atr;
if (close <= _stopPrice || close >= tp)
{
SellMarket(Math.Abs(Position));
_stopPrice = null;
_entryPrice = 0;
}
}
else if (Position < 0)
{
var trail = close + TrailAtrMult * atr;
if (_stopPrice == null || trail < _stopPrice)
_stopPrice = trail;
var tp = _entryPrice - TpAtrMult * atr;
if (close >= _stopPrice || close <= tp)
{
BuyMarket(Math.Abs(Position));
_stopPrice = null;
_entryPrice = 0;
}
}
// Entry signals
if (Position == 0 && _prevDeMarker is decimal prevDe && _prevRsi is decimal prevRsi)
{
var longSignal = (prevDe < 0.25m && deMarker >= 0.25m) || (prevRsi < 25m && rsi >= 25m);
var shortSignal = (prevDe > 0.75m && deMarker <= 0.75m) || (prevRsi > 75m && rsi <= 75m);
if (longSignal)
{
BuyMarket();
_entryPrice = close;
_stopPrice = close - SlAtrMult * atr;
}
else if (shortSignal)
{
SellMarket();
_entryPrice = close;
_stopPrice = close + SlAtrMult * atr;
}
}
_prevDeMarker = deMarker;
_prevRsi = rsi;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import DeMarker, RelativeStrengthIndex, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class cash_machine5min_strategy(Strategy):
def __init__(self):
super(cash_machine5min_strategy, self).__init__()
self._tp_atr_mult = self.Param("TpAtrMult", 2.5)
self._sl_atr_mult = self.Param("SlAtrMult", 1.5)
self._trail_atr_mult = self.Param("TrailAtrMult", 1.0)
self._de_marker_length = self.Param("DeMarkerLength", 14)
self._rsi_length = self.Param("RsiLength", 14)
self._atr_length = self.Param("AtrLength", 14)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_de_marker = None
self._prev_rsi = None
self._entry_price = 0.0
self._stop_price = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(cash_machine5min_strategy, self).OnStarted2(time)
self._prev_de_marker = None
self._prev_rsi = None
self._entry_price = 0.0
self._stop_price = None
de_marker = DeMarker()
de_marker.Length = self._de_marker_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(de_marker, rsi, atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, de_marker_value, rsi_value, atr_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
de_marker = float(de_marker_value)
rsi = float(rsi_value)
atr = float(atr_value)
close = float(candle.ClosePrice)
tp_mult = float(self._tp_atr_mult.Value)
sl_mult = float(self._sl_atr_mult.Value)
trail_mult = float(self._trail_atr_mult.Value)
pos = float(self.Position)
if pos > 0:
trail = close - trail_mult * atr
if self._stop_price is None or trail > self._stop_price:
self._stop_price = trail
tp = self._entry_price + tp_mult * atr
if close <= self._stop_price or close >= tp:
self.SellMarket(abs(pos))
self._stop_price = None
self._entry_price = 0.0
elif pos < 0:
trail = close + trail_mult * atr
if self._stop_price is None or trail < self._stop_price:
self._stop_price = trail
tp = self._entry_price - tp_mult * atr
if close >= self._stop_price or close <= tp:
self.BuyMarket(abs(pos))
self._stop_price = None
self._entry_price = 0.0
if self.Position == 0 and self._prev_de_marker is not None and self._prev_rsi is not None:
long_signal = (self._prev_de_marker < 0.25 and de_marker >= 0.25) or (self._prev_rsi < 25.0 and rsi >= 25.0)
short_signal = (self._prev_de_marker > 0.75 and de_marker <= 0.75) or (self._prev_rsi > 75.0 and rsi <= 75.0)
if long_signal:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - sl_mult * atr
elif short_signal:
self.SellMarket()
self._entry_price = close
self._stop_price = close + sl_mult * atr
self._prev_de_marker = de_marker
self._prev_rsi = rsi
def OnReseted(self):
super(cash_machine5min_strategy, self).OnReseted()
self._prev_de_marker = None
self._prev_rsi = None
self._entry_price = 0.0
self._stop_price = None
def CreateClone(self):
return cash_machine5min_strategy()