Fibonacci TP SL戦略
この戦略はFibonacciリトレースメントレベルを使用してエントリーを生成し、ATRベースのストップロスとパーセンテージベースのテイクプロフィットを設定します。トレード間の最小バー間隔と週次利益上限によって取引が制限されます。
詳細
エントリー条件:
- ロング:
Close <= Fib 38.2%&&Close >= Fib 78.6%&&Min bars since last trade - ショート:
Close <= Fib 23.6%&&Close >= Fib 61.8%&&Min bars since last trade
- ロング:
ロング/ショート: 両方向
エグジット条件:
ATR stop-lossまたはTake-profit
ストップ: はい
デフォルト値:
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()