Martin Strategy - No Loss Exit V3
This martingale averaging strategy adds to a long position whenever price drops by a configured percentage from the first entry. Each new order increases the cash amount by a multiplier and adjusts the average price. The position is closed when the candle high reaches the average price plus the take-profit percentage, ensuring exits only in profit.
Details
- Entry Criteria:
- Long:
Flat→ buy forInitial Cash - Add:
Price <= EntryPrice * (1 - PriceStep% * orderCount)&&orderCount < MaxOrders
- Long:
- Long/Short: Long only
- Exit Criteria:
High >= AvgPrice * (1 + TakeProfit%)
- Stops: No
- Default Values:
Initial Cash= 100Max Orders= 20Price Step %= 1.5Take Profit %= 1Increase Factor= 1.05
- Filters:
- Category: Averaging down
- Direction: Long
- Indicators: None
- Stops: No
- Complexity: Low
- Timeframe: Any
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: High
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;
public class MartinNoLossExitV3Strategy : Strategy
{
private readonly StrategyParam<decimal> _priceStepPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _increaseFactor;
private readonly StrategyParam<int> _maxOrders;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private ExponentialMovingAverage _ema;
private decimal _entryPrice;
private decimal _totalCost;
private decimal _totalQty;
private decimal _lastCash;
private int _orderCount;
private bool _inPosition;
public decimal PriceStepPercent { get => _priceStepPercent.Value; set => _priceStepPercent.Value = value; }
public decimal TakeProfitPercent { get => _takeProfitPercent.Value; set => _takeProfitPercent.Value = value; }
public decimal IncreaseFactor { get => _increaseFactor.Value; set => _increaseFactor.Value = value; }
public int MaxOrders { get => _maxOrders.Value; set => _maxOrders.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MartinNoLossExitV3Strategy()
{
_priceStepPercent = Param(nameof(PriceStepPercent), 2.5m);
_takeProfitPercent = Param(nameof(TakeProfitPercent), 2.5m);
_increaseFactor = Param(nameof(IncreaseFactor), 1.10m);
_maxOrders = Param(nameof(MaxOrders), 8);
_emaLength = Param(nameof(EmaLength), 50);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_entryPrice = 0m;
_totalCost = 0m;
_totalQty = 0m;
_lastCash = 0m;
_orderCount = 0;
_inPosition = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0m;
_totalCost = 0m;
_totalQty = 0m;
_lastCash = 0m;
_orderCount = 0;
_inPosition = false;
_ema = new ExponentialMovingAverage { Length = EmaLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed)
return;
var initialCash = 100m;
if (_inPosition)
{
var avgPrice = _totalQty > 0m ? _totalCost / _totalQty : 0m;
var takeProfitPrice = avgPrice * (1 + TakeProfitPercent / 100m);
if (candle.HighPrice >= takeProfitPrice && Position > 0)
{
SellMarket();
_inPosition = false;
_entryPrice = 0m;
_totalCost = 0m;
_totalQty = 0m;
_lastCash = 0m;
_orderCount = 0;
return;
}
var nextEntryPrice = _entryPrice * (1 - PriceStepPercent / 100m * _orderCount);
if (_orderCount < MaxOrders && candle.ClosePrice <= nextEntryPrice)
{
BuyMarket();
var newCash = _lastCash * IncreaseFactor;
_totalCost += newCash;
_totalQty += newCash / candle.ClosePrice;
_lastCash = newCash;
_orderCount++;
}
}
else
{
if (candle.ClosePrice > ema)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_totalCost = initialCash;
_totalQty = initialCash / candle.ClosePrice;
_lastCash = initialCash;
_orderCount = 1;
_inPosition = true;
}
}
}
}
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 martin_no_loss_exit_v3_strategy(Strategy):
def __init__(self):
super(martin_no_loss_exit_v3_strategy, self).__init__()
self._price_step_percent = self.Param("PriceStepPercent", 2.5) \
.SetDisplay("Price Step %", "Step percent for martingale", "General")
self._take_profit_percent = self.Param("TakeProfitPercent", 2.5) \
.SetDisplay("Take Profit %", "Take profit percent", "General")
self._increase_factor = self.Param("IncreaseFactor", 1.10) \
.SetDisplay("Increase Factor", "Martingale increase factor", "General")
self._max_orders = self.Param("MaxOrders", 8) \
.SetDisplay("Max Orders", "Maximum martingale orders", "General")
self._ema_length = self.Param("EmaLength", 50) \
.SetDisplay("EMA Length", "EMA trend filter", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._entry_price = 0.0
self._total_cost = 0.0
self._total_qty = 0.0
self._last_cash = 0.0
self._order_count = 0
self._in_position = False
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(martin_no_loss_exit_v3_strategy, self).OnReseted()
self._entry_price = 0.0
self._total_cost = 0.0
self._total_qty = 0.0
self._last_cash = 0.0
self._order_count = 0
self._in_position = False
def OnStarted2(self, time):
super(martin_no_loss_exit_v3_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._total_cost = 0.0
self._total_qty = 0.0
self._last_cash = 0.0
self._order_count = 0
self._in_position = False
self._ema = ExponentialMovingAverage()
self._ema.Length = self._ema_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self.OnProcess).Start()
def OnProcess(self, candle, ema):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed:
return
ev = float(ema)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
initial_cash = 100.0
if self._in_position:
avg_price = self._total_cost / self._total_qty if self._total_qty > 0.0 else 0.0
tp_pct = float(self._take_profit_percent.Value) / 100.0
take_profit_price = avg_price * (1.0 + tp_pct)
if high >= take_profit_price and self.Position > 0:
self.SellMarket()
self._in_position = False
self._entry_price = 0.0
self._total_cost = 0.0
self._total_qty = 0.0
self._last_cash = 0.0
self._order_count = 0
return
step_pct = float(self._price_step_percent.Value) / 100.0
next_entry_price = self._entry_price * (1.0 - step_pct * self._order_count)
max_orders = self._max_orders.Value
inc_factor = float(self._increase_factor.Value)
if self._order_count < max_orders and close <= next_entry_price:
self.BuyMarket()
new_cash = self._last_cash * inc_factor
self._total_cost += new_cash
if close > 0.0:
self._total_qty += new_cash / close
self._last_cash = new_cash
self._order_count += 1
else:
if close > ev:
self.BuyMarket()
self._entry_price = close
self._total_cost = initial_cash
self._total_qty = initial_cash / close if close > 0.0 else 0.0
self._last_cash = initial_cash
self._order_count = 1
self._in_position = True
def CreateClone(self):
return martin_no_loss_exit_v3_strategy()