Стратегия VR Steals 2
Конвертация советника MetaTrader 5 «VR---STEALS-2» для платформы StockSharp. Стратегия открывает одну длинную позицию и управляет ею фиксированными уровнями без использования индикаторов.
Как работает
- При запуске происходит покупка через
BuyMarketи запоминается цена входа. - Через
SubscribeCandlesподписывается поток свечей (по умолчанию 1‑минутные). - На каждой завершённой свече:
- При достижении прибыли в размере
Breakevenшагов стоп переносится наBreakevenOffsetшагов выше цены входа. - При достижении цены входа плюс
TakeProfitшагов позиция закрывается. - Если цена опускается до уровня стопа (изначальный
StopLossниже входа либо перенесённый стоп), позиция закрывается.
- При достижении прибыли в размере
- После выхода стратегия новые сделки не открывает.
Параметры
| Имя | Описание | Значение по умолчанию |
|---|---|---|
| TakeProfit | Расстояние до фиксации прибыли в шагах цены. | 50 |
| StopLoss | Начальная дистанция стоп‑лосса в шагах цены. | 50 |
| Breakeven | Прибыль в шагах для переноса стопа в безубыток. | 20 |
| BreakevenOffset | Смещение выше цены входа при переносе стопа. | 9 |
| CandleType | Тип свечей для обработки цен. | Таймфрейм 1 минута |
Используется StartProtection() для включения встроенной защиты позиции.
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>
/// Opens positions based on SMA trend direction, manages with fixed take profit,
/// stop loss and breakeven levels.
/// </summary>
public class VRSteals2Strategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _breakeven;
private readonly StrategyParam<decimal> _breakevenOffset;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _stopPrice;
private bool _breakevenActivated;
private decimal _previousFast;
private decimal _previousSlow;
private bool _maInitialized;
public decimal TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
public decimal StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
public decimal Breakeven { get => _breakeven.Value; set => _breakeven.Value = value; }
public decimal BreakevenOffset { get => _breakevenOffset.Value; set => _breakevenOffset.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VRSteals2Strategy()
{
_takeProfit = Param(nameof(TakeProfit), 50m)
.SetDisplay("Take Profit", "Distance to take profit in steps", "General");
_stopLoss = Param(nameof(StopLoss), 50m)
.SetDisplay("Stop Loss", "Distance to stop loss in steps", "General");
_breakeven = Param(nameof(Breakeven), 20m)
.SetDisplay("Breakeven", "Distance to activate breakeven in steps", "General");
_breakevenOffset = Param(nameof(BreakevenOffset), 9m)
.SetDisplay("Breakeven Offset", "Offset applied when breakeven is triggered", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to process", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_stopPrice = 0m;
_breakevenActivated = false;
_previousFast = 0m;
_previousSlow = 0m;
_maInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0m;
_breakevenActivated = false;
var fastSma = new SimpleMovingAverage { Length = 8 };
var slowSma = new SimpleMovingAverage { Length = 34 };
var sub = SubscribeCandles(CandleType);
sub.Bind(fastSma, slowSma, (candle, fast, slow) =>
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Manage existing long position
if (Position > 0)
{
if (!_breakevenActivated && Breakeven > 0m && price >= _entryPrice + Breakeven * step)
{
_stopPrice = _entryPrice + BreakevenOffset * step;
_breakevenActivated = true;
}
if (TakeProfit > 0m && price >= _entryPrice + TakeProfit * step)
{
SellMarket();
_entryPrice = 0m;
_breakevenActivated = false;
return;
}
var stop = _breakevenActivated
? _stopPrice
: (StopLoss > 0m ? _entryPrice - StopLoss * step : decimal.MinValue);
if (price <= stop)
{
SellMarket();
_entryPrice = 0m;
_breakevenActivated = false;
return;
}
}
// Manage existing short position
else if (Position < 0)
{
if (!_breakevenActivated && Breakeven > 0m && price <= _entryPrice - Breakeven * step)
{
_stopPrice = _entryPrice - BreakevenOffset * step;
_breakevenActivated = true;
}
if (TakeProfit > 0m && price <= _entryPrice - TakeProfit * step)
{
BuyMarket();
_entryPrice = 0m;
_breakevenActivated = false;
return;
}
var stop = _breakevenActivated
? _stopPrice
: (StopLoss > 0m ? _entryPrice + StopLoss * step : decimal.MaxValue);
if (price >= stop)
{
BuyMarket();
_entryPrice = 0m;
_breakevenActivated = false;
return;
}
}
// Entry signals based on SMA cross
if (Position == 0)
{
if (_maInitialized && _previousFast <= _previousSlow && fast > slow)
{
BuyMarket();
_entryPrice = price;
_breakevenActivated = false;
}
else if (_maInitialized && _previousFast >= _previousSlow && fast < slow)
{
SellMarket();
_entryPrice = price;
_breakevenActivated = false;
}
}
_previousFast = fast;
_previousSlow = slow;
_maInitialized = true;
}).Start();
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class vr_steals_2_strategy(Strategy):
"""SMA crossover (8/34) with breakeven/SL/TP management and StartProtection."""
def __init__(self):
super(vr_steals_2_strategy, self).__init__()
self._tp = self.Param("TakeProfit", 50).SetDisplay("Take Profit", "TP in steps", "General")
self._sl = self.Param("StopLoss", 50).SetDisplay("Stop Loss", "SL in steps", "General")
self._breakeven = self.Param("Breakeven", 20).SetDisplay("Breakeven", "Distance to activate breakeven", "General")
self._breakeven_offset = self.Param("BreakevenOffset", 9).SetDisplay("Breakeven Offset", "Offset when breakeven triggered", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(vr_steals_2_strategy, self).OnReseted()
self._entry_price = 0
self._stop_price = 0
self._be_activated = False
self._prev_fast = 0
self._prev_slow = 0
self._ma_init = False
def OnStarted2(self, time):
super(vr_steals_2_strategy, self).OnStarted2(time)
self._entry_price = 0
self._stop_price = 0
self._be_activated = False
self._prev_fast = 0
self._prev_slow = 0
self._ma_init = False
fast_sma = SimpleMovingAverage()
fast_sma.Length = 8
slow_sma = SimpleMovingAverage()
slow_sma.Length = 34
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast_sma, slow_sma, self.OnProcess).Start()
self.StartProtection(
Unit(2000, UnitTypes.Absolute),
Unit(1000, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast_sma)
self.DrawIndicator(area, slow_sma)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
fv = float(fast_val)
sv = float(slow_val)
step = 1.0
tp_val = self._tp.Value
sl_val = self._sl.Value
be_val = self._breakeven.Value
be_off = self._breakeven_offset.Value
# Manage long
if self.Position > 0:
if not self._be_activated and be_val > 0 and price >= self._entry_price + be_val * step:
self._stop_price = self._entry_price + be_off * step
self._be_activated = True
if tp_val > 0 and price >= self._entry_price + tp_val * step:
self.SellMarket()
self._entry_price = 0
self._be_activated = False
self._prev_fast = fv
self._prev_slow = sv
self._ma_init = True
return
if self._be_activated:
stop = self._stop_price
else:
stop = self._entry_price - sl_val * step if sl_val > 0 else -999999
if price <= stop:
self.SellMarket()
self._entry_price = 0
self._be_activated = False
self._prev_fast = fv
self._prev_slow = sv
self._ma_init = True
return
# Manage short
elif self.Position < 0:
if not self._be_activated and be_val > 0 and price <= self._entry_price - be_val * step:
self._stop_price = self._entry_price - be_off * step
self._be_activated = True
if tp_val > 0 and price <= self._entry_price - tp_val * step:
self.BuyMarket()
self._entry_price = 0
self._be_activated = False
self._prev_fast = fv
self._prev_slow = sv
self._ma_init = True
return
if self._be_activated:
stop = self._stop_price
else:
stop = self._entry_price + sl_val * step if sl_val > 0 else 999999
if price >= stop:
self.BuyMarket()
self._entry_price = 0
self._be_activated = False
self._prev_fast = fv
self._prev_slow = sv
self._ma_init = True
return
# Entry on SMA cross
if self.Position == 0:
if self._ma_init and self._prev_fast <= self._prev_slow and fv > sv:
self.BuyMarket()
self._entry_price = price
self._be_activated = False
elif self._ma_init and self._prev_fast >= self._prev_slow and fv < sv:
self.SellMarket()
self._entry_price = price
self._be_activated = False
self._prev_fast = fv
self._prev_slow = sv
self._ma_init = True
def CreateClone(self):
return vr_steals_2_strategy()