Cycle Biologique策略
Cycle Biologique策略基于正弦周期。当周期上穿零轴时做多,周期下穿零轴时平仓。
细节
- 入场条件:周期上穿零轴。
- 出场条件:周期下穿零轴。
- 默认参数:
CycleLength= 30Amplitude= 1.0Offset= 0
- 过滤器:
- 类型:Cycle
- 方向:多头
- 指标:正弦波
- 止损:否
- 复杂度:低
- 时间框架:任意
- 季节性:否
- 神经网络:否
- 背离:否
- 风险等级:中等
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Cycle Biologique strategy.
/// The cycle is Amplitude * sin(2 * pi * (bar index + Offset) / CycleLength).
/// A cross above zero opens a long and a cross below zero closes it.
/// </summary>
public class CycleBiologiqueStrategy : Strategy
{
private readonly StrategyParam<int> _cycleLength;
private readonly StrategyParam<decimal> _amplitude;
private readonly StrategyParam<int> _offset;
private readonly StrategyParam<DataType> _candleType;
private int _barIndex;
private double? _prevCycle;
/// <summary>
/// Bars in one full cycle.
/// </summary>
public int CycleLength
{
get => _cycleLength.Value;
set => _cycleLength.Value = value;
}
/// <summary>
/// Cycle amplitude.
/// </summary>
public decimal Amplitude
{
get => _amplitude.Value;
set => _amplitude.Value = value;
}
/// <summary>
/// Phase shift of the cycle in bars.
/// </summary>
public int Offset
{
get => _offset.Value;
set => _offset.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public CycleBiologiqueStrategy()
{
_cycleLength = Param(nameof(CycleLength), 30)
.SetGreaterThanZero()
.SetDisplay("Cycle Length", "Bars in one full cycle", "Cycle");
_amplitude = Param(nameof(Amplitude), 1.0m)
.SetGreaterThanZero()
.SetDisplay("Amplitude", "Cycle amplitude", "Cycle");
_offset = Param(nameof(Offset), 0)
.SetDisplay("Offset", "Phase shift of the cycle in bars", "Cycle");
_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();
_barIndex = 0;
_prevCycle = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_barIndex = 0;
_prevCycle = null;
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 cycle = (double)Amplitude * Math.Sin(2 * Math.PI * (_barIndex + Offset) / CycleLength);
_barIndex++;
var prev = _prevCycle;
_prevCycle = cycle;
if (prev is not double prevCycle)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (prevCycle <= 0 && cycle > 0 && Position == 0)
BuyMarket();
else if (prevCycle >= 0 && cycle < 0 && Position > 0)
SellMarket(Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
import math
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class cycle_biologique_strategy(Strategy):
"""
Cycle Biologique strategy.
The cycle is Amplitude * sin(2 * pi * (bar index + Offset) / CycleLength).
A cross above zero opens a long and a cross below zero closes it.
"""
def __init__(self):
super(cycle_biologique_strategy, self).__init__()
self._cycle_length = self.Param("CycleLength", 30) \
.SetGreaterThanZero() \
.SetDisplay("Cycle Length", "Bars in one full cycle", "Cycle")
self._amplitude = self.Param("Amplitude", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Amplitude", "Cycle amplitude", "Cycle")
self._offset = self.Param("Offset", 0) \
.SetDisplay("Offset", "Phase shift of the cycle in bars", "Cycle")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._bar_index = 0
self._prev_cycle = None
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(cycle_biologique_strategy, self).OnReseted()
self._bar_index = 0
self._prev_cycle = None
def OnStarted2(self, time):
super(cycle_biologique_strategy, self).OnStarted2(time)
self._bar_index = 0
self._prev_cycle = None
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
cycle = float(self._amplitude.Value) * math.sin(
2 * math.pi * (self._bar_index + self._offset.Value) / self._cycle_length.Value)
self._bar_index += 1
prev = self._prev_cycle
self._prev_cycle = cycle
if prev is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
if prev <= 0 and cycle > 0 and self.Position == 0:
self.BuyMarket()
elif prev >= 0 and cycle < 0 and self.Position > 0:
self.SellMarket(self.Position)
def CreateClone(self):
return cycle_biologique_strategy()