Strategie des Linearen Korrelationsoszillators
Die Strategie des Linearen Korrelationsoszillators misst die Korrelation zwischen Preis und Zeit über ein rollierendes Fenster. Die Strategie geht long, wenn der Oszillator über null kreuzt, und short, wenn er unter null kreuzt.
Details
- Einstiegskriterien:
- Oszillator kreuzt über null → Long.
- Oszillator kreuzt unter null → Short.
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Entgegengesetzter Nulldurchgang.
- Stops: Keine.
- Standardwerte:
Length= 14
- Filter:
- Kategorie: Oszillator
- Richtung: Beide
- Indikatoren: Linear Correlation
- Stops: Keine
- Komplexität: Niedrig
- Zeitrahmen: Beliebig
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
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>
/// Linear Correlation Oscillator strategy.
/// Goes long when correlation crosses above zero and shorts on cross below.
/// </summary>
public class LinearCorrelationOscillatorStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<decimal> _entryLevel;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal[] _prices;
private int _index;
private decimal _prevCorrelation;
private int _barsFromSignal;
/// <summary>
/// Lookback period for correlation calculation.
/// </summary>
public int Length
{
get => _length.Value;
set
{
_length.Value = value;
_prices = new decimal[value];
}
}
/// <summary>
/// Absolute correlation level required to open a position.
/// </summary>
public decimal EntryLevel
{
get => _entryLevel.Value;
set => _entryLevel.Value = value;
}
/// <summary>
/// Minimum number of bars between entry signals.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type used for processing.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public LinearCorrelationOscillatorStrategy()
{
_length = Param(nameof(Length), 20)
.SetGreaterThanZero()
.SetDisplay("Length", "Lookback length", "General")
.SetOptimize(18, 60, 2);
_entryLevel = Param(nameof(EntryLevel), 0.08m)
.SetGreaterThanZero()
.SetDisplay("Entry Level", "Absolute level required for entry", "General")
.SetOptimize(0.10m, 0.40m, 0.05m);
_cooldownBars = Param(nameof(CooldownBars), 4)
.SetGreaterThanZero()
.SetDisplay("Cooldown Bars", "Bars between entry signals", "General")
.SetOptimize(4, 20, 1);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle type", "Candle type", "General");
_prices = new decimal[Length];
_barsFromSignal = CooldownBars;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prices = new decimal[Length];
_index = 0;
_prevCorrelation = 0m;
_barsFromSignal = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var dummyEma1 = new ExponentialMovingAverage { Length = 10 };
var dummyEma2 = new ExponentialMovingAverage { Length = 20 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(dummyEma1, dummyEma2, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal d1, decimal d2)
{
if (candle.State != CandleStates.Finished)
return;
_prices[_index % Length] = candle.ClosePrice;
_index++;
if (_index < Length)
{
_prevCorrelation = 0m;
return;
}
var correlation = CalculateCorrelation();
_barsFromSignal++;
if (_barsFromSignal >= CooldownBars)
{
if (_prevCorrelation <= EntryLevel && correlation > EntryLevel && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (_prevCorrelation >= -EntryLevel && correlation < -EntryLevel && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
_prevCorrelation = correlation;
}
private decimal CalculateCorrelation()
{
var n = Length;
decimal sumY = 0m;
decimal sumY2 = 0m;
decimal sumXY = 0m;
for (var i = 0; i < n; i++)
{
var price = _prices[( _index - n + i) % n];
var x = i + 1;
sumY += price;
sumY2 += price * price;
sumXY += price * x;
}
var sumX = n * (n + 1m) / 2m;
var sumX2 = n * (n + 1m) * (2m * n + 1m) / 6m;
var numerator = n * sumXY - sumX * sumY;
var denominator = (decimal)Math.Sqrt((double)((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY)));
return denominator == 0m ? 0m : numerator / denominator;
}
}
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 linear_correlation_oscillator_strategy(Strategy):
"""
Linear correlation oscillator: computes Pearson correlation of price
vs time index. Goes long when crossing above entry level, short below.
"""
def __init__(self):
super(linear_correlation_oscillator_strategy, self).__init__()
self._length = self.Param("Length", 20) \
.SetDisplay("Length", "Lookback length", "General")
self._entry_level = self.Param("EntryLevel", 0.08) \
.SetDisplay("Entry Level", "Absolute level for entry", "General")
self._cooldown_bars = self.Param("CooldownBars", 4) \
.SetDisplay("Cooldown Bars", "Bars between entry signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle type", "General")
self._prices = []
self._prev_correlation = 0.0
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(linear_correlation_oscillator_strategy, self).OnReseted()
self._prices = []
self._prev_correlation = 0.0
self._bars_from_signal = self._cooldown_bars.Value
def OnStarted2(self, time):
super(linear_correlation_oscillator_strategy, self).OnStarted2(time)
self._bars_from_signal = self._cooldown_bars.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
length = self._length.Value
self._prices.append(float(candle.ClosePrice))
if len(self._prices) > length:
self._prices.pop(0)
if len(self._prices) < length:
return
correlation = self._calculate_correlation()
self._bars_from_signal += 1
entry = self._entry_level.Value
if self._bars_from_signal >= self._cooldown_bars.Value:
if self._prev_correlation <= entry and correlation > entry and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif self._prev_correlation >= -entry and correlation < -entry and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
self._prev_correlation = correlation
def _calculate_correlation(self):
n = len(self._prices)
sum_y = 0.0
sum_y2 = 0.0
sum_xy = 0.0
for i in range(n):
y = self._prices[i]
x = i + 1
sum_y += y
sum_y2 += y * y
sum_xy += y * x
sum_x = n * (n + 1.0) / 2.0
sum_x2 = n * (n + 1.0) * (2.0 * n + 1.0) / 6.0
numerator = n * sum_xy - sum_x * sum_y
denom_sq = (n * sum_x2 - sum_x * sum_x) * (n * sum_y2 - sum_y * sum_y)
if denom_sq <= 0:
return 0.0
denominator = math.sqrt(denom_sq)
return numerator / denominator if denominator != 0 else 0.0
def CreateClone(self):
return linear_correlation_oscillator_strategy()