The VR Overturn Strategy implements a simple martingale/anti-martingale logic.
It always keeps a single position and, once closed, immediately opens a new one
based on the result of the previous trade.
Strategy Logic
Open the first position according to StartSide with volume StartVolume.
Attach stop-loss and take-profit using point offsets.
When the position closes:
Calculate profit of the last trade.
For Martingale mode:
After a profitable trade, reset the volume to StartVolume and keep the same direction.
After a losing trade, multiply the volume by Multiplier and reverse the direction.
For AntiMartingale mode:
After a profitable trade, multiply the volume by Multiplier and keep the same direction.
After a losing trade, reset the volume to StartVolume and reverse the direction.
Open the next position using the computed direction and volume.
The process repeats indefinitely while the strategy is running.
Parameters
Name
Description
Mode
Trading mode: Martingale or AntiMartingale.
StartSide
Side of the very first trade (Buy or Sell).
TakeProfit
Take-profit value in points from the entry price.
StopLoss
Stop-loss value in points from the entry price.
StartVolume
Initial volume used for the first order.
Multiplier
Multiplier applied to the volume after a profit or loss.
Notes
Protective orders are registered as stop and limit orders.
Only one position exists at any moment.
The strategy does not use any market indicators.
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>
/// Overturn strategy using EMA crossover.
/// </summary>
public class VROverturnStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VROverturnStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast EMA period", "General");
_slowPeriod = Param(nameof(SlowPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow EMA period", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
SubscribeCandles(CandleType)
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev)
{
_prevFast = fastVal;
_prevSlow = slowVal;
_hasPrev = true;
return;
}
var crossUp = _prevFast <= _prevSlow && fastVal > slowVal;
var crossDown = _prevFast >= _prevSlow && fastVal < slowVal;
if (crossUp && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (crossDown && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevFast = fastVal;
_prevSlow = slowVal;
}
}