Forex Fraus Portfolio Strategy
This strategy trades a single instrument based on the Williams %R indicator with a long period. When the indicator leaves extreme zones, the strategy opens positions in the direction of the breakout.
How it works
- Calculate Williams %R over
WprPeriod candles.
- When the indicator drops below
BuyThreshold, a long opportunity is prepared. Once it rises above the threshold, a market buy order is placed.
- When the indicator rises above
SellThreshold, a short opportunity is prepared. Once it falls below the threshold, a market sell order is placed.
- Positions are allowed only during the time window between
StartHour and StopHour.
- Optional stop loss, take profit and trailing stop can be enabled through parameters.
Parameters
WprPeriod – Williams %R period.
BuyThreshold – value to enable a long signal.
SellThreshold – value to enable a short signal.
StartHour / StopHour – trading session limits.
SlPoints – stop loss in points. Disabled if 0.
TpPoints – take profit in points. Disabled if 0.
UseTrailing – enable trailing stop logic.
TrailingStop – trailing distance in points.
TrailingStep – step for trailing updates.
CandleType – candle type to subscribe.
Notes
The original MQL4 version traded multiple currency pairs and managed orders for each one. This C# port focuses on a single instrument and demonstrates the core idea using the high-level API of StockSharp.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Williams %R based strategy with trailing stop logic.
/// </summary>
public class ForexFrausPortfolioStrategy : Strategy
{
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _buyThreshold;
private readonly StrategyParam<decimal> _sellThreshold;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _stopHour;
private readonly StrategyParam<DataType> _candleType;
private bool _okBuy;
private bool _okSell;
public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
public decimal BuyThreshold { get => _buyThreshold.Value; set => _buyThreshold.Value = value; }
public decimal SellThreshold { get => _sellThreshold.Value; set => _sellThreshold.Value = value; }
public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }
public int StopHour { get => _stopHour.Value; set => _stopHour.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ForexFrausPortfolioStrategy()
{
_wprPeriod = Param(nameof(WprPeriod), 60)
.SetDisplay("WPR Period", "Williams %R calculation period", "Parameters")
.SetOptimize(20, 200, 20);
_buyThreshold = Param(nameof(BuyThreshold), -90m)
.SetDisplay("Buy Threshold", "Trigger level for long entry", "Parameters");
_sellThreshold = Param(nameof(SellThreshold), -10m)
.SetDisplay("Sell Threshold", "Trigger level for short entry", "Parameters");
_startHour = Param(nameof(StartHour), 0)
.SetDisplay("Start Hour", "Trading start hour", "Time");
_stopHour = Param(nameof(StopHour), 24)
.SetDisplay("Stop Hour", "Trading stop hour", "Time");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_okBuy = false;
_okSell = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_okBuy = false;
_okSell = false;
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(wpr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, wpr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var hour = candle.OpenTime.Hour;
var inTime = StartHour <= StopHour
? hour >= StartHour && hour < StopHour
: hour >= StartHour || hour < StopHour;
if (!inTime)
{
if (Position > 0)
SellMarket();
else if (Position < 0)
BuyMarket();
return;
}
// WPR dips below buy threshold => arm buy signal
if (wprValue < BuyThreshold)
_okBuy = true;
// WPR crosses back above buy threshold while armed => buy
if (wprValue > BuyThreshold && _okBuy)
{
_okBuy = false;
if (Position <= 0)
BuyMarket();
return;
}
// WPR rises above sell threshold => arm sell signal
if (wprValue > SellThreshold)
_okSell = true;
// WPR crosses back below sell threshold while armed => sell
if (wprValue < SellThreshold && _okSell)
{
_okSell = false;
if (Position >= 0)
SellMarket();
}
}
}
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 WilliamsR
from StockSharp.Algo.Strategies import Strategy
class forex_fraus_portfolio_strategy(Strategy):
def __init__(self):
super(forex_fraus_portfolio_strategy, self).__init__()
self._wpr_period = self.Param("WprPeriod", 60) \
.SetDisplay("WPR Period", "Williams %R calculation period", "Parameters")
self._buy_threshold = self.Param("BuyThreshold", -90.0) \
.SetDisplay("Buy Threshold", "Trigger level for long entry", "Parameters")
self._sell_threshold = self.Param("SellThreshold", -10.0) \
.SetDisplay("Sell Threshold", "Trigger level for short entry", "Parameters")
self._start_hour = self.Param("StartHour", 0) \
.SetDisplay("Start Hour", "Trading start hour", "Time")
self._stop_hour = self.Param("StopHour", 24) \
.SetDisplay("Stop Hour", "Trading stop hour", "Time")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._ok_buy = False
self._ok_sell = False
@property
def wpr_period(self):
return self._wpr_period.Value
@property
def buy_threshold(self):
return self._buy_threshold.Value
@property
def sell_threshold(self):
return self._sell_threshold.Value
@property
def start_hour(self):
return self._start_hour.Value
@property
def stop_hour(self):
return self._stop_hour.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(forex_fraus_portfolio_strategy, self).OnReseted()
self._ok_buy = False
self._ok_sell = False
def OnStarted2(self, time):
super(forex_fraus_portfolio_strategy, self).OnStarted2(time)
self._ok_buy = False
self._ok_sell = False
wpr = WilliamsR()
wpr.Length = self.wpr_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(wpr, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, wpr)
self.DrawOwnTrades(area)
def process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
wpr_value = float(wpr_value)
hour = candle.OpenTime.Hour
start_h = int(self.start_hour)
stop_h = int(self.stop_hour)
if start_h <= stop_h:
in_time = hour >= start_h and hour < stop_h
else:
in_time = hour >= start_h or hour < stop_h
if not in_time:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
return
buy_thr = float(self.buy_threshold)
sell_thr = float(self.sell_threshold)
if wpr_value < buy_thr:
self._ok_buy = True
if wpr_value > buy_thr and self._ok_buy:
self._ok_buy = False
if self.Position <= 0:
self.BuyMarket()
return
if wpr_value > sell_thr:
self._ok_sell = True
if wpr_value < sell_thr and self._ok_sell:
self._ok_sell = False
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return forex_fraus_portfolio_strategy()