namespace StockSharp.Samples.Strategies;
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using StockSharp.Algo;
public class MamyExpertStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<MaCalculationTypes> _maType;
private readonly StrategyParam<decimal> _tradeVolume;
private DecimalLengthIndicator _closeMa;
private DecimalLengthIndicator _openMa;
private DecimalLengthIndicator _weightedPriceMa;
private decimal? _previousCloseMa;
private decimal? _previousOpenMa;
private decimal? _previousWeightedMa;
private decimal? _previousOpenSignal;
private decimal? _previousCloseSignal;
public MamyExpertStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle type", "Primary timeframe used for price aggregation.", "General");
_maPeriod = Param(nameof(MaPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("MA period", "Length applied to all moving averages.", "Indicator");
_maType = Param(nameof(MaType), MaCalculationTypes.Weighted)
.SetDisplay("MA method", "Averaging algorithm applied to open/close/weighted prices.", "Indicator");
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Volume", "Order volume submitted for entries.", "Trading");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
public MaCalculationTypes MaType
{
get => _maType.Value;
set => _maType.Value = value;
}
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_previousCloseMa = null;
_previousOpenMa = null;
_previousWeightedMa = null;
_previousOpenSignal = null;
_previousCloseSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = TradeVolume;
StartProtection(null, null);
_closeMa = CreateMovingAverage(MaType, MaPeriod);
_openMa = CreateMovingAverage(MaType, MaPeriod);
_weightedPriceMa = CreateMovingAverage(MaType, MaPeriod);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _closeMa);
DrawIndicator(area, _openMa);
DrawIndicator(area, _weightedPriceMa);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_closeMa == null || _openMa == null || _weightedPriceMa == null)
return;
var closeMaResult = _closeMa.Process(new DecimalIndicatorValue(_closeMa, candle.ClosePrice, candle.OpenTime) { IsFinal = true });
var openMaResult = _openMa.Process(new DecimalIndicatorValue(_openMa, candle.OpenPrice, candle.OpenTime) { IsFinal = true });
var weightedPrice = CalculateWeightedPrice(candle);
var weightedMaResult = _weightedPriceMa.Process(new DecimalIndicatorValue(_weightedPriceMa, weightedPrice, candle.OpenTime) { IsFinal = true });
if (closeMaResult.IsEmpty || openMaResult.IsEmpty || weightedMaResult.IsEmpty)
return;
var closeMaValue = closeMaResult.ToDecimal();
var openMaValue = openMaResult.ToDecimal();
var weightedMaValue = weightedMaResult.ToDecimal();
var previousCloseMa = _previousCloseMa;
var previousOpenMa = _previousOpenMa;
var previousWeightedMa = _previousWeightedMa;
var previousOpenSignal = _previousOpenSignal;
var previousCloseSignal = _previousCloseSignal;
_previousCloseMa = closeMaValue;
_previousOpenMa = openMaValue;
_previousWeightedMa = weightedMaValue;
if (!_closeMa.IsFormed || !_openMa.IsFormed || !_weightedPriceMa.IsFormed)
{
_previousOpenSignal = null;
_previousCloseSignal = null;
return;
}
var closeSignal = closeMaValue - weightedMaValue;
var openSignal = 0m;
if (previousCloseMa.HasValue && previousOpenMa.HasValue && previousWeightedMa.HasValue && previousCloseSignal.HasValue)
{
var closeDecreasing = closeMaValue < previousCloseMa.Value &&
weightedMaValue < previousWeightedMa.Value &&
closeMaValue < weightedMaValue &&
weightedMaValue < openMaValue &&
previousWeightedMa.Value < previousOpenMa.Value &&
closeSignal <= previousCloseSignal.Value;
var closeIncreasing = closeMaValue > previousCloseMa.Value &&
weightedMaValue > previousWeightedMa.Value &&
closeMaValue > weightedMaValue &&
weightedMaValue > openMaValue &&
previousWeightedMa.Value > previousOpenMa.Value &&
closeSignal >= previousCloseSignal.Value;
if (closeDecreasing || closeIncreasing)
openSignal = (weightedMaValue - openMaValue) + (closeMaValue - weightedMaValue);
}
if (previousOpenSignal.HasValue && previousCloseSignal.HasValue &&
openSignal >= 0m &&
openSignal > previousOpenSignal.Value &&
closeSignal < 0m &&
previousCloseSignal.Value >= 0m)
{
closeSignal = 0m;
}
var hasPreviousOpenSignal = previousOpenSignal.HasValue;
var hasPreviousCloseSignal = previousCloseSignal.HasValue;
var openBuy = hasPreviousOpenSignal && openSignal > 0m && previousOpenSignal.Value <= 0m;
var openSell = hasPreviousOpenSignal && openSignal < 0m && previousOpenSignal.Value >= 0m;
var closeBuy = hasPreviousCloseSignal && closeSignal < 0m && previousCloseSignal.Value >= 0m;
var closeSell = hasPreviousCloseSignal && closeSignal > 0m && previousCloseSignal.Value <= 0m;
_previousOpenSignal = openSignal;
_previousCloseSignal = closeSignal;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (TradeVolume <= 0m)
return;
var longPosition = Position > 0m ? Position : 0m;
var shortPosition = Position < 0m ? -Position : 0m;
if (longPosition > 0m)
{
if (closeBuy)
SellMarket(longPosition);
}
else if (shortPosition > 0m)
{
if (closeSell)
BuyMarket(shortPosition);
}
else
{
if (openBuy)
BuyMarket(TradeVolume);
else if (openSell)
SellMarket(TradeVolume);
}
}
private static decimal CalculateWeightedPrice(ICandleMessage candle)
{
return (candle.HighPrice + candle.LowPrice + candle.ClosePrice * 2m) / 4m;
}
private static DecimalLengthIndicator CreateMovingAverage(MaCalculationTypes type, int length)
{
return type switch
{
MaCalculationTypes.Simple => new SMA { Length = length },
MaCalculationTypes.Exponential => new EMA { Length = length },
MaCalculationTypes.Smoothed => new SmoothedMovingAverage { Length = length },
_ => new WeightedMovingAverage { Length = length },
};
}
public enum MaCalculationTypes
{
Simple,
Exponential,
Smoothed,
Weighted
}
}
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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class mamy_expert_strategy(Strategy):
"""
MAMy Expert: weighted MA on close/open/weighted-price crossover signals.
"""
def __init__(self):
super(mamy_expert_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 3).SetDisplay("MA Period", "MA length", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Candles", "General")
self._prev_close_ma = None
self._prev_open_ma = None
self._prev_weighted_ma = None
self._prev_open_signal = None
self._prev_close_signal = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(mamy_expert_strategy, self).OnReseted()
self._prev_close_ma = None
self._prev_open_ma = None
self._prev_weighted_ma = None
self._prev_open_signal = None
self._prev_close_signal = None
self._close_ma = None
self._open_ma = None
self._weighted_ma = None
def OnStarted2(self, time):
super(mamy_expert_strategy, self).OnStarted2(time)
self._close_ma = WeightedMovingAverage()
self._close_ma.Length = self._ma_period.Value
self._open_ma = WeightedMovingAverage()
self._open_ma.Length = self._ma_period.Value
self._weighted_ma = WeightedMovingAverage()
self._weighted_ma.Length = self._ma_period.Value
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
if self._close_ma is None:
return
from System import Decimal
close_result = process_float(self._close_ma, candle.ClosePrice, candle.OpenTime, True)
open_result = process_float(self._open_ma, candle.OpenPrice, candle.OpenTime, True)
weighted_price = (candle.HighPrice + candle.LowPrice + candle.ClosePrice * Decimal(2)) / Decimal(4)
weighted_result = process_float(self._weighted_ma, weighted_price, candle.OpenTime, True)
if close_result.IsEmpty or open_result.IsEmpty or weighted_result.IsEmpty:
return
close_ma_val = float(close_result)
open_ma_val = float(open_result)
weighted_ma_val = float(weighted_result)
prev_close_ma = self._prev_close_ma
prev_open_ma = self._prev_open_ma
prev_weighted_ma = self._prev_weighted_ma
prev_open_signal = self._prev_open_signal
prev_close_signal = self._prev_close_signal
self._prev_close_ma = close_ma_val
self._prev_open_ma = open_ma_val
self._prev_weighted_ma = weighted_ma_val
if not self._close_ma.IsFormed or not self._open_ma.IsFormed or not self._weighted_ma.IsFormed:
self._prev_open_signal = None
self._prev_close_signal = None
return
close_signal = close_ma_val - weighted_ma_val
open_signal = 0.0
if prev_close_ma is not None and prev_open_ma is not None and prev_weighted_ma is not None and prev_close_signal is not None:
close_decreasing = (close_ma_val < prev_close_ma and weighted_ma_val < prev_weighted_ma and
close_ma_val < weighted_ma_val and weighted_ma_val < open_ma_val and
prev_weighted_ma < prev_open_ma and close_signal <= prev_close_signal)
close_increasing = (close_ma_val > prev_close_ma and weighted_ma_val > prev_weighted_ma and
close_ma_val > weighted_ma_val and weighted_ma_val > open_ma_val and
prev_weighted_ma > prev_open_ma and close_signal >= prev_close_signal)
if close_decreasing or close_increasing:
open_signal = (weighted_ma_val - open_ma_val) + (close_ma_val - weighted_ma_val)
if (prev_open_signal is not None and prev_close_signal is not None and
open_signal >= 0.0 and open_signal > prev_open_signal and
close_signal < 0.0 and prev_close_signal >= 0.0):
close_signal = 0.0
has_prev_open = prev_open_signal is not None
has_prev_close = prev_close_signal is not None
open_buy = has_prev_open and open_signal > 0.0 and prev_open_signal <= 0.0
open_sell = has_prev_open and open_signal < 0.0 and prev_open_signal >= 0.0
close_buy = has_prev_close and close_signal < 0.0 and prev_close_signal >= 0.0
close_sell = has_prev_close and close_signal > 0.0 and prev_close_signal <= 0.0
self._prev_open_signal = open_signal
self._prev_close_signal = close_signal
if self.Position > 0:
if close_buy:
self.SellMarket()
elif self.Position < 0:
if close_sell:
self.BuyMarket()
else:
if open_buy:
self.BuyMarket()
elif open_sell:
self.SellMarket()
def CreateClone(self):
return mamy_expert_strategy()