Forex Fraus 4 For M1s Strategy
Conversion of MQL4 strategy #13643. The original expert advisor enters trades when the Williams %R indicator touches extreme levels and then crosses back. This C# version uses the high-level API of StockSharp.
The strategy works on 1-minute candles and reacts to two key levels:
- A long signal is generated after Williams %R rises above -99.9 having been below it.
- A short signal appears when Williams %R falls below -0.1 having been above it.
Positions are closed by fixed stops, targets or trailing stop. A time filter can restrict trading to a specific intraday window.
Details
- Entry Criteria
- Long:
WilliamsR crosses up BuyThreshold (-99.9) after being lower.
- Short:
WilliamsR crosses down SellThreshold (-0.1) after being higher.
- Long/Short: Both
- Exit Criteria
- Price hits stop-loss (
StopLoss) or take-profit (TakeProfit)
- Trailing stop (
TrailingStop) activated when enabled
- Stops: Step-based
- Default Values
WprPeriod = 360
BuyThreshold = -99.9
SellThreshold = -0.1
StopLoss = 0
TakeProfit = 0
UseProfitTrailing = true
TrailingStop = 30
TrailingStep = 1
UseTimeFilter = false
StartHour = 7
StopHour = 17
Volume = 0.01
CandleType = TimeSpan.FromMinutes(1).TimeFrame()
- Filters
- Category: Trend reversal
- Direction: Both
- Indicators: Williams %R
- Stops: Yes
- Complexity: Basic
- Timeframe: Intraday (M1)
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
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 extreme cross strategy.
/// Buys when WPR crosses above oversold level, sells when crossing below overbought level.
/// </summary>
public class ForexFraus4ForM1sStrategy : Strategy
{
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _buyThreshold;
private readonly StrategyParam<decimal> _sellThreshold;
private readonly StrategyParam<DataType> _candleType;
private bool _wasOversold;
private bool _wasOverbought;
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 DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ForexFraus4ForM1sStrategy()
{
_wprPeriod = Param(nameof(WprPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators");
_buyThreshold = Param(nameof(BuyThreshold), -90m)
.SetDisplay("Buy Threshold", "Level crossing up triggers buy", "Trading");
_sellThreshold = Param(nameof(SellThreshold), -10m)
.SetDisplay("Sell Threshold", "Level crossing down triggers sell", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).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();
_wasOversold = default;
_wasOverbought = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_wasOversold = false;
_wasOverbought = false;
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(wpr, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, wpr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!wprValue.IsFormed)
return;
var wpr = wprValue.ToDecimal();
// Track oversold/overbought states
if (wpr < BuyThreshold)
_wasOversold = true;
if (wpr > SellThreshold)
_wasOverbought = true;
// Buy signal: was oversold and now crossed above threshold
if (_wasOversold && wpr > BuyThreshold && Position <= 0)
{
_wasOversold = false;
if (Position < 0) BuyMarket();
BuyMarket();
}
// Sell signal: was overbought and now crossed below threshold
else if (_wasOverbought && wpr < SellThreshold && Position >= 0)
{
_wasOverbought = false;
if (Position > 0) SellMarket();
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class forex_fraus4_for_m1s_strategy(Strategy):
def __init__(self):
super(forex_fraus4_for_m1s_strategy, self).__init__()
self._wpr_period = self.Param("WprPeriod", 100) \
.SetDisplay("Williams %R Period", "Period for Williams %R", "Indicators")
self._buy_threshold = self.Param("BuyThreshold", -90.0) \
.SetDisplay("Buy Threshold", "Level crossing up triggers buy", "Trading")
self._sell_threshold = self.Param("SellThreshold", -10.0) \
.SetDisplay("Sell Threshold", "Level crossing down triggers sell", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._was_oversold = False
self._was_overbought = False
@property
def WprPeriod(self):
return self._wpr_period.Value
@WprPeriod.setter
def WprPeriod(self, value):
self._wpr_period.Value = value
@property
def BuyThreshold(self):
return self._buy_threshold.Value
@BuyThreshold.setter
def BuyThreshold(self, value):
self._buy_threshold.Value = value
@property
def SellThreshold(self):
return self._sell_threshold.Value
@SellThreshold.setter
def SellThreshold(self, value):
self._sell_threshold.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(forex_fraus4_for_m1s_strategy, self).OnStarted2(time)
self._was_oversold = False
self._was_overbought = False
wpr = WilliamsR()
wpr.Length = self.WprPeriod
self.SubscribeCandles(self.CandleType) \
.BindEx(wpr, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(2.0, UnitTypes.Percent),
stopLoss=Unit(1.0, UnitTypes.Percent)
)
def ProcessCandle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
if not wpr_value.IsFormed:
return
wpr = float(wpr_value)
buy_th = float(self.BuyThreshold)
sell_th = float(self.SellThreshold)
if wpr < buy_th:
self._was_oversold = True
if wpr > sell_th:
self._was_overbought = True
if self._was_oversold and wpr > buy_th and self.Position <= 0:
self._was_oversold = False
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._was_overbought and wpr < sell_th and self.Position >= 0:
self._was_overbought = False
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def OnReseted(self):
super(forex_fraus4_for_m1s_strategy, self).OnReseted()
self._was_oversold = False
self._was_overbought = False
def CreateClone(self):
return forex_fraus4_for_m1s_strategy()