Стратегия Forex Profit Boost
Обзор
Forex Profit Boost — это контртрендовая торговая система, основанная на пересечении быстрой экспоненциальной скользящей средней (EMA) и медленной простой скользящей средней (SMA). Стратегия ожидает пересечения EMA и SMA и открывает позицию против направления пересечения, рассчитывая на откат цены. Для управления рисками можно задавать уровни стоп-лосса и тейк-профита в абсолютных пунктах цены.
Индикаторы
- EMA (быстрая): период по умолчанию 7.
- SMA (медленная): период по умолчанию 21.
Правила торговли
- Подписка на выбранный таймфрейм свечей.
- Расчёт EMA и SMA на каждой завершенной свече.
- Когда EMA пересекает SMA сверху вниз:
- Закрыть все короткие позиции.
- Открыть новую длинную позицию.
- Когда EMA пересекает SMA снизу вверх:
- Закрыть все длинные позиции.
- Открыть новую короткую позицию.
- При необходимости применяются уровни стоп-лосса и тейк-профита относительно цены входа.
Параметры
| Имя | Описание | Значение по умолчанию |
|---|---|---|
FastPeriod |
Период быстрой EMA | 7 |
SlowPeriod |
Период медленной SMA | 21 |
StopLoss |
Расстояние стоп-лосса в пунктах цены | 1000 |
TakeProfit |
Расстояние тейк-профита в пунктах цены | 2000 |
CandleType |
Таймфрейм свечей | 1 час |
Примечания
- Стратегия использует высокоуровневый API StockSharp и не сохраняет исторические коллекции.
- Сделки выполняются рыночными ордерами только после формирования свечи.
- Все комментарии в исходном коде написаны на английском языке, как требуется.
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>
/// Forex Profit Boost reversal strategy.
/// Opens long when fast EMA crosses below slow SMA.
/// Opens short when fast EMA crosses above slow SMA.
/// Uses optional stop-loss and take-profit in price points.
/// </summary>
public class ForexProfitBoostStrategy : Strategy
{
private static readonly TimeSpan _signalCooldown = TimeSpan.FromHours(18);
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private bool? _wasFastAboveSlow;
private decimal _entryPrice;
private bool _isLongPosition;
private DateTime _lastSignalTime;
/// <summary>
/// Fast EMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop loss distance in price points.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit distance in price points.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// The type of candles used for strategy calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="ForexProfitBoostStrategy"/>.
/// </summary>
public ForexProfitBoostStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Fast EMA Period", "Period of the fast EMA", "Parameters")
.SetOptimize(5, 15, 1);
_slowPeriod = Param(nameof(SlowPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Slow SMA Period", "Period of the slow SMA", "Parameters")
.SetOptimize(10, 40, 5);
_stopLoss = Param(nameof(StopLoss), 1000m)
.SetDisplay("Stop Loss", "Stop loss distance in price points", "Risk Management")
.SetOptimize(500m, 2000m, 100m);
_takeProfit = Param(nameof(TakeProfit), 2000m)
.SetDisplay("Take Profit", "Take profit distance in price points", "Risk Management")
.SetOptimize(1000m, 4000m, 100m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wasFastAboveSlow = null;
_entryPrice = 0m;
_isLongPosition = false;
_lastSignalTime = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fastEma = new ExponentialMovingAverage { Length = FastPeriod };
var slowSma = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowSma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastEma);
DrawIndicator(area, slowSma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var isFastAboveSlow = fastValue > slowValue;
if (_wasFastAboveSlow is null)
{
_wasFastAboveSlow = isFastAboveSlow;
return;
}
var isBullishSignal = _wasFastAboveSlow == true && !isFastAboveSlow;
var isBearishSignal = _wasFastAboveSlow == false && isFastAboveSlow;
if ((isBullishSignal || isBearishSignal) && _lastSignalTime != default && candle.CloseTime < _lastSignalTime + _signalCooldown)
{
_wasFastAboveSlow = isFastAboveSlow;
CheckRisk(candle.ClosePrice);
return;
}
// Detect crossover and trade against the direction (reversal)
if (isBullishSignal)
{
// Fast EMA crossed below slow SMA -> open long
if (Position <= 0)
{
var volume = Position < 0 ? Math.Abs(Position) + Volume : Volume;
BuyMarket(volume);
_entryPrice = candle.ClosePrice;
_isLongPosition = true;
_lastSignalTime = candle.CloseTime;
}
}
else if (isBearishSignal)
{
// Fast EMA crossed above slow SMA -> open short
if (Position >= 0)
{
var volume = Position > 0 ? Position + Volume : Volume;
SellMarket(volume);
_entryPrice = candle.ClosePrice;
_isLongPosition = false;
_lastSignalTime = candle.CloseTime;
}
}
// Update crossover state
_wasFastAboveSlow = isFastAboveSlow;
// Check stop loss and take profit
CheckRisk(candle.ClosePrice);
}
private void CheckRisk(decimal currentPrice)
{
if (Position == 0 || _entryPrice == 0)
return;
if (_isLongPosition)
{
if (_stopLoss.Value > 0m && currentPrice <= _entryPrice - _stopLoss.Value)
{
SellMarket(Position);
_entryPrice = 0m;
return;
}
if (_takeProfit.Value > 0m && currentPrice >= _entryPrice + _takeProfit.Value)
{
SellMarket(Position);
_entryPrice = 0m;
}
}
else
{
if (_stopLoss.Value > 0m && currentPrice >= _entryPrice + _stopLoss.Value)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
return;
}
if (_takeProfit.Value > 0m && currentPrice <= _entryPrice - _takeProfit.Value)
{
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
}
}
}
}
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, DateTime
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class forex_profit_boost_strategy(Strategy):
def __init__(self):
super(forex_profit_boost_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast EMA Period", "Period of the fast EMA", "Parameters")
self._slow_period = self.Param("SlowPeriod", 21) \
.SetDisplay("Slow SMA Period", "Period of the slow SMA", "Parameters")
self._stop_loss = self.Param("StopLoss", 1000.0) \
.SetDisplay("Stop Loss", "Stop loss distance in price points", "Risk Management")
self._take_profit = self.Param("TakeProfit", 2000.0) \
.SetDisplay("Take Profit", "Take profit distance in price points", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._was_fast_above_slow = None
self._entry_price = 0.0
self._is_long_position = False
self._last_signal_time = DateTime.MinValue
self._signal_cooldown = TimeSpan.FromHours(18)
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _check_risk(self, current_price):
if self.Position == 0 or self._entry_price == 0.0:
return
if self._is_long_position:
if self.StopLoss > 0.0 and current_price <= self._entry_price - self.StopLoss:
self.SellMarket(self.Position)
self._entry_price = 0.0
return
if self.TakeProfit > 0.0 and current_price >= self._entry_price + self.TakeProfit:
self.SellMarket(self.Position)
self._entry_price = 0.0
else:
if self.StopLoss > 0.0 and current_price >= self._entry_price + self.StopLoss:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
return
if self.TakeProfit > 0.0 and current_price <= self._entry_price - self.TakeProfit:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
def OnStarted2(self, time):
super(forex_profit_boost_strategy, self).OnStarted2(time)
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastPeriod
slow_sma = SimpleMovingAverage()
slow_sma.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(fast_ema, slow_sma, self.ProcessCandle) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_sma)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
is_fast_above_slow = fast_val > slow_val
if self._was_fast_above_slow is None:
self._was_fast_above_slow = is_fast_above_slow
return
is_bullish_signal = self._was_fast_above_slow and not is_fast_above_slow
is_bearish_signal = not self._was_fast_above_slow and is_fast_above_slow
if ((is_bullish_signal or is_bearish_signal)
and self._last_signal_time != DateTime.MinValue
and candle.CloseTime < self._last_signal_time + self._signal_cooldown):
self._was_fast_above_slow = is_fast_above_slow
self._check_risk(float(candle.ClosePrice))
return
if is_bullish_signal:
if self.Position <= 0:
volume = abs(self.Position) + self.Volume if self.Position < 0 else self.Volume
self.BuyMarket(volume)
self._entry_price = float(candle.ClosePrice)
self._is_long_position = True
self._last_signal_time = candle.CloseTime
elif is_bearish_signal:
if self.Position >= 0:
volume = self.Position + self.Volume if self.Position > 0 else self.Volume
self.SellMarket(volume)
self._entry_price = float(candle.ClosePrice)
self._is_long_position = False
self._last_signal_time = candle.CloseTime
self._was_fast_above_slow = is_fast_above_slow
self._check_risk(float(candle.ClosePrice))
def OnReseted(self):
super(forex_profit_boost_strategy, self).OnReseted()
self._was_fast_above_slow = None
self._entry_price = 0.0
self._is_long_position = False
self._last_signal_time = DateTime.MinValue
def CreateClone(self):
return forex_profit_boost_strategy()