Monthly Breakout 策略
该策略只在选定的月份交易当前月份的高点或低点突破。可以选择在月高或月低做多或做空,持仓在固定的柱数后平仓。
详情
- 入场条件:根据
EntryOption与所选月份,如收盘价突破当月高点。 - 多空方向:可配置。
- 出场条件:持仓达到
HoldingPeriod根K线后平仓。 - 止损:无。
- 默认值:
EntryOption= LongAtHighHoldingPeriod= 5CandleType= TimeSpan.FromDays(1)
- 过滤器:
- 分类:Breakout
- 方向:可配置
- 指标:无
- 止损:无
- 复杂度:基础
- 时间框架:日线
- 季节性:有
- 神经网络:无
- 背离:无
- 风险等级:中等
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Monthly breakout strategy using selectable months and holding period.
/// </summary>
public class MonthlyBreakoutStrategy : Strategy
{
public enum EntryOptions
{
LongAtHigh,
ShortAtHigh,
LongAtLow,
ShortAtLow
}
private readonly StrategyParam<EntryOptions> _entryOption;
private readonly StrategyParam<int> _holdingPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _january;
private readonly StrategyParam<bool> _february;
private readonly StrategyParam<bool> _march;
private readonly StrategyParam<bool> _april;
private readonly StrategyParam<bool> _may;
private readonly StrategyParam<bool> _june;
private readonly StrategyParam<bool> _july;
private readonly StrategyParam<bool> _august;
private readonly StrategyParam<bool> _september;
private readonly StrategyParam<bool> _october;
private readonly StrategyParam<bool> _november;
private readonly StrategyParam<bool> _december;
private decimal _monthlyHigh;
private decimal _monthlyLow;
private decimal _prevMonthlyHigh;
private decimal _prevMonthlyLow;
private decimal _prevClose;
private int _currentMonth;
private int _barIndex;
private int? _entryBar;
public EntryOptions EntryOption
{
get => _entryOption.Value;
set => _entryOption.Value = value;
}
public int HoldingPeriod
{
get => _holdingPeriod.Value;
set => _holdingPeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public bool January { get => _january.Value; set => _january.Value = value; }
public bool February { get => _february.Value; set => _february.Value = value; }
public bool March { get => _march.Value; set => _march.Value = value; }
public bool April { get => _april.Value; set => _april.Value = value; }
public bool May { get => _may.Value; set => _may.Value = value; }
public bool June { get => _june.Value; set => _june.Value = value; }
public bool July { get => _july.Value; set => _july.Value = value; }
public bool August { get => _august.Value; set => _august.Value = value; }
public bool September { get => _september.Value; set => _september.Value = value; }
public bool October { get => _october.Value; set => _october.Value = value; }
public bool November { get => _november.Value; set => _november.Value = value; }
public bool December { get => _december.Value; set => _december.Value = value; }
public MonthlyBreakoutStrategy()
{
_entryOption = Param(nameof(EntryOption), EntryOptions.LongAtLow)
.SetDisplay("Entry Option", "Breakout direction", "General");
_holdingPeriod = Param(nameof(HoldingPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Holding Period", "Bars to hold position", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Working candle timeframe", "General");
_january = Param(nameof(January), true).SetDisplay("January", "Enable trading in January", "Months");
_february = Param(nameof(February), true).SetDisplay("February", "Enable trading in February", "Months");
_march = Param(nameof(March), true).SetDisplay("March", "Enable trading in March", "Months");
_april = Param(nameof(April), true).SetDisplay("April", "Enable trading in April", "Months");
_may = Param(nameof(May), true).SetDisplay("May", "Enable trading in May", "Months");
_june = Param(nameof(June), true).SetDisplay("June", "Enable trading in June", "Months");
_july = Param(nameof(July), true).SetDisplay("July", "Enable trading in July", "Months");
_august = Param(nameof(August), true).SetDisplay("August", "Enable trading in August", "Months");
_september = Param(nameof(September), true).SetDisplay("September", "Enable trading in September", "Months");
_october = Param(nameof(October), true).SetDisplay("October", "Enable trading in October", "Months");
_november = Param(nameof(November), true).SetDisplay("November", "Enable trading in November", "Months");
_december = Param(nameof(December), true).SetDisplay("December", "Enable trading in December", "Months");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_monthlyHigh = 0m;
_monthlyLow = 0m;
_prevMonthlyHigh = 0m;
_prevMonthlyLow = 0m;
_prevClose = 0m;
_currentMonth = 0;
_barIndex = 0;
_entryBar = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var dummyEma1 = new ExponentialMovingAverage { Length = 10 };
var dummyEma2 = new ExponentialMovingAverage { Length = 20 };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(dummyEma1, dummyEma2, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal d1, decimal d2)
{
if (candle.State != CandleStates.Finished)
return;
_barIndex++;
var close = candle.ClosePrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var week = System.Globalization.CultureInfo.InvariantCulture.Calendar
.GetWeekOfYear(candle.OpenTime, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Monday);
var month = candle.OpenTime.Month;
if (week != _currentMonth)
{
if (_currentMonth != 0)
{
_prevMonthlyHigh = _monthlyHigh;
_prevMonthlyLow = _monthlyLow;
}
_monthlyHigh = high;
_monthlyLow = low;
_currentMonth = week;
}
else
{
_monthlyHigh = Math.Max(_monthlyHigh, high);
_monthlyLow = Math.Min(_monthlyLow, low);
}
if (_prevMonthlyHigh <= 0m || _prevMonthlyLow <= 0m)
{
_prevClose = close;
return;
}
var crossAboveHigh = _prevClose <= _prevMonthlyHigh && close > _prevMonthlyHigh;
var crossBelowHigh = _prevClose >= _prevMonthlyHigh && close < _prevMonthlyHigh;
var crossAboveLow = _prevClose <= _prevMonthlyLow && close > _prevMonthlyLow;
var crossBelowLow = _prevClose >= _prevMonthlyLow && close < _prevMonthlyLow;
if (Position != 0 && _entryBar.HasValue && _barIndex >= _entryBar.Value + HoldingPeriod)
{
if (Position > 0)
SellMarket();
else
BuyMarket();
_entryBar = null;
}
if (IsMonthSelected(month))
{
switch (EntryOption)
{
case EntryOptions.LongAtHigh when crossAboveHigh && Position <= 0:
BuyMarket();
_entryBar = _barIndex;
break;
case EntryOptions.ShortAtHigh when crossBelowHigh && Position >= 0:
SellMarket();
_entryBar = _barIndex;
break;
case EntryOptions.LongAtLow when crossAboveLow && Position <= 0:
BuyMarket();
_entryBar = _barIndex;
break;
case EntryOptions.ShortAtLow when crossBelowLow && Position >= 0:
SellMarket();
_entryBar = _barIndex;
break;
}
}
_prevClose = close;
}
private bool IsMonthSelected(int month)
{
return month switch
{
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
12 => December,
_ => false
};
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class monthly_breakout_strategy(Strategy):
def __init__(self):
super(monthly_breakout_strategy, self).__init__()
self._entry_option = self.Param("EntryOption", 2) \
.SetDisplay("Entry Option", "0=LongAtHigh,1=ShortAtHigh,2=LongAtLow,3=ShortAtLow", "General")
self._holding_period = self.Param("HoldingPeriod", 5) \
.SetGreaterThanZero() \
.SetDisplay("Holding Period", "Bars to hold position", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Working candle timeframe", "General")
self._monthly_high = 0.0
self._monthly_low = 0.0
self._prev_monthly_high = 0.0
self._prev_monthly_low = 0.0
self._prev_close = 0.0
self._current_week = 0
self._bar_index = 0
self._entry_bar = -1
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(monthly_breakout_strategy, self).OnReseted()
self._monthly_high = 0.0
self._monthly_low = 0.0
self._prev_monthly_high = 0.0
self._prev_monthly_low = 0.0
self._prev_close = 0.0
self._current_week = 0
self._bar_index = 0
self._entry_bar = -1
def OnStarted2(self, time):
super(monthly_breakout_strategy, self).OnStarted2(time)
self._monthly_high = 0.0
self._monthly_low = 0.0
self._prev_monthly_high = 0.0
self._prev_monthly_low = 0.0
self._prev_close = 0.0
self._current_week = 0
self._bar_index = 0
self._entry_bar = -1
dummy1 = ExponentialMovingAverage()
dummy1.Length = 10
dummy2 = ExponentialMovingAverage()
dummy2.Length = 20
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(dummy1, dummy2, self.OnProcess).Start()
def _get_week(self, dt):
day_of_year = dt.DayOfYear
return (day_of_year - 1) // 7
def OnProcess(self, candle, d1, d2):
if candle.State != CandleStates.Finished:
return
self._bar_index += 1
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
week = self._get_week(candle.OpenTime)
if week != self._current_week:
if self._current_week != 0:
self._prev_monthly_high = self._monthly_high
self._prev_monthly_low = self._monthly_low
self._monthly_high = high
self._monthly_low = low
self._current_week = week
else:
if high > self._monthly_high:
self._monthly_high = high
if low < self._monthly_low:
self._monthly_low = low
if self._prev_monthly_high <= 0.0 or self._prev_monthly_low <= 0.0:
self._prev_close = close
return
cross_above_high = self._prev_close <= self._prev_monthly_high and close > self._prev_monthly_high
cross_below_high = self._prev_close >= self._prev_monthly_high and close < self._prev_monthly_high
cross_above_low = self._prev_close <= self._prev_monthly_low and close > self._prev_monthly_low
cross_below_low = self._prev_close >= self._prev_monthly_low and close < self._prev_monthly_low
hp = self._holding_period.Value
if self.Position != 0 and self._entry_bar >= 0 and self._bar_index >= self._entry_bar + hp:
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_bar = -1
opt = self._entry_option.Value
if opt == 0 and cross_above_high and self.Position <= 0:
self.BuyMarket()
self._entry_bar = self._bar_index
elif opt == 1 and cross_below_high and self.Position >= 0:
self.SellMarket()
self._entry_bar = self._bar_index
elif opt == 2 and cross_above_low and self.Position <= 0:
self.BuyMarket()
self._entry_bar = self._bar_index
elif opt == 3 and cross_below_low and self.Position >= 0:
self.SellMarket()
self._entry_bar = self._bar_index
self._prev_close = close
def CreateClone(self):
return monthly_breakout_strategy()