Стратегия AFL Winner Sign
Стратегия основана на индикаторе AFL WinnerSign. Она использует двойное сглаживание стохастического осциллятора, рассчитанного по цене, взвешенной на объём. Длинная позиция открывается при пересечении быстрой линии стохастика выше медленной, короткая позиция — при пересечении быстрой линии ниже медленной.
Подробности
- Условия входа:
- Лонг: быстрая %K пересекает медленную %D снизу вверх
- Шорт: быстрая %K пересекает медленную %D сверху вниз
- Длинные/Короткие: Оба направления
- Условия выхода: Обратный сигнал закрывает или разворачивает позицию
- Стопы: Процентные через
StartProtection - Значения по умолчанию:
Period= 10KPeriod= 5DPeriod= 5CandleType=TimeSpan.FromMinutes(5).TimeFrame()
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: Стохастический осциллятор
- Стопы: Да
- Сложность: Базовая
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенции: Нет
- Уровень риска: Средний
using System;
using System.Collections.Generic;
using Ecng.Common;
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 a smoothed momentum crossover.
/// </summary>
public class AflWinnerSignStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<int> _kPeriod;
private readonly StrategyParam<int> _dPeriod;
private readonly StrategyParam<DataType> _candleType;
private readonly ExponentialMovingAverage _fast = new();
private readonly ExponentialMovingAverage _slow = new();
private decimal _prevK;
private decimal _prevD;
private bool _isInitialized;
/// <summary>
/// Base period for the oscillator.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Smoothing period for the fast line.
/// </summary>
public int KPeriod
{
get => _kPeriod.Value;
set => _kPeriod.Value = value;
}
/// <summary>
/// Smoothing period for the slow line.
/// </summary>
public int DPeriod
{
get => _dPeriod.Value;
set => _dPeriod.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="AflWinnerSignStrategy"/>.
/// </summary>
public AflWinnerSignStrategy()
{
_period = Param(nameof(Period), 10)
.SetGreaterThanZero()
.SetDisplay("Stoch Period", "Base period for oscillator calculation", "AFL WinnerSign")
.SetOptimize(5, 20, 1);
_kPeriod = Param(nameof(KPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("%K Period", "Smoothing period for %K line", "AFL WinnerSign")
.SetOptimize(3, 10, 1);
_dPeriod = Param(nameof(DPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("%D Period", "Smoothing period for %D line", "AFL WinnerSign")
.SetOptimize(3, 10, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).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();
_fast.Length = KPeriod;
_slow.Length = DPeriod;
_fast.Reset();
_slow.Reset();
_prevK = 0m;
_prevD = 0m;
_isInitialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast.Length = KPeriod;
_slow.Length = DPeriod;
var rsi = new RelativeStrengthIndex { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ProcessCandle).Start();
StartProtection(new Unit(2, UnitTypes.Percent), new Unit(2, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal momentum)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var k = _fast.Process(new DecimalIndicatorValue(_fast, momentum, candle.OpenTime) { IsFinal = true }).ToDecimal();
var d = _slow.Process(new DecimalIndicatorValue(_slow, k, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_fast.IsFormed || !_slow.IsFormed)
return;
if (!_isInitialized)
{
_prevK = k;
_prevD = d;
_isInitialized = true;
return;
}
if (_prevK <= _prevD && k > d && k < 35m && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (_prevK >= _prevD && k < d && k > 65m && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
_prevK = k;
_prevD = d;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import (
ExponentialMovingAverage, RelativeStrengthIndex
)
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class afl_winner_sign_strategy(Strategy):
def __init__(self):
super(afl_winner_sign_strategy, self).__init__()
self._period = self.Param("Period", 10) \
.SetDisplay("Stoch Period", "Base period for oscillator calculation", "AFL WinnerSign")
self._k_period = self.Param("KPeriod", 5) \
.SetDisplay("%K Period", "Smoothing period for %K line", "AFL WinnerSign")
self._d_period = self.Param("DPeriod", 5) \
.SetDisplay("%D Period", "Smoothing period for %D line", "AFL WinnerSign")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._fast = None
self._slow = None
self._prev_k = 0.0
self._prev_d = 0.0
self._is_initialized = False
@property
def Period(self):
return self._period.Value
@Period.setter
def Period(self, value):
self._period.Value = value
@property
def KPeriod(self):
return self._k_period.Value
@KPeriod.setter
def KPeriod(self, value):
self._k_period.Value = value
@property
def DPeriod(self):
return self._d_period.Value
@DPeriod.setter
def DPeriod(self, value):
self._d_period.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(afl_winner_sign_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.KPeriod
self._slow = ExponentialMovingAverage()
self._slow.Length = self.DPeriod
rsi = RelativeStrengthIndex()
rsi.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self.ProcessCandle).Start()
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(2, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, momentum):
if candle.State != CandleStates.Finished:
return
k_result = process_float(self._fast, float(momentum), candle.OpenTime, True)
k = float(k_result)
d_result = process_float(self._slow, k, candle.OpenTime, True)
d = float(d_result)
if not self._fast.IsFormed or not self._slow.IsFormed:
return
if not self._is_initialized:
self._prev_k = k
self._prev_d = d
self._is_initialized = True
return
if self._prev_k <= self._prev_d and k > d and k < 35.0 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self._prev_k >= self._prev_d and k < d and k > 65.0 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._prev_k = k
self._prev_d = d
def OnReseted(self):
super(afl_winner_sign_strategy, self).OnReseted()
if self._fast is not None:
self._fast.Length = self.KPeriod
self._fast.Reset()
if self._slow is not None:
self._slow.Length = self.DPeriod
self._slow.Reset()
self._prev_k = 0.0
self._prev_d = 0.0
self._is_initialized = False
def CreateClone(self):
return afl_winner_sign_strategy()