Стратегия Fibonacci TP SL
Эта стратегия использует уровни коррекции Фибоначчи для входа в позицию, устанавливая стоп-лосс на основе ATR и тейк-профит в процентах. Торговля ограничена минимальным интервалом между сделками и недельным ограничением прибыли.
Детали
Условия входа:
- Лонг:
Close <= Fib 38.2%&&Close >= Fib 78.6%&&Минимальное количество баров с последней сделки - Шорт:
Close <= Fib 23.6%&&Close >= Fib 61.8%&&Минимальное количество баров с последней сделки
- Лонг:
Направление: Обе стороны
Условия выхода:
ATR стоп-лоссилиТейк-профит
Стопы: Да
Значения по умолчанию:
Take Profit %= 4Min Bars Between Trades= 10Lookback= 100ATR Period= 14ATR Multiplier= 1.5Max Weekly Return= 0.15
Фильтры:
- Категория: Среднее возвращение
- Направление: Обе
- Индикаторы: Highest, Lowest, ATR
- Стопы: Да
- Сложность: Низкая
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Fibonacci TP SL strategy.
/// Retracement levels are measured down from the highest high of the last Lookback candles towards the lowest low. While flat it
/// goes long when the close lies between the 78.6% and 38.2% levels and short when it lies between the 61.8% and 23.6% levels,
/// provided at least MinBarsBetweenTrades candles passed since the last entry. A position closes at an ATR-based stop set at entry
/// or at a percent take profit. No new trades open in a week once the week's return reaches MaxWeeklyReturn.
/// </summary>
public class FibonacciTpSlStrategy : Strategy
{
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<int> _minBarsBetweenTrades;
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _maxWeeklyReturn;
private readonly StrategyParam<DataType> _candleType;
private int _barIndex;
private int? _lastEntryBar;
private decimal _stopPrice;
private decimal _takePrice;
private DateTime _currentWeek;
private decimal _weekStartPnL;
private decimal _weekStartValue;
/// <summary>
/// Take profit in percent of the entry price.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Minimum candles between entries.
/// </summary>
public int MinBarsBetweenTrades
{
get => _minBarsBetweenTrades.Value;
set => _minBarsBetweenTrades.Value = value;
}
/// <summary>
/// Candles the Fibonacci range spans.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// ATR multiplier of the stop distance.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Weekly return (fraction of the account value) after which no new trades open.
/// </summary>
public decimal MaxWeeklyReturn
{
get => _maxWeeklyReturn.Value;
set => _maxWeeklyReturn.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FibonacciTpSlStrategy()
{
_takeProfitPercent = Param(nameof(TakeProfitPercent), 4m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit in percent of the entry price", "Risk");
_minBarsBetweenTrades = Param(nameof(MinBarsBetweenTrades), 10)
.SetNotNegative()
.SetDisplay("Min Bars Between Trades", "Minimum candles between entries", "General");
_lookback = Param(nameof(Lookback), 100)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Candles the Fibonacci range spans", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR period", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR multiplier of the stop distance", "Risk");
_maxWeeklyReturn = Param(nameof(MaxWeeklyReturn), 0.15m)
.SetNotNegative()
.SetDisplay("Max Weekly Return", "Weekly return after which no new trades open", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_barIndex = 0;
_lastEntryBar = null;
_stopPrice = 0m;
_takePrice = 0m;
_currentWeek = default;
_weekStartPnL = 0m;
_weekStartValue = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var highest = new Highest { Length = Lookback };
var lowest = new Lowest { Length = Lookback };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
var bar = _barIndex++;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var weekCapReached = UpdateWeek(candle.OpenTime);
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || (_takePrice > 0 && candle.HighPrice >= _takePrice))
SellMarket(Position);
return;
}
if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || (_takePrice > 0 && candle.LowPrice <= _takePrice))
BuyMarket(-Position);
return;
}
if (weekCapReached)
return;
if (_lastEntryBar is int lastBar && bar - lastBar < MinBarsBetweenTrades)
return;
var range = high - low;
if (range <= 0)
return;
var fib236 = high - range * 0.236m;
var fib382 = high - range * 0.382m;
var fib618 = high - range * 0.618m;
var fib786 = high - range * 0.786m;
var close = candle.ClosePrice;
var stopDistance = atr * AtrMultiplier;
if (close <= fib382 && close >= fib786)
{
_stopPrice = close - stopDistance;
_takePrice = TakeProfitPercent > 0 ? close * (1 + TakeProfitPercent / 100m) : 0m;
_lastEntryBar = bar;
BuyMarket();
}
else if (close <= fib236 && close >= fib618)
{
_stopPrice = close + stopDistance;
_takePrice = TakeProfitPercent > 0 ? close * (1 - TakeProfitPercent / 100m) : 0m;
_lastEntryBar = bar;
SellMarket();
}
}
private bool UpdateWeek(DateTime time)
{
var date = time.Date;
var week = date.AddDays(-(((int)date.DayOfWeek + 6) % 7));
if (_currentWeek != week)
{
_currentWeek = week;
_weekStartPnL = PnL;
_weekStartValue = Portfolio?.CurrentValue ?? 0m;
}
// Without a known account value there is nothing to measure the return against.
return _weekStartValue > 0 && MaxWeeklyReturn > 0 && (PnL - _weekStartPnL) / _weekStartValue >= MaxWeeklyReturn;
}
}
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 Highest, Lowest, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class fibonacci_tp_sl_strategy(Strategy):
"""
Fibonacci TP SL strategy.
Retracement levels are measured down from the highest high of the last Lookback candles towards the lowest low. While flat it
goes long when the close lies between the 78.6% and 38.2% levels and short when it lies between the 61.8% and 23.6% levels,
provided at least MinBarsBetweenTrades candles passed since the last entry. A position closes at an ATR-based stop set at entry
or at a percent take profit. No new trades open in a week once the week's return reaches MaxWeeklyReturn.
"""
def __init__(self):
super(fibonacci_tp_sl_strategy, self).__init__()
self._take_profit_percent = self.Param("TakeProfitPercent", 4.0).SetNotNegative().SetDisplay("Take Profit %", "Take profit in percent of the entry price", "Risk")
self._min_bars_between_trades = self.Param("MinBarsBetweenTrades", 10).SetNotNegative().SetDisplay("Min Bars Between Trades", "Minimum candles between entries", "General")
self._lookback = self.Param("Lookback", 100).SetGreaterThanZero().SetDisplay("Lookback", "Candles the Fibonacci range spans", "Indicators")
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero().SetDisplay("ATR Period", "ATR period", "Indicators")
self._atr_multiplier = self.Param("AtrMultiplier", 1.5).SetGreaterThanZero().SetDisplay("ATR Multiplier", "ATR multiplier of the stop distance", "Risk")
self._max_weekly_return = self.Param("MaxWeeklyReturn", 0.15).SetNotNegative().SetDisplay("Max Weekly Return", "Weekly return after which no new trades open", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._bar_index = 0
self._last_entry_bar = None
self._stop_price = 0.0
self._take_price = 0.0
self._current_week = None
self._week_start_pnl = 0.0
self._week_start_value = 0.0
def OnReseted(self):
super(fibonacci_tp_sl_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(fibonacci_tp_sl_strategy, self).OnStarted2(time)
self._reset_state()
highest = Highest()
highest.Length = self._lookback.Value
lowest = Lowest()
lowest.Length = self._lookback.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(highest, lowest, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, highest)
self.DrawIndicator(area, lowest)
self.DrawOwnTrades(area)
def _process_candle(self, candle, high_value, low_value, atr_value):
if candle.State != CandleStates.Finished:
return
bar = self._bar_index
self._bar_index += 1
if not self.IsFormedAndOnlineAndAllowTrading():
return
week_cap_reached = self._update_week(candle.OpenTime)
if self.Position > 0:
if float(candle.LowPrice) <= self._stop_price or (self._take_price > 0 and float(candle.HighPrice) >= self._take_price):
self.SellMarket(self.Position)
return
if self.Position < 0:
if float(candle.HighPrice) >= self._stop_price or (self._take_price > 0 and float(candle.LowPrice) <= self._take_price):
self.BuyMarket(-self.Position)
return
if week_cap_reached:
return
if self._last_entry_bar is not None and bar - self._last_entry_bar < self._min_bars_between_trades.Value:
return
high = float(high_value)
low = float(low_value)
rng = high - low
if rng <= 0:
return
fib236 = high - rng * 0.236
fib382 = high - rng * 0.382
fib618 = high - rng * 0.618
fib786 = high - rng * 0.786
close = float(candle.ClosePrice)
stop_distance = float(atr_value) * float(self._atr_multiplier.Value)
take_percent = float(self._take_profit_percent.Value)
if close <= fib382 and close >= fib786:
self._stop_price = close - stop_distance
self._take_price = close * (1.0 + take_percent / 100.0) if take_percent > 0 else 0.0
self._last_entry_bar = bar
self.BuyMarket()
elif close <= fib236 and close >= fib618:
self._stop_price = close + stop_distance
self._take_price = close * (1.0 - take_percent / 100.0) if take_percent > 0 else 0.0
self._last_entry_bar = bar
self.SellMarket()
def _update_week(self, time):
date = time.Date
week = date.AddDays(-((int(date.DayOfWeek) + 6) % 7))
pnl = float(self.PnL)
if self._current_week != week:
self._current_week = week
self._week_start_pnl = pnl
self._week_start_value = float(self.Portfolio.CurrentValue) if self.Portfolio is not None and self.Portfolio.CurrentValue is not None else 0.0
# Without a known account value there is nothing to measure the return against.
cap = float(self._max_weekly_return.Value)
return self._week_start_value > 0 and cap > 0 and (pnl - self._week_start_pnl) / self._week_start_value >= cap
def CreateClone(self):
return fibonacci_tp_sl_strategy()