The Universal Investor Strategy uses the crossover between the Exponential Moving Average (EMA) and the Linear Weighted Moving Average (LWMA) to determine market direction. It confirms trend strength by checking that both averages move in the same direction.
Logic
Buy Entry: LWMA is above EMA and both averages are rising.
Sell Entry: LWMA is below EMA and both averages are falling.
Buy Exit: LWMA crosses below EMA.
Sell Exit: LWMA crosses above EMA.
The strategy reduces position size after consecutive losing trades when the decrease factor is enabled.
Parameters
Name
Description
MovingPeriod
Length for EMA and LWMA calculations.
DecreaseFactor
Lot reduction factor after losses (0 disables reduction).
CandleType
Candle data type for calculations.
Volume
Base trade volume from the strategy settings.
Notes
Works on finished candles only.
Uses high-level StockSharp API with indicator binding.
No Python version is provided.
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()