Стратегия MathStatisticsKernelFunctions
Реализует набор статистических ядер и совершает сделки при пересечении выходом выбранного ядра уровня 0.5.
Параметры
- Kernel – название ядра (
uniform,triangle,epanechnikov,quartic,triweight,tricubic,gaussian,cosine,logistic,sigmoid). - Bandwidth – ширина ядра.
- Candle Type – тип свечей.
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;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Demonstrates various statistical kernel functions.
/// Places trades when the selected kernel output crosses 0.5.
/// </summary>
public class MathStatisticsKernelFunctionsStrategy : Strategy
{
private readonly StrategyParam<string> _kernel;
private readonly StrategyParam<decimal> _bandwidth;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private int _barIndex;
private int _lastTradeBar = -1;
/// <summary>
/// Kernel function name.
/// </summary>
public string Kernel
{
get => _kernel.Value;
set => _kernel.Value = value;
}
/// <summary>
/// Bandwidth value for kernel calculation.
/// </summary>
public decimal Bandwidth
{
get => _bandwidth.Value;
set => _bandwidth.Value = value;
}
/// <summary>
/// Minimum bars between two market entries.
/// </summary>
public int SignalCooldownBars
{
get => _signalCooldownBars.Value;
set => _signalCooldownBars.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public MathStatisticsKernelFunctionsStrategy()
{
_kernel = Param(nameof(Kernel), "uniform")
.SetDisplay("Kernel", "Kernel function name", "General");
_bandwidth = Param(nameof(Bandwidth), 0.5m)
.SetGreaterThanZero()
.SetDisplay("Bandwidth", "Kernel bandwidth", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 20)
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_barIndex = 0;
_lastTradeBar = -1;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Slower synthetic cycle reduces signal frequency and keeps order count stable.
var test = -1m + (_barIndex % 400) * 0.005m;
_barIndex++;
var value = Select(Kernel, test, Bandwidth);
var canTrade = _lastTradeBar < 0 || _barIndex - _lastTradeBar >= SignalCooldownBars;
if (canTrade && value > 0.5m && Position <= 0)
{
BuyMarket();
_lastTradeBar = _barIndex;
}
else if (canTrade && value < 0.5m && Position >= 0)
{
SellMarket();
_lastTradeBar = _barIndex;
}
}
private static decimal Uniform(decimal distance, decimal bandwidth)
{
return Math.Abs(distance) > bandwidth ? 0m : 0.5m;
}
private static decimal Triangular(decimal distance, decimal bandwidth)
{
return Math.Abs(distance) > bandwidth ? 0m : 1m - Math.Abs(distance / bandwidth);
}
private static decimal Epanechnikov(decimal distance, decimal bandwidth)
{
if (Math.Abs(distance) > bandwidth)
return 0m;
var ratio = distance / bandwidth;
return 0.25m * (1m - ratio * ratio);
}
private static decimal Quartic(decimal distance, decimal bandwidth)
{
if (Math.Abs(distance) > bandwidth)
return 0m;
var ratio = distance / bandwidth;
var inner = 1m - ratio * ratio;
return 0.9375m * inner * inner;
}
private static decimal Triweight(decimal distance, decimal bandwidth)
{
if (Math.Abs(distance) > bandwidth)
return 0m;
var ratio = distance / bandwidth;
var inner = 1m - ratio * ratio;
return (35m / 32m) * inner * inner * inner;
}
private static decimal Tricubic(decimal distance, decimal bandwidth)
{
if (Math.Abs(distance) > bandwidth)
return 0m;
var ratio = Math.Abs(distance) / bandwidth;
var inner = 1m - ratio * ratio * ratio;
return (70m / 81m) * inner * inner * inner;
}
private static decimal Gaussian(decimal distance, decimal bandwidth)
{
var d = (double)(distance / bandwidth);
var result = 1d / Math.Sqrt(2d * Math.PI) * Math.Exp(-0.5d * d * d);
return (decimal)result;
}
private static decimal Cosine(decimal distance, decimal bandwidth)
{
if (Math.Abs(distance) > bandwidth)
return 0m;
var d = (double)(distance / bandwidth);
var result = (Math.PI / 4d) * Math.Cos(Math.PI / 2d * d);
return (decimal)result;
}
private static decimal Logistic(decimal distance, decimal bandwidth)
{
var d = (double)(distance / bandwidth);
var result = 1d / (Math.Exp(d) + 2d + Math.Exp(-d));
return (decimal)result;
}
private static decimal Sigmoid(decimal distance, decimal bandwidth)
{
var d = (double)(distance / bandwidth);
var result = 2d / Math.PI * (1d / (Math.Exp(d) + Math.Exp(-d)));
return (decimal)result;
}
private static decimal Select(string kernel, decimal distance, decimal bandwidth)
{
return kernel switch
{
"uniform" => Uniform(distance, bandwidth),
"triangle" => Triangular(distance, bandwidth),
"epanechnikov" => Epanechnikov(distance, bandwidth),
"quartic" => Quartic(distance, bandwidth),
"triweight" => Triweight(distance, bandwidth),
"tricubic" => Tricubic(distance, bandwidth),
"gaussian" => Gaussian(distance, bandwidth),
"cosine" => Cosine(distance, bandwidth),
"logistic" => Logistic(distance, bandwidth),
"sigmoid" => Sigmoid(distance, bandwidth),
_ => throw new ArgumentException("Invalid kernel", nameof(kernel)),
};
}
}
import clr
import math
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.Strategies import Strategy
class math_statistics_kernel_functions_strategy(Strategy):
def __init__(self):
super(math_statistics_kernel_functions_strategy, self).__init__()
self._kernel = self.Param("Kernel", "uniform") \
.SetDisplay("Kernel", "Kernel function name", "General")
self._bandwidth = self.Param("Bandwidth", 0.5) \
.SetGreaterThanZero() \
.SetDisplay("Bandwidth", "Kernel bandwidth", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 20) \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Timeframe for candles", "General")
self._bar_index = 0
self._last_trade_bar = -1
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(math_statistics_kernel_functions_strategy, self).OnReseted()
self._bar_index = 0
self._last_trade_bar = -1
def OnStarted2(self, time):
super(math_statistics_kernel_functions_strategy, self).OnStarted2(time)
self._bar_index = 0
self._last_trade_bar = -1
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
def _uniform(self, d, bw):
return 0.0 if abs(d) > bw else 0.5
def _triangular(self, d, bw):
return 0.0 if abs(d) > bw else 1.0 - abs(d / bw)
def _epanechnikov(self, d, bw):
if abs(d) > bw:
return 0.0
r = d / bw
return 0.25 * (1.0 - r * r)
def _quartic(self, d, bw):
if abs(d) > bw:
return 0.0
r = d / bw
inner = 1.0 - r * r
return 0.9375 * inner * inner
def _triweight(self, d, bw):
if abs(d) > bw:
return 0.0
r = d / bw
inner = 1.0 - r * r
return (35.0 / 32.0) * inner * inner * inner
def _tricubic(self, d, bw):
if abs(d) > bw:
return 0.0
r = abs(d) / bw
inner = 1.0 - r * r * r
return (70.0 / 81.0) * inner * inner * inner
def _gaussian(self, d, bw):
x = d / bw
return (1.0 / math.sqrt(2.0 * math.pi)) * math.exp(-0.5 * x * x)
def _cosine(self, d, bw):
if abs(d) > bw:
return 0.0
x = d / bw
return (math.pi / 4.0) * math.cos(math.pi / 2.0 * x)
def _logistic(self, d, bw):
x = d / bw
return 1.0 / (math.exp(x) + 2.0 + math.exp(-x))
def _sigmoid_k(self, d, bw):
x = d / bw
return (2.0 / math.pi) * (1.0 / (math.exp(x) + math.exp(-x)))
def _select(self, kernel, d, bw):
if kernel == "uniform":
return self._uniform(d, bw)
elif kernel == "triangle":
return self._triangular(d, bw)
elif kernel == "epanechnikov":
return self._epanechnikov(d, bw)
elif kernel == "quartic":
return self._quartic(d, bw)
elif kernel == "triweight":
return self._triweight(d, bw)
elif kernel == "tricubic":
return self._tricubic(d, bw)
elif kernel == "gaussian":
return self._gaussian(d, bw)
elif kernel == "cosine":
return self._cosine(d, bw)
elif kernel == "logistic":
return self._logistic(d, bw)
elif kernel == "sigmoid":
return self._sigmoid_k(d, bw)
return 0.0
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
test = -1.0 + (self._bar_index % 400) * 0.005
self._bar_index += 1
bw = float(self._bandwidth.Value)
kernel = str(self._kernel.Value)
value = self._select(kernel, test, bw)
cd = self._signal_cooldown_bars.Value
can_trade = self._last_trade_bar < 0 or self._bar_index - self._last_trade_bar >= cd
if can_trade and value > 0.5 and self.Position <= 0:
self.BuyMarket()
self._last_trade_bar = self._bar_index
elif can_trade and value < 0.5 and self.Position >= 0:
self.SellMarket()
self._last_trade_bar = self._bar_index
def CreateClone(self):
return math_statistics_kernel_functions_strategy()