Estrategia Nevalyashka
Esta estrategia implementa un sistema simple de alternancia largo/corto con dimensionamiento de posición martingala.
Lógica de la estrategia
- Al inicio se abre una posición corta.
- Se adjuntan un take profit y un stop loss fijos a la posición.
- Cada vez que la posición se cierra (por stop o por objetivo):
- La siguiente operación se abre en la dirección opuesta.
- Si la operación anterior terminó con pérdida, el volumen de la orden se multiplica por
LotMultiplier. - Si la operación anterior terminó con ganancia, el volumen se restablece al
Volumebase.
- Los pasos 2‑3 se repiten indefinidamente.
Parámetros
Volume– volumen de orden base utilizado para la primera operación y después de operaciones ganadoras.LotMultiplier– multiplicador aplicado al volumen después de una operación perdedora.TakeProfit– distancia del objetivo de ganancia en puntos de precio.StopLoss– distancia del stop loss en puntos de precio.
Notas
- Las órdenes de protección se gestionan a través de
StartProtection. - La estrategia no depende de datos de mercado; solo reacciona a cambios en la posición.
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>
/// Alternating long/short strategy (Nevalyashka / Tumbler).
/// Enters on RSI overbought/oversold, exits on TP/SL, then reverses.
/// </summary>
public class NevalyashkaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<decimal> _oversold;
private decimal _entryPrice;
private decimal _prevRsi;
private bool _hasPrev;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public decimal Overbought
{
get => _overbought.Value;
set => _overbought.Value = value;
}
public decimal Oversold
{
get => _oversold.Value;
set => _oversold.Value = value;
}
public NevalyashkaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI period", "Parameters");
_overbought = Param(nameof(Overbought), 65m)
.SetDisplay("Overbought", "Overbought level", "Parameters");
_oversold = Param(nameof(Oversold), 35m)
.SetDisplay("Oversold", "Oversold level", "Parameters");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevRsi = 50;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevRsi = 50;
_hasPrev = false;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
if (!_hasPrev)
{
_prevRsi = rsiValue;
_hasPrev = true;
return;
}
// Exit on TP/SL
if (Position > 0 && _entryPrice > 0)
{
var pnlPct = (price - _entryPrice) / _entryPrice * 100m;
if (pnlPct >= 2m || pnlPct <= -1m || rsiValue > Overbought)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0 && _entryPrice > 0)
{
var pnlPct = (_entryPrice - price) / _entryPrice * 100m;
if (pnlPct >= 2m || pnlPct <= -1m || rsiValue < Oversold)
{
BuyMarket();
_entryPrice = 0;
}
}
// Entry signals
if (Position == 0)
{
if (_prevRsi <= Oversold && rsiValue > Oversold)
{
BuyMarket();
_entryPrice = price;
}
else if (_prevRsi >= Overbought && rsiValue < Overbought)
{
SellMarket();
_entryPrice = price;
}
}
_prevRsi = rsiValue;
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class nevalyashka_strategy(Strategy):
def __init__(self):
super(nevalyashka_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Parameters")
self._overbought = self.Param("Overbought", 65.0) \
.SetDisplay("Overbought", "Overbought level", "Parameters")
self._oversold = self.Param("Oversold", 35.0) \
.SetDisplay("Oversold", "Oversold level", "Parameters")
self._entry_price = 0.0
self._prev_rsi = 50.0
self._has_prev = False
@property
def candle_type(self):
return self._candle_type.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def overbought(self):
return self._overbought.Value
@property
def oversold(self):
return self._oversold.Value
def OnReseted(self):
super(nevalyashka_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_rsi = 50.0
self._has_prev = False
def OnStarted2(self, time):
super(nevalyashka_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._prev_rsi = 50.0
self._has_prev = False
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, rsi)
self.DrawOwnTrades(area)
def process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_value = float(rsi_value)
price = float(candle.ClosePrice)
if not self._has_prev:
self._prev_rsi = rsi_value
self._has_prev = True
return
ob = float(self.overbought)
os_level = float(self.oversold)
# Exit on TP/SL
if self.Position > 0 and self._entry_price > 0:
pnl_pct = (price - self._entry_price) / self._entry_price * 100.0
if pnl_pct >= 2.0 or pnl_pct <= -1.0 or rsi_value > ob:
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 >= 2.0 or pnl_pct <= -1.0 or rsi_value < os_level:
self.BuyMarket()
self._entry_price = 0.0
# Entry signals
if self.Position == 0:
if self._prev_rsi <= os_level and rsi_value > os_level:
self.BuyMarket()
self._entry_price = price
elif self._prev_rsi >= ob and rsi_value < ob:
self.SellMarket()
self._entry_price = price
self._prev_rsi = rsi_value
def CreateClone(self):
return nevalyashka_strategy()