日中リバーサル戦略
日中リバーサルは、午前中のトレンドが消耗しがちな昼頃に生じる転換点を狙います。 セッション中盤は流動性が低下する傾向があり、トレーダーがポジションを手仕舞うことでリバーサルが起きます。
テストでは年平均リターンが約121%であることが示されています。仮想通貨市場で最も効果的です。
システムは昼頃のモメンタムの変化を監視し、午前の動きと逆方向にエントリーします。
パーセントストップでリスクを管理し、午後までにリバーサルが進展しない場合は決済します。
詳細
- エントリー条件: インジケーターシグナル
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは逆シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15 minuteStopLossPercent= 2MiddayHour= 12AfternoonHour= 16 市場は24時間取引のため、セッションはUTCの1日とします。 朝の値動きは、当日の始値からMiddayHourに終わる足の終値までの変化です。MiddayHourからAfternoonHourの間に、その動きと逆に引けた最初の足で一度だけ逆張りし、AfternoonHourにポジションを閉じます。
- フィルター:
- カテゴリ: イントラデイ
- 方向: 両方
- インジケーター: 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>
/// Midday Reversal 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 MiddayHour. From then until AfternoonHour, the first candle that closes
/// against that move opens a position against it, once a day. The position closes at the candle ending at AfternoonHour,
/// and a percent stop limits the loss.
/// </summary>
public class MiddayReversalStrategy : Strategy
{
private readonly StrategyParam<int> _middayHour;
private readonly StrategyParam<int> _afternoonHour;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private DateTime? _day;
private decimal _dayOpen;
private int _morning;
private bool _tradedToday;
/// <summary>
/// UTC hour that ends the morning.
/// </summary>
public int MiddayHour
{
get => _middayHour.Value;
set => _middayHour.Value = value;
}
/// <summary>
/// UTC hour by which the reversal must have worked.
/// </summary>
public int AfternoonHour
{
get => _afternoonHour.Value;
set => _afternoonHour.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 MiddayReversalStrategy()
{
_middayHour = Param(nameof(MiddayHour), 12)
.SetRange(0, 23)
.SetDisplay("Midday Hour", "UTC hour that ends the morning", "Session");
_afternoonHour = Param(nameof(AfternoonHour), 16)
.SetRange(0, 23)
.SetDisplay("Afternoon Hour", "UTC hour by which the reversal must have worked", "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;
_morning = 0;
_tradedToday = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_day = null;
_dayOpen = default;
_morning = 0;
_tradedToday = false;
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;
_morning = 0;
_tradedToday = false;
}
var frame = CandleType.Arg is TimeSpan tf ? tf : TimeSpan.Zero;
var closeTime = candle.OpenTime + frame;
var endsAtMidday = closeTime == day.AddHours(MiddayHour);
if (endsAtMidday)
_morning = Math.Sign(candle.ClosePrice - _dayOpen);
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
if (closeTime >= day.AddHours(AfternoonHour))
ClosePosition();
return;
}
var window = closeTime > day.AddHours(MiddayHour) && closeTime < day.AddHours(AfternoonHour);
if (!window || _tradedToday || _morning == 0)
return;
var direction = Math.Sign(candle.ClosePrice - candle.OpenPrice);
if (direction == -_morning)
{
if (_morning > 0)
SellMarket(Volume);
else
BuyMarket(Volume);
_tradedToday = true;
}
}
}
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 midday_reversal_strategy(Strategy):
"""
Midday Reversal 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 MiddayHour. From then until AfternoonHour, the first candle that closes
against that move opens a position against it, once a day. The position closes at the candle ending at AfternoonHour,
and a percent stop limits the loss.
"""
def __init__(self):
super(midday_reversal_strategy, self).__init__()
self._midday_hour = self.Param("MiddayHour", 12).SetRange(0, 23).SetDisplay("Midday Hour", "UTC hour that ends the morning", "Session")
self._afternoon_hour = self.Param("AfternoonHour", 16).SetRange(0, 23).SetDisplay("Afternoon Hour", "UTC hour by which the reversal must have worked", "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)
self._morning = 0
self._traded_today = False
def OnReseted(self):
super(midday_reversal_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(midday_reversal_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
self._morning = 0
self._traded_today = False
frame = self.candle_type.Arg
close_time = candle.OpenTime + frame
midday = day.AddHours(self._midday_hour.Value)
afternoon = day.AddHours(self._afternoon_hour.Value)
if close_time == midday:
self._morning = 1 if candle.ClosePrice > self._day_open else (-1 if candle.ClosePrice < self._day_open else 0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
if close_time >= afternoon:
self._close_position()
return
window = close_time > midday and close_time < afternoon
if not window or self._traded_today or self._morning == 0:
return
direction = 1 if candle.ClosePrice > candle.OpenPrice else (-1 if candle.ClosePrice < candle.OpenPrice else 0)
if direction == -self._morning:
if self._morning > 0:
self.SellMarket(self.Volume)
else:
self.BuyMarket(self.Volume)
self._traded_today = True
def CreateClone(self):
return midday_reversal_strategy()