Стратегия Figurelli Series
Обзор
Эта стратегия является конверсией советника MetaTrader5 "Exp_FigurelliSeries" в StockSharp. Используется индикатор Figurelli Series, измеряющий разницу между количеством скользящих средних выше и ниже текущей цены. Сделки совершаются один раз в день в заданное время начала, а все позиции закрываются во время остановки.
Индикатор
Индикатор строит цепочку экспоненциальных скользящих средних, начиная со значения Start Period и увеличивая период на Step для каждой из Total средних. На каждом баре считается сколько средних выше цены закрытия и сколько ниже. Значение индикатора равно bids - asks, где bids — количество средних ниже цены, а asks — количество средних выше цены.
Правила торговли
- В момент
Start Hour:Start Minute:- Покупка, если значение индикатора положительное и нет длинной позиции.
- Продажа, если значение индикатора отрицательное и нет короткой позиции.
- В момент
Stop Hour:Stop Minuteи позже все открытые позиции закрываются. - Используются только завершенные свечи выбранного типа
CandleType.
Параметры
StartPeriod– начальный период скользящих средних.Step– приращение периода между средними.Total– количество скользящих средних.StartHour/StartMinute– время возможного входа.StopHour/StopMinute– время выхода из всех позиций.CandleType– тип свечей для расчета.
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>
/// Strategy based on the Figurelli Series indicator.
/// Opens positions at a specified start time when the indicator is positive or negative.
/// Closes all positions at the stop time.
/// </summary>
public class FigurelliSeriesStrategy : Strategy
{
private readonly StrategyParam<int> _startPeriod;
private readonly StrategyParam<int> _step;
private readonly StrategyParam<int> _total;
private readonly StrategyParam<int> _startHour;
private readonly StrategyParam<int> _startMinute;
private readonly StrategyParam<int> _stopHour;
private readonly StrategyParam<int> _stopMinute;
private readonly StrategyParam<DataType> _candleType;
private int _lastSign;
/// <summary>Initial period for moving averages.</summary>
public int StartPeriod { get => _startPeriod.Value; set => _startPeriod.Value = value; }
/// <summary>Step between moving average periods.</summary>
public int Step { get => _step.Value; set => _step.Value = value; }
/// <summary>Number of moving averages.</summary>
public int Total { get => _total.Value; set => _total.Value = value; }
/// <summary>Hour to start trading.</summary>
public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }
/// <summary>Minute to start trading.</summary>
public int StartMinute { get => _startMinute.Value; set => _startMinute.Value = value; }
/// <summary>Hour to stop trading.</summary>
public int StopHour { get => _stopHour.Value; set => _stopHour.Value = value; }
/// <summary>Minute to stop trading.</summary>
public int StopMinute { get => _stopMinute.Value; set => _stopMinute.Value = value; }
/// <summary>Candle type to use.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Constructor.</summary>
public FigurelliSeriesStrategy()
{
_startPeriod = Param(nameof(StartPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Start Period", "Initial period for moving averages", "Indicator")
.SetOptimize(6, 18, 6);
_step = Param(nameof(Step), 2)
.SetGreaterThanZero()
.SetDisplay("Step", "Step between moving average periods", "Indicator")
.SetOptimize(6, 12, 2);
_total = Param(nameof(Total), 6)
.SetGreaterThanZero()
.SetDisplay("Total", "Number of moving averages", "Indicator")
.SetOptimize(12, 48, 12);
_startHour = Param(nameof(StartHour), 8)
.SetDisplay("Start Hour", "Hour to start trading", "Time")
.SetOptimize(0, 23, 1);
_startMinute = Param(nameof(StartMinute), 0)
.SetDisplay("Start Minute", "Minute to start trading", "Time")
.SetOptimize(0, 59, 1);
_stopHour = Param(nameof(StopHour), 23)
.SetDisplay("Stop Hour", "Hour to stop trading", "Time")
.SetOptimize(0, 23, 1);
_stopMinute = Param(nameof(StopMinute), 59)
.SetDisplay("Stop Minute", "Minute to stop trading", "Time")
.SetOptimize(0, 59, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_lastSign = 0;
var figurelli = new FigurelliSeriesIndicator
{
StartPeriod = StartPeriod,
Step = Step,
Total = Total
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(figurelli, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, figurelli);
DrawOwnTrades(area);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_lastSign = 0;
}
private void ProcessCandle(ICandleMessage candle, decimal value)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sign = Math.Sign(value);
if (sign == 0 || sign == _lastSign)
return;
if (sign > 0 && Position <= 0)
BuyMarket();
else if (sign < 0 && Position >= 0)
SellMarket();
_lastSign = sign;
}
private class FigurelliSeriesIndicator : BaseIndicator
{
public int StartPeriod { get; set; }
public int Step { get; set; }
public int Total { get; set; }
private ExponentialMovingAverage[] _averages = [];
public override void Reset()
{
base.Reset();
foreach (var ma in _averages)
ma.Reset();
}
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var candle = input.ToCandle();
var price = candle.ClosePrice;
if (_averages.Length == 0)
{
_averages = new ExponentialMovingAverage[Total];
for (var i = 0; i < Total; i++)
_averages[i] = new ExponentialMovingAverage { Length = StartPeriod + (Step * i) };
}
var bids = 0;
var asks = 0;
var allFormed = true;
foreach (var ma in _averages)
{
var maValue = ma.Process(price, input.Time, input.IsFinal).GetValue<decimal>();
if (!ma.IsFormed)
{
allFormed = false;
continue;
}
if (price > maValue)
bids++;
else if (price < maValue)
asks++;
}
if (allFormed)
IsFormed = true;
var result = bids - asks;
return new DecimalIndicatorValue(this, result, input.Time);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
import math
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class figurelli_series_strategy(Strategy):
"""
FigurelliSeries: Multiple EMA comparison strategy.
Computes N EMAs with increasing periods, counts how many price is above/below.
Trades on sign change of the net score.
"""
def __init__(self):
super(figurelli_series_strategy, self).__init__()
self._start_period = self.Param("StartPeriod", 3) \
.SetDisplay("Start Period", "Initial period for moving averages", "Indicator")
self._step = self.Param("Step", 2) \
.SetDisplay("Step", "Step between moving average periods", "Indicator")
self._total = self.Param("Total", 6) \
.SetDisplay("Total", "Number of moving averages", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles for calculations", "General")
self._last_sign = 0
self._emas = []
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(figurelli_series_strategy, self).OnReseted()
self._last_sign = 0
self._emas = []
def OnStarted2(self, time):
super(figurelli_series_strategy, self).OnStarted2(time)
self._last_sign = 0
total = self._total.Value
start = self._start_period.Value
step = self._step.Value
self._emas = []
for i in range(total):
ema = ExponentialMovingAverage()
ema.Length = start + step * i
self._emas.append(ema)
self.Indicators.Add(ema)
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
price = float(candle.ClosePrice)
bids = 0
asks = 0
all_formed = True
for ema in self._emas:
result = process_float(ema, candle.ClosePrice, candle.OpenTime, True)
if not ema.IsFormed:
all_formed = False
continue
ma_val = float(result)
if price > ma_val:
bids += 1
elif price < ma_val:
asks += 1
if not all_formed:
return
value = bids - asks
sign = 1 if value > 0 else (-1 if value < 0 else 0)
if sign == 0 or sign == self._last_sign:
return
if sign > 0 and self.Position <= 0:
self.BuyMarket()
elif sign < 0 and self.Position >= 0:
self.SellMarket()
self._last_sign = sign
def CreateClone(self):
return figurelli_series_strategy()