Universal Investor Strategy использует пересечение экспоненциального скользящего среднего (EMA) и линейно-взвешенного скользящего среднего (LWMA) для определения направления рынка. Дополнительно проверяется, что оба средних движутся в одном направлении.
Логика
Покупка: LWMA находится выше EMA и оба средних растут.
Продажа: LWMA находится ниже EMA и оба средних падают.
Выход из покупки: LWMA пересекает EMA сверху вниз.
Выход из продажи: LWMA пересекает EMA снизу вверх.
При включенном коэффициенте уменьшения объем позиции сокращается после подряд идущих убыточных сделок.
Параметры
Имя
Описание
MovingPeriod
Период расчёта EMA и LWMA.
DecreaseFactor
Коэффициент уменьшения объёма после убытков (0 отключает уменьшение).
CandleType
Тип свечей для расчёта.
Volume
Базовый объём сделки из настроек стратегии.
Примечания
Работает только на завершённых свечах.
Используется высокоуровневый API StockSharp с привязкой индикаторов.
Версия на Python отсутствует.
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 EMA and WMA crossover with trend confirmation.
/// </summary>
public class UniversalInvestorStrategy : Strategy
{
private readonly StrategyParam<int> _movingPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevEma;
private decimal _prevLwma;
private bool _hasPrev;
public int MovingPeriod { get => _movingPeriod.Value; set => _movingPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public UniversalInvestorStrategy()
{
_movingPeriod = Param(nameof(MovingPeriod), 23)
.SetGreaterThanZero()
.SetDisplay("Moving Period", "Smoothing period for EMA and WMA", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevEma = 0;
_prevLwma = 0;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ema = new ExponentialMovingAverage { Length = MovingPeriod };
var lwma = new WeightedMovingAverage { Length = MovingPeriod };
SubscribeCandles(CandleType)
.Bind(ema, lwma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal lwmaValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev)
{
_prevEma = emaValue;
_prevLwma = lwmaValue;
_hasPrev = true;
return;
}
var openBuy = lwmaValue > emaValue && lwmaValue > _prevLwma && emaValue > _prevEma;
var openSell = lwmaValue < emaValue && lwmaValue < _prevLwma && emaValue < _prevEma;
var closeBuy = lwmaValue < emaValue;
var closeSell = lwmaValue > emaValue;
if (Position > 0 && closeBuy)
{
SellMarket();
}
else if (Position < 0 && closeSell)
{
BuyMarket();
}
else if (Position == 0)
{
if (openBuy && !closeBuy)
BuyMarket();
else if (openSell && !closeSell)
SellMarket();
}
_prevEma = emaValue;
_prevLwma = lwmaValue;
}
}
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, WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class universal_investor_strategy(Strategy):
def __init__(self):
super(universal_investor_strategy, self).__init__()
self._moving_period = self.Param("MovingPeriod", 23) \
.SetDisplay("Moving Period", "Smoothing period for EMA and WMA", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles for calculations", "General")
self._prev_ema = 0.0
self._prev_lwma = 0.0
self._has_prev = False
@property
def moving_period(self):
return self._moving_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(universal_investor_strategy, self).OnReseted()
self._prev_ema = 0.0
self._prev_lwma = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(universal_investor_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self.moving_period
lwma = WeightedMovingAverage()
lwma.Length = self.moving_period
self.SubscribeCandles(self.candle_type).Bind(ema, lwma, self.process_candle).Start()
def process_candle(self, candle, ema_value, lwma_value):
if candle.State != CandleStates.Finished:
return
ev = float(ema_value)
lv = float(lwma_value)
if not self._has_prev:
self._prev_ema = ev
self._prev_lwma = lv
self._has_prev = True
return
open_buy = lv > ev and lv > self._prev_lwma and ev > self._prev_ema
open_sell = lv < ev and lv < self._prev_lwma and ev < self._prev_ema
close_buy = lv < ev
close_sell = lv > ev
if self.Position > 0 and close_buy:
self.SellMarket()
elif self.Position < 0 and close_sell:
self.BuyMarket()
elif self.Position == 0:
if open_buy and not close_buy:
self.BuyMarket()
elif open_sell and not close_sell:
self.SellMarket()
self._prev_ema = ev
self._prev_lwma = lv
def CreateClone(self):
return universal_investor_strategy()