GO Strategy
This strategy calculates a composite GO value based on exponential moving averages (EMA) of open, high, low, and close prices multiplied by volume. Trading decisions are made according to the sign and level of the GO value.
Formula
GO = ((C - O) + (H - O) + (L - O) + (C - L) + (C - H)) * V
Where:
C,O,H,L– EMA values of Close, Open, High, and Low prices.V– volume of the processed candle.
Trading Rules
- Open Long: GO >
OpenLevel - Open Short: GO <
-OpenLevel - Close Long: GO < (
OpenLevel-CloseLevelDiff) - Close Short: GO > -(
OpenLevel-CloseLevelDiff)
Parameters
| Name | Description |
|---|---|
MaPeriod |
EMA period for price smoothing. |
OpenLevel |
GO level to trigger new positions. |
CloseLevelDiff |
Difference between open and close levels. |
ShowGo |
Whether to log GO values. |
CandleType |
Type of candles used for processing. |
The strategy operates on finished candles and uses market orders for position management.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// GO strategy: EMA-smoothed OHLC composite multiplied by candle volume.
/// </summary>
public class GoStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<decimal> _openLevel;
private readonly StrategyParam<decimal> _closeLevelDiff;
private readonly StrategyParam<bool> _showGo;
private readonly StrategyParam<DataType> _candleType;
private decimal? _openEma;
private decimal? _highEma;
private decimal? _lowEma;
private decimal? _closeEma;
private int _samples;
public int MaPeriod { get => _maPeriod.Value; set => _maPeriod.Value = value; }
public decimal OpenLevel { get => _openLevel.Value; set => _openLevel.Value = value; }
public decimal CloseLevelDiff { get => _closeLevelDiff.Value; set => _closeLevelDiff.Value = value; }
public bool ShowGo { get => _showGo.Value; set => _showGo.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public GoStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("MA Period", "EMA period for O/H/L/C smoothing.", "GO");
_openLevel = Param(nameof(OpenLevel), 0m)
.SetNotNegative()
.SetDisplay("Open Level", "Absolute GO threshold required for entry.", "GO");
_closeLevelDiff = Param(nameof(CloseLevelDiff), 0m)
.SetNotNegative()
.SetDisplay("Close Level Diff", "Distance between entry and exit GO thresholds.", "GO");
_showGo = Param(nameof(ShowGo), false)
.SetDisplay("Show GO", "Log computed GO values.", "GO");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle type.", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_openEma = null;
_highEma = null;
_lowEma = null;
_closeEma = null;
_samples = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType).Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_samples++;
_openEma = Ema(_openEma, candle.OpenPrice, MaPeriod);
_highEma = Ema(_highEma, candle.HighPrice, MaPeriod);
_lowEma = Ema(_lowEma, candle.LowPrice, MaPeriod);
_closeEma = Ema(_closeEma, candle.ClosePrice, MaPeriod);
if (_samples < MaPeriod)
return;
var go = CalculateGo(_openEma.Value, _highEma.Value, _lowEma.Value, _closeEma.Value, candle.TotalVolume);
if (ShowGo)
LogInfo("GO={0}", go);
var closeLevel = OpenLevel - CloseLevelDiff;
var position = Position;
if (position > 0m && go < closeLevel)
{
SellMarket(Math.Abs(position));
position = 0m;
}
else if (position < 0m && go > -closeLevel)
{
BuyMarket(Math.Abs(position));
position = 0m;
}
if (position != 0m)
return;
if (go > OpenLevel)
BuyMarket();
else if (go < -OpenLevel)
SellMarket();
}
internal static decimal CalculateGo(decimal openEma, decimal highEma, decimal lowEma, decimal closeEma, decimal volume)
=> ((closeEma - openEma) +
(highEma - openEma) +
(lowEma - openEma) +
(closeEma - lowEma) +
(closeEma - highEma)) * volume;
private static decimal Ema(decimal? previous, decimal value, int period)
{
if (previous is null)
return value;
var alpha = 2m / (period + 1m);
return previous.Value + alpha * (value - previous.Value);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class go_strategy(Strategy):
def __init__(self):
super(go_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 14).SetGreaterThanZero()
self._open_level = self.Param("OpenLevel", 0.0).SetNotNegative()
self._close_level_diff = self.Param("CloseLevelDiff", 0.0).SetNotNegative()
self._show_go = self.Param("ShowGo", False)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._open_ema = None
self._high_ema = None
self._low_ema = None
self._close_ema = None
self._samples = 0
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnReseted(self):
super(go_strategy, self).OnReseted()
self._open_ema = None
self._high_ema = None
self._low_ema = None
self._close_ema = None
self._samples = 0
def OnStarted2(self, time):
super(go_strategy, self).OnStarted2(time)
self.SubscribeCandles(self._candle_type.Value).Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
period = int(self._ma_period.Value)
self._samples += 1
self._open_ema = self._ema(self._open_ema, float(candle.OpenPrice), period)
self._high_ema = self._ema(self._high_ema, float(candle.HighPrice), period)
self._low_ema = self._ema(self._low_ema, float(candle.LowPrice), period)
self._close_ema = self._ema(self._close_ema, float(candle.ClosePrice), period)
if self._samples < period:
return
go = self.calculate_go(
self._open_ema, self._high_ema, self._low_ema, self._close_ema, float(candle.TotalVolume))
if bool(self._show_go.Value):
self.LogInfo("GO={0}", go)
open_level = float(self._open_level.Value)
close_level = open_level - float(self._close_level_diff.Value)
position = self.Position
if position > 0 and go < close_level:
self.SellMarket(Math.Abs(position))
position = 0
elif position < 0 and go > -close_level:
self.BuyMarket(Math.Abs(position))
position = 0
if position != 0:
return
if go > open_level:
self.BuyMarket()
elif go < -open_level:
self.SellMarket()
@staticmethod
def calculate_go(open_ema, high_ema, low_ema, close_ema, volume):
return ((close_ema - open_ema) +
(high_ema - open_ema) +
(low_ema - open_ema) +
(close_ema - low_ema) +
(close_ema - high_ema)) * volume
@staticmethod
def _ema(previous, value, period):
if previous is None:
return value
alpha = 2.0 / (period + 1.0)
return previous + alpha * (value - previous)
def CreateClone(self):
return go_strategy()