ランチブレーク・フェード戦略
ランチブレーク・フェード戦略は、昼休みの閑散とした時間帯に生じるリバーサルを狙います。 午前のセッション後、昼頃に出来高が減少するとトレンドが一時停止または押し戻されることが多くあります。
テストでは年平均リターンが約127%であることが示されています。株式市場で最も効果的です。
この戦略は正午頃に午前の動きに逆張りし、優勢な方向と逆にエントリーして、出来高が戻る前に手仕舞います。
トレンドがフェードせず再開した場合のリスク管理のため、パーセントストップを使用します。
詳細
- エントリー条件: インジケーターシグナル
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは逆シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15 minuteStopLossPercent= 2LunchHour= 12LunchEndHour= 14 市場は24時間取引のため、セッションはUTCの1日とします。 LunchHourに終わる足の終値で、当日の始値からの動きと逆方向にエントリーし、LunchEndHourに決済します。
- フィルター:
- カテゴリ: イントラデイ
- 方向: 両方
- インジケーター: Price Action
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Lunch Break Fade strategy.
/// The market trades around the clock, so the session is the UTC day.
/// The morning move runs from the UTC day's open to the close at LunchHour. At that close the strategy enters against the move
/// and covers at the candle ending at LunchEndHour, before volume returns; a percent stop limits the loss.
/// </summary>
public class LunchBreakFadeStrategy : Strategy
{
private readonly StrategyParam<int> _lunchHour;
private readonly StrategyParam<int> _lunchEndHour;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _day;
private decimal _dayOpen;
/// <summary>
/// UTC hour that ends the morning.
/// </summary>
public int LunchHour
{
get => _lunchHour.Value;
set => _lunchHour.Value = value;
}
/// <summary>
/// UTC hour at which the position is covered.
/// </summary>
public int LunchEndHour
{
get => _lunchEndHour.Value;
set => _lunchEndHour.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public LunchBreakFadeStrategy()
{
_lunchHour = Param(nameof(LunchHour), 12)
.SetRange(0, 23)
.SetDisplay("Lunch Hour", "UTC hour that ends the morning", "Session");
_lunchEndHour = Param(nameof(LunchEndHour), 14)
.SetRange(0, 23)
.SetDisplay("Lunch End Hour", "UTC hour at which the position is covered", "Session");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_day = null;
_dayOpen = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_day = null;
_dayOpen = default;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
var firstOfDay = _day != day;
if (firstOfDay)
{
_day = day;
_dayOpen = candle.OpenPrice;
}
var frame = CandleType.Arg is TimeSpan tf ? tf : TimeSpan.Zero;
var closeTime = candle.OpenTime + frame;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
if (closeTime >= day.AddHours(LunchEndHour))
ClosePosition();
return;
}
if (closeTime != day.AddHours(LunchHour))
return;
if (candle.ClosePrice > _dayOpen)
SellMarket(Volume);
else if (candle.ClosePrice < _dayOpen)
BuyMarket(Volume);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Strategies import Strategy
class lunch_break_fade_strategy(Strategy):
"""
Lunch Break Fade strategy.
The market trades around the clock, so the session is the UTC day.
The morning move runs from the UTC day's open to the close at LunchHour. At that close the strategy enters against the move
and covers at the candle ending at LunchEndHour, before volume returns; a percent stop limits the loss.
"""
def __init__(self):
super(lunch_break_fade_strategy, self).__init__()
self._lunch_hour = self.Param("LunchHour", 12).SetRange(0, 23).SetDisplay("Lunch Hour", "UTC hour that ends the morning", "Session")
self._lunch_end_hour = self.Param("LunchEndHour", 14).SetRange(0, 23).SetDisplay("Lunch End Hour", "UTC hour at which the position is covered", "Session")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).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._day = None
self._day_open = Decimal(0)
def OnReseted(self):
super(lunch_break_fade_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(lunch_break_fade_strategy, self).OnStarted2(time)
self._reset_state()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _close_position(self):
if self.Position > 0:
self.SellMarket(self.Position)
elif self.Position < 0:
self.BuyMarket(-self.Position)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
day = candle.OpenTime.Date
first_of_day = self._day is None or self._day != day
if first_of_day:
self._day = day
self._day_open = candle.OpenPrice
frame = self.candle_type.Arg
close_time = candle.OpenTime + frame
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
if close_time >= day.AddHours(self._lunch_end_hour.Value):
self._close_position()
return
if close_time != day.AddHours(self._lunch_hour.Value):
return
if candle.ClosePrice > self._day_open:
self.SellMarket(self.Volume)
elif candle.ClosePrice < self._day_open:
self.BuyMarket(self.Volume)
def CreateClone(self):
return lunch_break_fade_strategy()