Стратегия GreenTrade
Обзор
GreenTrade — это конвертация оригинального советника MQL5. Стратегия ищет среднесрочные тренды, совмещая фильтр наклона сглаженной скользящей средней (SMMA) и подтверждение импульса через индекс относительной силы (RSI). Сигналы рассчитываются только на закрытых свечах выбранного таймфрейма. Стратегия умеет наращивать позицию ступенями до заданного лимита и использует фиксированные уровни риска вместе со ступенчатым трейлинг-стопом.
Логика торговли
- Подготовка индикаторов
- SMMA вычисляется по медианной цене
((High + Low) / 2)с периодомMaPeriod. - RSI строится по цене закрытия с окном
RsiPeriod.
- SMMA вычисляется по медианной цене
- Фильтр формы тренда
- Анализируются четыре значения SMMA в прошлом, определяемые параметрами сдвигов (
ShiftBar,ShiftBar1,ShiftBar2,ShiftBar3). - Для восходящего тренда требуется выполнение цепочки
SMMA(shift0) > SMMA(shift1) > SMMA(shift2) > SMMA(shift3). - Для нисходящего тренда — обратное неравенство.
- Анализируются четыре значения SMMA в прошлом, определяемые параметрами сдвигов (
- Подтверждение импульса
- Для покупок RSI должен быть выше
RsiBuyLevel, для продаж — нижеRsiSellLevel. Значение RSI берётся наShiftBarсвечей назад, повторяя логику MQL5, где текущая формирующаяся свеча игнорируется.
- Для покупок RSI должен быть выше
- Исполнение сделок
- При наличии сигнала и свободного лимита стратегия отправляет рыночную заявку объёмом
TradeVolume. - Если открыта позиция противоположного направления, сначала происходит её закрытие и только затем разворот.
- Если открыта позиция в ту же сторону, к совокупному объёму добавляется очередной блок до достижения лимита
MaxPositions * TradeVolume.
- При наличии сигнала и свободного лимита стратегия отправляет рыночную заявку объёмом
Управление рисками
- Начальный стоп и тейк. Для каждой сделки рассчитываются уровни по
StopLossPipsиTakeProfitPips. Пункты переводятся в цену черезPriceStepинструмента. Для инструментов с дробным шагом (5 знаков после запятой и т.п.) применяется дополнительный множитель 10 — как и в исходном эксперте. - Трейлинг-стоп. Когда плавающая прибыль превышает сумму
TrailingStopPips + TrailingStepPips, стоп подтягивается, сохраняя дистанциюTrailingStopPips. Следующие смещения требуют очередного прироста наTrailingStepPips, что полностью воспроизводит ступенчатую механику оригинала. - Ограничение позиций. Параметр
MaxPositionsзадаёт максимальное число объёмных блоков. Сигналы, ведущие к превышению лимита, игнорируются.
Параметры
| Параметр | Описание | Значение по умолчанию |
|---|---|---|
MaPeriod |
Период сглаженной скользящей средней по медианной цене. | 67 |
ShiftBar, ShiftBar1, ShiftBar2, ShiftBar3 |
Сдвиги (в барах) для выборки значений SMMA при проверке формы тренда. | 1, 1, 2, 3 |
RsiPeriod |
Окно расчёта RSI. | 57 |
RsiBuyLevel |
Порог RSI для подтверждения покупок. | 60 |
RsiSellLevel |
Порог RSI для подтверждения продаж. | 36 |
TradeVolume |
Объём одной сделки или донаращивания. | 0.1 |
StopLossPips |
Размер первоначального стоп-лосса в пунктах (0 отключает). | 300 |
TakeProfitPips |
Размер первоначального тейк-профита в пунктах (0 отключает). | 300 |
TrailingStopPips |
Дистанция трейлинг-стопа от цены после активации (0 отключает). | 12 |
TrailingStepPips |
Минимальный дополнительный ход цены перед очередным смещением трейлинг-стопа. | 5 |
MaxPositions |
Максимальное число объёмных блоков (TradeVolume), которые могут быть одновременно открыты. |
7 |
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>
/// GreenTrade strategy converted from the MQL implementation.
/// Combines a smoothed moving average slope filter with RSI momentum confirmation.
/// </summary>
public class GreenTradeStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _shiftBar;
private readonly StrategyParam<int> _shiftBar1;
private readonly StrategyParam<int> _shiftBar2;
private readonly StrategyParam<int> _shiftBar3;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _rsiBuyLevel;
private readonly StrategyParam<decimal> _rsiSellLevel;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _trailingStopPips;
private readonly StrategyParam<decimal> _trailingStepPips;
private readonly StrategyParam<int> _maxPositions;
private readonly StrategyParam<DataType> _candleType;
private SmoothedMovingAverage _smma;
private RelativeStrengthIndex _rsi;
private readonly List<decimal> _maHistory = new();
private readonly List<decimal> _rsiHistory = new();
private decimal _pipSize = 1m;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// Period for the smoothed moving average.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Shift (in bars) used for the most recent MA/RSI sample.
/// </summary>
public int ShiftBar
{
get => _shiftBar.Value;
set => _shiftBar.Value = value;
}
/// <summary>
/// Additional shift between the first and second MA comparison.
/// </summary>
public int ShiftBar1
{
get => _shiftBar1.Value;
set => _shiftBar1.Value = value;
}
/// <summary>
/// Additional shift between the second and third MA comparison.
/// </summary>
public int ShiftBar2
{
get => _shiftBar2.Value;
set => _shiftBar2.Value = value;
}
/// <summary>
/// Additional shift between the third and fourth MA comparison.
/// </summary>
public int ShiftBar3
{
get => _shiftBar3.Value;
set => _shiftBar3.Value = value;
}
/// <summary>
/// RSI lookback period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// RSI threshold to confirm bullish entries.
/// </summary>
public decimal RsiBuyLevel
{
get => _rsiBuyLevel.Value;
set => _rsiBuyLevel.Value = value;
}
/// <summary>
/// RSI threshold to confirm bearish entries.
/// </summary>
public decimal RsiSellLevel
{
get => _rsiSellLevel.Value;
set => _rsiSellLevel.Value = value;
}
/// <summary>
/// Trade volume for each new position add-on.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Stop-loss distance in pips.
/// </summary>
public decimal StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance in pips.
/// </summary>
public decimal TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance in pips.
/// </summary>
public decimal TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum price improvement (in pips) before trailing stop is moved again.
/// </summary>
public decimal TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Maximum number of position units (in TradeVolume steps) allowed.
/// </summary>
public int MaxPositions
{
get => _maxPositions.Value;
set => _maxPositions.Value = value;
}
/// <summary>
/// Candle type used for backtesting/live trading.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public GreenTradeStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 67)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Length of the smoothed moving average", "Indicators");
_shiftBar = Param(nameof(ShiftBar), 1)
.SetGreaterThanZero()
.SetDisplay("Shift #0", "Index of the most recent evaluated bar", "Signals");
_shiftBar1 = Param(nameof(ShiftBar1), 1)
.SetGreaterThanZero()
.SetDisplay("Shift #1", "Offset from bar #0 to bar #1", "Signals");
_shiftBar2 = Param(nameof(ShiftBar2), 2)
.SetGreaterThanZero()
.SetDisplay("Shift #2", "Offset from bar #1 to bar #2", "Signals");
_shiftBar3 = Param(nameof(ShiftBar3), 3)
.SetGreaterThanZero()
.SetDisplay("Shift #3", "Offset from bar #2 to bar #3", "Signals");
_rsiPeriod = Param(nameof(RsiPeriod), 57)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "Length of the RSI indicator", "Indicators");
_rsiBuyLevel = Param(nameof(RsiBuyLevel), 60m)
.SetDisplay("RSI Buy Level", "RSI threshold for bullish entries", "Signals");
_rsiSellLevel = Param(nameof(RsiSellLevel), 36m)
.SetDisplay("RSI Sell Level", "RSI threshold for bearish entries", "Signals");
_tradeVolume = Param(nameof(TradeVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Volume used for each new order", "Risk");
_stopLossPips = Param(nameof(StopLossPips), 300m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Initial stop-loss distance in pips", "Risk");
_takeProfitPips = Param(nameof(TakeProfitPips), 300m)
.SetNotNegative()
.SetDisplay("Take Profit", "Initial take-profit distance in pips", "Risk");
_trailingStopPips = Param(nameof(TrailingStopPips), 12m)
.SetNotNegative()
.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk");
_trailingStepPips = Param(nameof(TrailingStepPips), 5m)
.SetNotNegative()
.SetDisplay("Trailing Step", "Required progress before trailing adjusts", "Risk");
_maxPositions = Param(nameof(MaxPositions), 7)
.SetGreaterThanZero()
.SetDisplay("Max Positions", "Maximum number of volume units allowed", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle subscription", "Data");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_smma = null;
_rsi = null;
_maHistory.Clear();
_rsiHistory.Clear();
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_smma = new SmoothedMovingAverage { Length = MaPeriod };
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_pipSize = CalculatePipSize();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _smma);
DrawIndicator(area, _rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_smma == null || _rsi == null)
return;
var medianPrice = (candle.HighPrice + candle.LowPrice) / 2m;
var maResult = _smma.Process(new DecimalIndicatorValue(_smma, medianPrice, candle.OpenTime) { IsFinal = true });
var rsiResult = _rsi.Process(new DecimalIndicatorValue(_rsi, candle.ClosePrice, candle.OpenTime) { IsFinal = true });
if (!_smma.IsFormed || !_rsi.IsFormed)
{
_maHistory.Add(0m);
_rsiHistory.Add(0m);
TrimHistory();
return;
}
var maValue = maResult.ToDecimal();
var rsiValue = rsiResult.ToDecimal();
_maHistory.Add(maValue);
_rsiHistory.Add(rsiValue);
TrimHistory();
// Indicators checked above.
var shift0 = ShiftBar;
var shift1 = shift0 + ShiftBar1;
var shift2 = shift1 + ShiftBar2;
var shift3 = shift2 + ShiftBar3;
var ma0 = GetHistoryValue(_maHistory, shift0);
var ma1 = GetHistoryValue(_maHistory, shift1);
var ma2 = GetHistoryValue(_maHistory, shift2);
var ma3 = GetHistoryValue(_maHistory, shift3);
var rsiSample = GetHistoryValue(_rsiHistory, ShiftBar);
if (ma0 is null || ma1 is null || ma2 is null || ma3 is null || rsiSample is null)
return;
var buySignal = ma0 > ma1 && ma1 > ma2 && ma2 > ma3 && rsiSample > RsiBuyLevel;
var sellSignal = ma0 < ma1 && ma1 < ma2 && ma2 < ma3 && rsiSample < RsiSellLevel;
if (buySignal && CanIncreasePosition(true))
OpenPosition(true, candle);
else if (sellSignal && CanIncreasePosition(false))
OpenPosition(false, candle);
UpdateTrailing(candle);
ManageExits(candle);
}
private void OpenPosition(bool isLong, ICandleMessage candle)
{
var additionalVolume = TradeVolume;
var currentPosition = Position;
if (isLong && currentPosition < 0)
additionalVolume += Math.Abs(currentPosition);
else if (!isLong && currentPosition > 0)
additionalVolume += currentPosition;
if (additionalVolume <= 0)
return;
if (isLong)
BuyMarket();
else
SellMarket();
if (isLong)
{
if (currentPosition > 0)
{
var total = currentPosition + TradeVolume;
_entryPrice = total > 0 ? ((currentPosition * _entryPrice) + (TradeVolume * candle.ClosePrice)) / total : candle.ClosePrice;
}
else
{
_entryPrice = candle.ClosePrice;
}
}
else
{
if (currentPosition < 0)
{
var total = Math.Abs(currentPosition) + TradeVolume;
_entryPrice = total > 0 ? ((Math.Abs(currentPosition) * _entryPrice) + (TradeVolume * candle.ClosePrice)) / total : candle.ClosePrice;
}
else
{
_entryPrice = candle.ClosePrice;
}
}
var stopDistance = StopLossPips * _pipSize;
var takeDistance = TakeProfitPips * _pipSize;
_stopPrice = stopDistance > 0 ? (isLong ? _entryPrice - stopDistance : _entryPrice + stopDistance) : null;
_takePrice = takeDistance > 0 ? (isLong ? _entryPrice + takeDistance : _entryPrice - takeDistance) : null;
}
private void UpdateTrailing(ICandleMessage candle)
{
if (TrailingStopPips <= 0)
return;
var trailingDistance = TrailingStopPips * _pipSize;
var stepDistance = TrailingStepPips * _pipSize;
if (Position > 0 && _entryPrice > 0)
{
var profit = candle.ClosePrice - _entryPrice;
if (profit > trailingDistance + stepDistance)
{
var threshold = candle.ClosePrice - (trailingDistance + stepDistance);
if (!_stopPrice.HasValue || _stopPrice.Value < threshold)
_stopPrice = candle.ClosePrice - trailingDistance;
}
}
else if (Position < 0 && _entryPrice > 0)
{
var profit = _entryPrice - candle.ClosePrice;
if (profit > trailingDistance + stepDistance)
{
var threshold = candle.ClosePrice + trailingDistance + stepDistance;
if (!_stopPrice.HasValue || _stopPrice.Value > threshold)
_stopPrice = candle.ClosePrice + trailingDistance;
}
}
}
private void ManageExits(ICandleMessage candle)
{
if (Position > 0)
{
if (_takePrice.HasValue && candle.HighPrice >= _takePrice.Value)
{
SellMarket();
ResetPositionState();
return;
}
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice.Value)
{
SellMarket();
ResetPositionState();
return;
}
}
else if (Position < 0)
{
if (_takePrice.HasValue && candle.LowPrice <= _takePrice.Value)
{
BuyMarket();
ResetPositionState();
return;
}
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice.Value)
{
BuyMarket();
ResetPositionState();
return;
}
}
else
{
ResetPositionState();
}
}
private bool CanIncreasePosition(bool isLong)
{
if (TradeVolume <= 0)
return false;
if (MaxPositions <= 0)
return true;
var maxVolume = MaxPositions * TradeVolume;
var absolutePosition = Math.Abs(Position);
if (isLong && Position < 0)
return true;
if (!isLong && Position > 0)
return true;
var tolerance = Security?.VolumeStep ?? 0.0000001m;
return absolutePosition + TradeVolume <= maxVolume + tolerance;
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 1m;
if (step < 0.01m)
step *= 10m;
return step;
}
private static decimal? GetHistoryValue(List<decimal> values, int shift)
{
if (shift <= 0)
return null;
var index = values.Count - shift;
if (index < 0)
return null;
return values[index];
}
private void TrimHistory()
{
var maxShift = ShiftBar + ShiftBar1 + ShiftBar2 + ShiftBar3;
var maxCount = Math.Max(maxShift + 5, 10);
if (_maHistory.Count > maxCount)
_maHistory.RemoveRange(0, _maHistory.Count - maxCount);
if (_rsiHistory.Count > maxCount)
_rsiHistory.RemoveRange(0, _rsiHistory.Count - maxCount);
}
private void ResetPositionState()
{
if (Math.Abs(Position) < (Security?.VolumeStep ?? 0.0000001m))
{
_entryPrice = 0m;
_stopPrice = null;
_takePrice = null;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import (
SmoothedMovingAverage,
RelativeStrengthIndex,
)
from indicator_extensions import *
class green_trade_strategy(Strategy):
"""GreenTrade: smoothed MA slope filter with RSI momentum confirmation."""
def __init__(self):
super(green_trade_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 67) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Length of the smoothed moving average", "Indicators")
self._shift_bar = self.Param("ShiftBar", 1) \
.SetGreaterThanZero() \
.SetDisplay("Shift #0", "Index of the most recent evaluated bar", "Signals")
self._shift_bar1 = self.Param("ShiftBar1", 1) \
.SetGreaterThanZero() \
.SetDisplay("Shift #1", "Offset from bar #0 to bar #1", "Signals")
self._shift_bar2 = self.Param("ShiftBar2", 2) \
.SetGreaterThanZero() \
.SetDisplay("Shift #2", "Offset from bar #1 to bar #2", "Signals")
self._shift_bar3 = self.Param("ShiftBar3", 3) \
.SetGreaterThanZero() \
.SetDisplay("Shift #3", "Offset from bar #2 to bar #3", "Signals")
self._rsi_period = self.Param("RsiPeriod", 57) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "Length of the RSI indicator", "Indicators")
self._rsi_buy_level = self.Param("RsiBuyLevel", 60.0) \
.SetDisplay("RSI Buy Level", "RSI threshold for bullish entries", "Signals")
self._rsi_sell_level = self.Param("RsiSellLevel", 36.0) \
.SetDisplay("RSI Sell Level", "RSI threshold for bearish entries", "Signals")
self._trade_volume = self.Param("TradeVolume", 0.1) \
.SetGreaterThanZero() \
.SetDisplay("Trade Volume", "Volume used for each new order", "Risk")
self._stop_loss_pips = self.Param("StopLossPips", 300.0) \
.SetDisplay("Stop Loss", "Initial stop-loss distance in pips", "Risk")
self._take_profit_pips = self.Param("TakeProfitPips", 300.0) \
.SetDisplay("Take Profit", "Initial take-profit distance in pips", "Risk")
self._trailing_stop_pips = self.Param("TrailingStopPips", 12.0) \
.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Risk")
self._trailing_step_pips = self.Param("TrailingStepPips", 5.0) \
.SetDisplay("Trailing Step", "Required progress before trailing adjusts", "Risk")
self._max_positions = self.Param("MaxPositions", 7) \
.SetGreaterThanZero() \
.SetDisplay("Max Positions", "Maximum number of volume units allowed", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle subscription", "Data")
self._ma_history = []
self._rsi_history = []
self._pip_size = 1.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
@property
def MaPeriod(self):
return int(self._ma_period.Value)
@property
def ShiftBar(self):
return int(self._shift_bar.Value)
@property
def ShiftBar1(self):
return int(self._shift_bar1.Value)
@property
def ShiftBar2(self):
return int(self._shift_bar2.Value)
@property
def ShiftBar3(self):
return int(self._shift_bar3.Value)
@property
def RsiPeriod(self):
return int(self._rsi_period.Value)
@property
def RsiBuyLevel(self):
return float(self._rsi_buy_level.Value)
@property
def RsiSellLevel(self):
return float(self._rsi_sell_level.Value)
@property
def TradeVolume(self):
return float(self._trade_volume.Value)
@property
def StopLossPips(self):
return float(self._stop_loss_pips.Value)
@property
def TakeProfitPips(self):
return float(self._take_profit_pips.Value)
@property
def TrailingStopPips(self):
return float(self._trailing_stop_pips.Value)
@property
def TrailingStepPips(self):
return float(self._trailing_step_pips.Value)
@property
def MaxPositions(self):
return int(self._max_positions.Value)
@property
def CandleType(self):
return self._candle_type.Value
def _calc_pip_size(self):
sec = self.Security
if sec is None or sec.PriceStep is None:
return 1.0
step = float(sec.PriceStep)
if step <= 0:
return 1.0
if step < 0.01:
step *= 10.0
return step
def OnStarted2(self, time):
super(green_trade_strategy, self).OnStarted2(time)
self._smma = SmoothedMovingAverage()
self._smma.Length = self.MaPeriod
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
self._pip_size = self._calc_pip_size()
self._ma_history = []
self._rsi_history = []
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._smma)
self.DrawIndicator(area, self._rsi)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
median = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
close = float(candle.ClosePrice)
t = candle.OpenTime
ma_result = process_float(self._smma, Decimal(median), candle.ServerTime, True)
rsi_result = process_float(self._rsi, Decimal(close), candle.ServerTime, True)
if not self._smma.IsFormed or not self._rsi.IsFormed:
self._ma_history.append(0.0)
self._rsi_history.append(0.0)
self._trim_history()
return
ma_val = float(ma_result.Value)
rsi_val = float(rsi_result.Value)
self._ma_history.append(ma_val)
self._rsi_history.append(rsi_val)
self._trim_history()
shift0 = self.ShiftBar
shift1 = shift0 + self.ShiftBar1
shift2 = shift1 + self.ShiftBar2
shift3 = shift2 + self.ShiftBar3
ma0 = self._get_hist(self._ma_history, shift0)
ma1 = self._get_hist(self._ma_history, shift1)
ma2 = self._get_hist(self._ma_history, shift2)
ma3 = self._get_hist(self._ma_history, shift3)
rsi_sample = self._get_hist(self._rsi_history, self.ShiftBar)
if ma0 is None or ma1 is None or ma2 is None or ma3 is None or rsi_sample is None:
return
buy_signal = ma0 > ma1 and ma1 > ma2 and ma2 > ma3 and rsi_sample > self.RsiBuyLevel
sell_signal = ma0 < ma1 and ma1 < ma2 and ma2 < ma3 and rsi_sample < self.RsiSellLevel
if buy_signal and self._can_increase(True):
self._open_position(True, candle)
elif sell_signal and self._can_increase(False):
self._open_position(False, candle)
self._update_trailing(candle)
self._manage_exits(candle)
def _open_position(self, is_long, candle):
close = float(candle.ClosePrice)
cur_pos = self.Position
if is_long:
self.BuyMarket()
if cur_pos > 0:
total = cur_pos + self.TradeVolume
self._entry_price = ((cur_pos * self._entry_price) + (self.TradeVolume * close)) / total if total > 0 else close
else:
self._entry_price = close
else:
self.SellMarket()
if cur_pos < 0:
total = abs(cur_pos) + self.TradeVolume
self._entry_price = ((abs(cur_pos) * self._entry_price) + (self.TradeVolume * close)) / total if total > 0 else close
else:
self._entry_price = close
stop_dist = self.StopLossPips * self._pip_size
take_dist = self.TakeProfitPips * self._pip_size
if is_long:
self._stop_price = self._entry_price - stop_dist if stop_dist > 0 else None
self._take_price = self._entry_price + take_dist if take_dist > 0 else None
else:
self._stop_price = self._entry_price + stop_dist if stop_dist > 0 else None
self._take_price = self._entry_price - take_dist if take_dist > 0 else None
def _update_trailing(self, candle):
if self.TrailingStopPips <= 0:
return
trail_dist = self.TrailingStopPips * self._pip_size
step_dist = self.TrailingStepPips * self._pip_size
close = float(candle.ClosePrice)
if self.Position > 0 and self._entry_price > 0:
profit = close - self._entry_price
if profit > trail_dist + step_dist:
threshold = close - (trail_dist + step_dist)
if self._stop_price is None or self._stop_price < threshold:
self._stop_price = close - trail_dist
elif self.Position < 0 and self._entry_price > 0:
profit = self._entry_price - close
if profit > trail_dist + step_dist:
threshold = close + trail_dist + step_dist
if self._stop_price is None or self._stop_price > threshold:
self._stop_price = close + trail_dist
def _manage_exits(self, candle):
h = float(candle.HighPrice)
lo = float(candle.LowPrice)
if self.Position > 0:
if self._take_price is not None and h >= self._take_price:
self.SellMarket()
self._reset_state()
return
if self._stop_price is not None and lo <= self._stop_price:
self.SellMarket()
self._reset_state()
return
elif self.Position < 0:
if self._take_price is not None and lo <= self._take_price:
self.BuyMarket()
self._reset_state()
return
if self._stop_price is not None and h >= self._stop_price:
self.BuyMarket()
self._reset_state()
return
else:
self._reset_state()
def _can_increase(self, is_long):
if self.TradeVolume <= 0:
return False
if self.MaxPositions <= 0:
return True
max_vol = self.MaxPositions * self.TradeVolume
abs_pos = abs(self.Position)
if is_long and self.Position < 0:
return True
if not is_long and self.Position > 0:
return True
return abs_pos + self.TradeVolume <= max_vol + 0.0000001
def _get_hist(self, values, shift):
if shift <= 0:
return None
index = len(values) - shift
if index < 0:
return None
return values[index]
def _trim_history(self):
max_shift = self.ShiftBar + self.ShiftBar1 + self.ShiftBar2 + self.ShiftBar3
max_count = max(max_shift + 5, 10)
while len(self._ma_history) > max_count:
self._ma_history.pop(0)
while len(self._rsi_history) > max_count:
self._rsi_history.pop(0)
def _reset_state(self):
if abs(self.Position) < 0.0000001:
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(green_trade_strategy, self).OnReseted()
self._ma_history = []
self._rsi_history = []
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
def CreateClone(self):
return green_trade_strategy()