GreenTrade 戦略
概要
GreenTrade戦略は元のMQL5エキスパートアドバイザーの変換版です。平滑化移動平均(SMMA)の傾きフィルターと相対力指数(RSI)からのモメンタム確認を組み合わせることで中期トレンドを追います。シグナルは設定された時間軸の完成したローソク足で計算され、固定リスクコントロールとステップベースのトレーリングストップを適用しながら設定可能な数のポジションユニットまでピラミッドすることができます。
トレードロジック
- インジケーター準備
- SMMAは
MaPeriodパラメーターを使用して中央値価格((High + Low) / 2)で計算されます。 - RSIは
RsiPeriodルックバックを使用して終値で計算されます。
- SMMAは
- トレンドシェイプフィルター
- バーシフトパラメーター(
ShiftBar,ShiftBar1,ShiftBar2,ShiftBar3)に従って4つの歴史的SMMAサンプルが検査されます。 - 強気トレンドには
SMMA(shift0) > SMMA(shift1) > SMMA(shift2) > SMMA(shift3)が必要です。 - 弱気トレンドには
SMMA(shift0) < SMMA(shift1) < SMMA(shift2) < SMMA(shift3)が必要です。
- バーシフトパラメーター(
- モメンタム確認
- ロングエントリーにはRSIが
RsiBuyLevelを上回り、ショートエントリーにはRsiSellLevelを下回る必要があります。RSI値は形成中のローソク足を無視するMQL5ロジックを反映するため、ShiftBarバー前に取られます。
- ロングエントリーにはRSIが
- 注文実行
- シグナルが確認され、ポジション上限が超えていない場合、戦略は
TradeVolumeの市場注文を送信します。 - 反対方向のポジションが存在する場合、戦略はまずそれを中和してから設定されたボリュームで新しいポジションを開きます。
- 同方向のポジションが存在する場合、取引ボリュームは
MaxPositions * TradeVolumeまで純エクスポージャーに追加されます。
- シグナルが確認され、ポジション上限が超えていない場合、戦略は
リスク管理
- 初期ストップロス / テイクプロフィット:各新規エントリーは
StopLossPipsとTakeProfitPipsに基づいて価格ターゲットを設定します。pip距離はインスツルメントのPriceStepを使用して価格単位に変換されます。小数点以下のステップを持つインスツルメント(例:5桁Forexシンボル)は元のエキスパートと同様に追加の10倍の係数を受け取ります。 - トレーリングストップ:利益が
TrailingStopPips + TrailingStepPipsを超えると、ストップはTrailingStopPipsの距離を維持するよう移動します。追加の移動にはさらにTrailingStepPipsの価格改善が必要で、MQLコードからのステップワイズトレーリング動作を再現します。 - ポジション上限:
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 |
pipsでの初期ストップロス距離(0で無効)。 | 300 |
TakeProfitPips |
pipsでの初期テイクプロフィット距離(0で無効)。 | 300 |
TrailingStopPips |
有効化後の価格とトレーリングストップの距離(0でトレーリング無効)。 | 12 |
TrailingStepPips |
トレーリングストップが再度移動する前に必要な追加の進行。 | 5 |
MaxPositions |
アクティブにできるボリュームユニット(TradeVolumeの倍数)の最大数。 |
7 |
CandleType |
インジケーター更新に使用されるローソク足データシリーズ。 | 1時間時間軸 |
注意事項
- すべての計算は完成したローソク足のみで実行されます;ノイズの多いシグナルを避けるため未完成のローソク足は無視されます。
- ポジション状態は内部で追跡されるため、保護注文がエクスチェンジに配置されていなくても、ストップロス、テイクプロフィット、トレーリング決済が処理されます。
- 変換はpip変換とトレーリングステップロジックの元の動作を維持しながら、サブスクリプションと注文実行のためにStockSharpの高レベルAPIを活用します。
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()