FrakTrak XonaX is a breakout strategy based on fractal levels calculated on a higher timeframe. When price moves beyond the most recent fractal by a small offset the strategy enters in the direction of the breakout. A fixed take profit and trailing stop manage the open position.
Parameters
Volume – order size.
Take Profit – distance in points for the take-profit level.
Trailing Stop – distance in points used for trailing the stop-loss.
Trailing Correction – additional distance added to the trailing stop.
Candle Type – timeframe used to build candles and fractals.
Trading rules
Calculate upper and lower fractals using the last completed candles.
Buy when the close price exceeds the upper fractal plus 15 points and no long position exists. Stop-loss is placed at the last lower fractal and take-profit is set using Take Profit.
Sell when the close price falls below the lower fractal minus 15 points and no short position exists. Stop-loss is placed at the last upper fractal and take-profit is set using Take Profit.
After a position becomes profitable more than Trailing Stop points, the stop-loss trails behind price with an additional Trailing Correction offset.
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>
/// FrakTrak XonaX strategy.
/// Uses fractal breakouts to generate entry signals.
/// </summary>
public class FraktrakXonaxStrategy : Strategy
{
private readonly StrategyParam<decimal> _fractalOffset;
private readonly StrategyParam<DataType> _candleType;
private decimal _h1, _h2, _h3, _h4, _h5;
private decimal _l1, _l2, _l3, _l4, _l5;
private decimal? _upFractal;
private decimal? _downFractal;
private decimal? _lastUpFractal;
private decimal? _lastDownFractal;
/// <summary>
/// Price offset added beyond fractal for entry trigger.
/// </summary>
public decimal FractalOffset
{
get => _fractalOffset.Value;
set => _fractalOffset.Value = value;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public FraktrakXonaxStrategy()
{
_fractalOffset = Param(nameof(FractalOffset), 50m)
.SetDisplay("Fractal Offset", "Price offset beyond fractal for entry", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_h1 = _h2 = _h3 = _h4 = _h5 = 0m;
_l1 = _l2 = _l3 = _l4 = _l5 = 0m;
_upFractal = _downFractal = _lastUpFractal = _lastDownFractal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
// Shift high and low buffers
_h1 = _h2; _h2 = _h3; _h3 = _h4; _h4 = _h5; _h5 = candle.HighPrice;
_l1 = _l2; _l2 = _l3; _l3 = _l4; _l4 = _l5; _l5 = candle.LowPrice;
// Need at least 5 bars for fractal detection
if (_h1 == 0 || _l1 == 0)
return;
// Detect new fractals (bar 3 is the middle of 5 bars)
if (_h3 > _h1 && _h3 > _h2 && _h3 > _h4 && _h3 > _h5)
_upFractal = _h3;
if (_l3 < _l1 && _l3 < _l2 && _l3 < _l4 && _l3 < _l5)
_downFractal = _l3;
// Buy signal: close above up fractal + offset
if (_upFractal is decimal up && _lastUpFractal != up)
{
var trigger = up + FractalOffset;
if (candle.ClosePrice > trigger && Position <= 0)
{
BuyMarket();
_lastUpFractal = up;
}
}
// Sell signal: close below down fractal - offset
if (_downFractal is decimal low && _lastDownFractal != low)
{
var trigger = low - FractalOffset;
if (candle.ClosePrice < trigger && Position >= 0)
{
SellMarket();
_lastDownFractal = low;
}
}
}
}
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.Strategies import Strategy
class fraktrak_xonax_strategy(Strategy):
def __init__(self):
super(fraktrak_xonax_strategy, self).__init__()
self._fractal_offset = self.Param("FractalOffset", 50.0) \
.SetDisplay("Fractal Offset", "Price offset beyond fractal for entry", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._h1 = 0.0
self._h2 = 0.0
self._h3 = 0.0
self._h4 = 0.0
self._h5 = 0.0
self._l1 = 0.0
self._l2 = 0.0
self._l3 = 0.0
self._l4 = 0.0
self._l5 = 0.0
self._up_fractal = None
self._down_fractal = None
self._last_up_fractal = None
self._last_down_fractal = None
@property
def fractal_offset(self):
return self._fractal_offset.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fraktrak_xonax_strategy, self).OnReseted()
self._h1 = self._h2 = self._h3 = self._h4 = self._h5 = 0.0
self._l1 = self._l2 = self._l3 = self._l4 = self._l5 = 0.0
self._up_fractal = None
self._down_fractal = None
self._last_up_fractal = None
self._last_down_fractal = None
def OnStarted2(self, time):
super(fraktrak_xonax_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
# Shift high and low buffers
self._h1 = self._h2
self._h2 = self._h3
self._h3 = self._h4
self._h4 = self._h5
self._h5 = float(candle.HighPrice)
self._l1 = self._l2
self._l2 = self._l3
self._l3 = self._l4
self._l4 = self._l5
self._l5 = float(candle.LowPrice)
# Need at least 5 bars for fractal detection
if self._h1 == 0 or self._l1 == 0:
return
# Detect new fractals (bar 3 is the middle of 5 bars)
if self._h3 > self._h1 and self._h3 > self._h2 and self._h3 > self._h4 and self._h3 > self._h5:
self._up_fractal = self._h3
if self._l3 < self._l1 and self._l3 < self._l2 and self._l3 < self._l4 and self._l3 < self._l5:
self._down_fractal = self._l3
close = float(candle.ClosePrice)
offset = float(self.fractal_offset)
# Buy signal: close above up fractal + offset
if self._up_fractal is not None and self._last_up_fractal != self._up_fractal:
trigger = self._up_fractal + offset
if close > trigger and self.Position <= 0:
self.BuyMarket()
self._last_up_fractal = self._up_fractal
# Sell signal: close below down fractal - offset
if self._down_fractal is not None and self._last_down_fractal != self._down_fractal:
trigger = self._down_fractal - offset
if close < trigger and self.Position >= 0:
self.SellMarket()
self._last_down_fractal = self._down_fractal
def CreateClone(self):
return fraktrak_xonax_strategy()