Berlin Candles 策略
该策略使用基于平滑 Heikin Ashi 值的 Berlin 自定义蜡烛。当看涨的 Berlin 蜡烛收盘价高于 Donchian 基线时做多;当看跌的 Berlin 蜡烛收盘价低于基线时做空。
细节
- 入场条件:
- 多头:Berlin 收盘价 > Berlin 开盘价 且 Berlin 收盘价 > baseline。
- 空头:Berlin 收盘价 < Berlin 开盘价 且 Berlin 收盘价 < baseline。
- 方向:双向
- 止损:默认无
- 默认参数:
Smoothing= 1BaselinePeriod= 26CandleType= TimeSpan.FromMinutes(5).TimeFrame()
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>
/// Strategy based on Berlin candles with Donchian baseline.
/// </summary>
public class BerlinCandlesStrategy : Strategy
{
private readonly StrategyParam<int> _smoothing;
private readonly StrategyParam<int> _baselinePeriod;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema;
private DonchianChannels _donchian;
private decimal _prevEma;
private bool _isInitialized;
private int _cooldown;
public int Smoothing
{
get => _smoothing.Value;
set => _smoothing.Value = value;
}
public int BaselinePeriod
{
get => _baselinePeriod.Value;
set => _baselinePeriod.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public BerlinCandlesStrategy()
{
_smoothing = Param(nameof(Smoothing), 1)
.SetDisplay("Smoothing", "EMA smoothing for Berlin open", "Berlin")
.SetOptimize(1, 10, 1);
_baselinePeriod = Param(nameof(BaselinePeriod), 26)
.SetDisplay("Baseline Period", "Donchian baseline period", "Berlin")
.SetOptimize(10, 50, 5);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevEma = default;
_isInitialized = false;
_cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = Smoothing + 1 };
_donchian = new DonchianChannels { Length = BaselinePeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _donchian);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
// Manually process Donchian
var donchianResult = _donchian.Process(candle);
if (donchianResult is not DonchianChannelsValue dv || dv.Middle is not decimal middleBand)
return;
if (!_isInitialized)
{
_prevEma = emaValue;
_isInitialized = true;
return;
}
var openExpr = _prevEma;
var closeExpr = candle.ClosePrice;
decimal openValue;
decimal closeValue;
if (openExpr > closeExpr)
{
openValue = Math.Min(openExpr, candle.HighPrice);
closeValue = Math.Max(closeExpr, candle.LowPrice);
}
else
{
openValue = Math.Max(openExpr, candle.LowPrice);
closeValue = Math.Min(closeExpr, candle.HighPrice);
}
var baseline = middleBand;
if (_cooldown > 0)
{
_cooldown--;
_prevEma = emaValue;
return;
}
if (closeValue > openValue && closeValue > baseline && Position <= 0)
{
BuyMarket();
_cooldown = 100;
}
else if (closeValue < openValue && closeValue < baseline && Position >= 0)
{
SellMarket();
_cooldown = 100;
}
_prevEma = emaValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.BusinessEntities")
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, DonchianChannels, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class berlin_candles_strategy(Strategy):
def __init__(self):
super(berlin_candles_strategy, self).__init__()
self._smoothing = self.Param("Smoothing", 1) \
.SetDisplay("Smoothing", "EMA smoothing for Berlin open", "Berlin")
self._baseline_period = self.Param("BaselinePeriod", 26) \
.SetDisplay("Baseline Period", "Donchian baseline period", "Berlin")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_ema = 0.0
self._is_initialized = False
self._cooldown = 0
@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(berlin_candles_strategy, self).OnReseted()
self._prev_ema = 0.0
self._is_initialized = False
self._cooldown = 0
def OnStarted2(self, time):
super(berlin_candles_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self._smoothing.Value + 1
self._donchian = DonchianChannels()
self._donchian.Length = self._baseline_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._donchian)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val):
if candle.State != CandleStates.Finished:
return
ema_v = float(ema_val)
donchian_result = self._donchian.Process(CandleIndicatorValue(self._donchian, candle))
middle = donchian_result.Middle
if middle is None:
return
middle_v = float(middle)
if not self._is_initialized:
self._prev_ema = ema_v
self._is_initialized = True
return
open_expr = self._prev_ema
close_expr = float(candle.ClosePrice)
high_v = float(candle.HighPrice)
low_v = float(candle.LowPrice)
if open_expr > close_expr:
open_value = min(open_expr, high_v)
close_value = max(close_expr, low_v)
else:
open_value = max(open_expr, low_v)
close_value = min(close_expr, high_v)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_ema = ema_v
return
if close_value > open_value and close_value > middle_v and self.Position <= 0:
self.BuyMarket()
self._cooldown = 100
elif close_value < open_value and close_value < middle_v and self.Position >= 0:
self.SellMarket()
self._cooldown = 100
self._prev_ema = ema_v
def CreateClone(self):
return berlin_candles_strategy()