HSI首个30分钟蜡烛策略
该策略在香港市场开盘后的前30分钟记录最高价和最低价,并在5分钟图上交易其突破。每天仅允许一笔交易。
细节
- 入场条件:
- 多头:价格在交易时段内突破前30分钟高点。
- 空头:价格在交易时段内跌破前30分钟低点。
- 多空方向:双向。
- 出场条件:
- 止损位于区间另一侧。
- 止盈为区间大小乘以
RiskReward。
- 止损:有。
- 默认值:
RiskReward= 1。CandleType= TimeSpan.FromMinutes(5).TimeFrame().
- 筛选:
- 类型: 突破
- 方向: 双向
- 指标: 价格行为
- 止损: 有
- 复杂度: 基础
- 时间框架: 日内
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险等级: 中
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>
/// HSI First 30m Candle strategy.
/// Records the high and low of the first 30 minutes after the Hong Kong session opens (09:30 HKT, 01:30 UTC). During the rest of
/// the session a close above that high goes long and a close below that low goes short, at most one trade per day. The stop sits
/// at the opposite side of the range and the target at the range size multiplied by RiskReward from the entry.
/// </summary>
public class HsiFirst30mCandleStrategy : Strategy
{
private static readonly TimeSpan _sessionOpen = new(1, 30, 0);
private static readonly TimeSpan _rangeEnd = new(2, 0, 0);
private static readonly TimeSpan _sessionClose = new(8, 0, 0);
private readonly StrategyParam<decimal> _riskReward;
private readonly StrategyParam<DataType> _candleType;
private DateTime _currentDay;
private decimal? _rangeHigh;
private decimal? _rangeLow;
private bool _tradedToday;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Target distance as a multiple of the range size.
/// </summary>
public decimal RiskReward
{
get => _riskReward.Value;
set => _riskReward.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public HsiFirst30mCandleStrategy()
{
_riskReward = Param(nameof(RiskReward), 1m)
.SetGreaterThanZero()
.SetDisplay("Risk Reward", "Target distance as a multiple of the range size", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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()
{
_currentDay = default;
_rangeHigh = null;
_rangeLow = null;
_tradedToday = false;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
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;
var openTime = candle.OpenTime;
var day = openTime.Date;
var timeOfDay = openTime.TimeOfDay;
if (day != _currentDay)
{
_currentDay = day;
_rangeHigh = null;
_rangeLow = null;
_tradedToday = false;
}
if (timeOfDay >= _sessionOpen && timeOfDay < _rangeEnd)
{
_rangeHigh = _rangeHigh is decimal h ? Math.Max(h, candle.HighPrice) : candle.HighPrice;
_rangeLow = _rangeLow is decimal l ? Math.Min(l, candle.LowPrice) : candle.LowPrice;
return;
}
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
SellMarket(Position);
return;
}
if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
BuyMarket(-Position);
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_tradedToday || timeOfDay < _rangeEnd || timeOfDay >= _sessionClose)
return;
if (_rangeHigh is not decimal high || _rangeLow is not decimal low)
return;
var range = high - low;
if (range <= 0)
return;
var close = candle.ClosePrice;
if (close > high)
{
BuyMarket(Volume);
_stopPrice = low;
_takePrice = close + range * RiskReward;
_tradedToday = true;
}
else if (close < low)
{
SellMarket(Volume);
_stopPrice = high;
_takePrice = close - range * RiskReward;
_tradedToday = true;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
# Minutes after midnight UTC.
SESSION_OPEN = 90.0
RANGE_END = 120.0
SESSION_CLOSE = 480.0
class hsi_first_30m_candle_strategy(Strategy):
"""
HSI First 30m Candle strategy.
Records the high and low of the first 30 minutes after the Hong Kong session opens (09:30 HKT, 01:30 UTC). During the rest of
the session a close above that high goes long and a close below that low goes short, at most one trade per day. The stop sits
at the opposite side of the range and the target at the range size multiplied by RiskReward from the entry.
"""
def __init__(self):
super(hsi_first_30m_candle_strategy, self).__init__()
self._risk_reward = self.Param("RiskReward", 1.0).SetGreaterThanZero().SetDisplay("Risk Reward", "Target distance as a multiple of the range size", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).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._current_day = None
self._range_high = None
self._range_low = None
self._traded_today = False
self._stop_price = 0.0
self._take_price = 0.0
def OnReseted(self):
super(hsi_first_30m_candle_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(hsi_first_30m_candle_strategy, self).OnStarted2(time)
self._reset_state()
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
open_time = candle.OpenTime
day = open_time.Date
time_of_day = open_time.TimeOfDay.TotalMinutes
if self._current_day is None or day != self._current_day:
self._current_day = day
self._range_high = None
self._range_low = None
self._traded_today = False
high_price = float(candle.HighPrice)
low_price = float(candle.LowPrice)
if time_of_day >= SESSION_OPEN and time_of_day < RANGE_END:
self._range_high = high_price if self._range_high is None else max(self._range_high, high_price)
self._range_low = low_price if self._range_low is None else min(self._range_low, low_price)
return
if self.Position > 0:
if low_price <= self._stop_price or high_price >= self._take_price:
self.SellMarket(self.Position)
return
if self.Position < 0:
if high_price >= self._stop_price or low_price <= self._take_price:
self.BuyMarket(-self.Position)
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._traded_today or time_of_day < RANGE_END or time_of_day >= SESSION_CLOSE:
return
if self._range_high is None or self._range_low is None:
return
high = self._range_high
low = self._range_low
rng = high - low
if rng <= 0:
return
close = float(candle.ClosePrice)
rr = float(self._risk_reward.Value)
if close > high:
self.BuyMarket(self.Volume)
self._stop_price = low
self._take_price = close + rng * rr
self._traded_today = True
elif close < low:
self.SellMarket(self.Volume)
self._stop_price = high
self._take_price = close - rng * rr
self._traded_today = True
def CreateClone(self):
return hsi_first_30m_candle_strategy()