Supertrend プット・コール・レシオ戦略
Supertrend Put Call Ratio 戦略は、SupertrendとPut/Callレシオを中心に構築されています。
テストでは年平均リターン約112%が示されています。外国為替市場で最も優れたパフォーマンスを発揮します。
インジケーターがイントラデイ(15m)データでのトレンド転換を確認したときにシグナルが発生します。これによりこの手法はアクティブトレーダーに適しています。
ストップはATRの倍数とPeriod、Multiplierなどのパラメーターに依存します。リスクとリワードのバランスを取るためにこれらのデフォルト値を調整してください。
詳細
- エントリー条件: インジケーター条件については実装を参照してください。
- ロング/ショート: 両方向。
- エグジット条件: 反対シグナルまたはストップロジック。
- ストップ: はい、インジケーターベースの計算を使用。
- デフォルト値:
Period = 10Multiplier = 3mPCRPeriod = 20PCRMultiplier = 2mCandleType = TimeSpan.FromMinutes(15).TimeFrame()
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: 複数のインジケーター
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ (15m)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Supertrend with Put/Call Ratio strategy.
/// Entry condition:
/// Long: Price > Supertrend && PCR < Avg(PCR, N) - k*StdDev(PCR, N)
/// Short: Price < Supertrend && PCR > Avg(PCR, N) + k*StdDev(PCR, N)
/// Exit condition:
/// Long: Price < Supertrend
/// Short: Price > Supertrend
/// </summary>
public class SupertrendWithPutCallRatioStrategy : Strategy
{
private readonly StrategyParam<int> _period;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<int> _pcrPeriod;
private readonly StrategyParam<decimal> _pcrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _pcrHistory = [];
private decimal _pcrAverage;
private decimal _pcrStdDev;
private bool _isLong;
private bool _isShort;
// Simulated PCR value (in real implementation this would come from market data)
private decimal _currentPcr;
/// <summary>
/// Supertrend period.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Supertrend multiplier.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// PCR averaging period.
/// </summary>
public int PCRPeriod
{
get => _pcrPeriod.Value;
set => _pcrPeriod.Value = value;
}
/// <summary>
/// PCR standard deviation multiplier for thresholds.
/// </summary>
public decimal PCRMultiplier
{
get => _pcrMultiplier.Value;
set => _pcrMultiplier.Value = value;
}
/// <summary>
/// Type of candles to use.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor with default parameters.
/// </summary>
public SupertrendWithPutCallRatioStrategy()
{
_period = Param(nameof(Period), 10)
.SetGreaterThanZero()
.SetDisplay("Supertrend Period", "Supertrend ATR period", "Supertrend Settings")
.SetOptimize(5, 20, 3);
_multiplier = Param(nameof(Multiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Supertrend Multiplier", "Supertrend ATR multiplier", "Supertrend Settings")
.SetOptimize(2m, 4m, 0.5m);
_pcrPeriod = Param(nameof(PCRPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("PCR Period", "Put/Call Ratio averaging period", "PCR Settings")
.SetOptimize(10, 30, 5);
_pcrMultiplier = Param(nameof(PCRMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("PCR Std Dev Multiplier", "Multiplier for PCR standard deviation", "PCR Settings")
.SetOptimize(1m, 3m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pcrHistory.Clear();
_isLong = default;
_isShort = default;
_currentPcr = default;
_pcrAverage = default;
_pcrStdDev = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create Supertrend indicator
var supertrend = new SuperTrend { Length = Period, Multiplier = Multiplier };
// Subscribe to candles and bind indicator
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(supertrend, ProcessCandle)
.Start();
// Create chart visualization if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, supertrend);
DrawOwnTrades(area);
}
}
/// <summary>
/// Process each candle and Supertrend value.
/// </summary>
private void ProcessCandle(ICandleMessage candle, decimal supertrendValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Update PCR value (in a real system, this would come from market data)
UpdatePCR(candle);
// Calculate PCR thresholds based on historical data
var bullishPcrThreshold = _pcrAverage - PCRMultiplier * _pcrStdDev;
var bearishPcrThreshold = _pcrAverage + PCRMultiplier * _pcrStdDev;
var price = candle.ClosePrice;
var priceAboveSupertrend = price > supertrendValue;
var priceBelowSupertrend = price < supertrendValue;
// Trading logic
// Entry conditions
// Long entry: Price > Supertrend && PCR < bullish threshold (bullish PCR)
if (priceAboveSupertrend && _currentPcr < bullishPcrThreshold && !_isLong && Position <= 0)
{
LogInfo($"Long signal: Price {price} > Supertrend {supertrendValue}, PCR {_currentPcr} < Threshold {bullishPcrThreshold}");
BuyMarket(Volume);
_isLong = true;
_isShort = false;
}
// Short entry: Price < Supertrend && PCR > bearish threshold (bearish PCR)
else if (priceBelowSupertrend && _currentPcr > bearishPcrThreshold && !_isShort && Position >= 0)
{
LogInfo($"Short signal: Price {price} < Supertrend {supertrendValue}, PCR {_currentPcr} > Threshold {bearishPcrThreshold}");
SellMarket(Volume);
_isShort = true;
_isLong = false;
}
// Exit conditions (based only on Supertrend, not PCR)
// Exit long: Price < Supertrend
if (_isLong && priceBelowSupertrend && Position > 0)
{
LogInfo($"Exit long: Price {price} < Supertrend {supertrendValue}");
SellMarket(Math.Abs(Position));
_isLong = false;
}
// Exit short: Price > Supertrend
else if (_isShort && priceAboveSupertrend && Position < 0)
{
LogInfo($"Exit short: Price {price} > Supertrend {supertrendValue}");
BuyMarket(Math.Abs(Position));
_isShort = false;
}
}
/// <summary>
/// Update Put/Call Ratio value.
/// In a real implementation, this would fetch data from market.
/// </summary>
private void UpdatePCR(ICandleMessage candle)
{
// Base PCR on candle pattern with some randomness
decimal pcr;
// Bullish candle tends to have lower PCR
if (candle.ClosePrice > candle.OpenPrice)
{
pcr = 0.7m + (decimal)(RandomGen.GetDouble() * 0.3);
}
// Bearish candle tends to have higher PCR
else
{
pcr = 1.0m + (decimal)(RandomGen.GetDouble() * 0.5);
}
_currentPcr = pcr;
// Add to history
_pcrHistory.Add(_currentPcr);
if (_pcrHistory.Count > PCRPeriod)
{
_pcrHistory.RemoveAt(0);
}
// Calculate average
decimal sum = 0;
foreach (var value in _pcrHistory)
{
sum += value;
}
_pcrAverage = _pcrHistory.Count > 0
? sum / _pcrHistory.Count
: 1.0m; // Default to neutral (1.0)
// Calculate standard deviation
if (_pcrHistory.Count > 1)
{
decimal sumSquaredDiffs = 0;
foreach (var value in _pcrHistory)
{
var diff = value - _pcrAverage;
sumSquaredDiffs += diff * diff;
}
_pcrStdDev = (decimal)Math.Sqrt((double)(sumSquaredDiffs / (_pcrHistory.Count - 1)));
}
else
{
_pcrStdDev = 0.1m; // Default value until we have enough data
}
LogInfo($"PCR: {_currentPcr}, Avg: {_pcrAverage}, StdDev: {_pcrStdDev}");
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("Ecng.Common")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from Ecng.Common import RandomGen
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SuperTrend
from StockSharp.Algo.Strategies import Strategy
class supertrend_put_call_ratio_strategy(Strategy):
"""
Supertrend with Put/Call Ratio strategy.
"""
def __init__(self):
super(supertrend_put_call_ratio_strategy, self).__init__()
self._period = self.Param("Period", 10) \
.SetGreaterThanZero() \
.SetDisplay("Supertrend Period", "Supertrend ATR period", "Supertrend Settings")
self._multiplier = self.Param("Multiplier", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("Supertrend Multiplier", "Supertrend ATR multiplier", "Supertrend Settings")
self._pcr_period = self.Param("PCRPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("PCR Period", "Put/Call Ratio averaging period", "PCR Settings")
self._pcr_multiplier = self.Param("PCRMultiplier", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("PCR Std Dev Multiplier", "Multiplier for PCR standard deviation", "PCR Settings")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._pcr_history = []
self._pcr_average = 0.0
self._pcr_std_dev = 0.0
self._is_long = False
self._is_short = False
self._current_pcr = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(supertrend_put_call_ratio_strategy, self).OnReseted()
self._pcr_history = []
self._is_long = False
self._is_short = False
self._current_pcr = 0.0
self._pcr_average = 0.0
self._pcr_std_dev = 0.0
def OnStarted2(self, time):
super(supertrend_put_call_ratio_strategy, self).OnStarted2(time)
supertrend = SuperTrend()
supertrend.Length = int(self._period.Value)
supertrend.Multiplier = Decimal(float(self._multiplier.Value))
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(supertrend, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, supertrend)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, supertrend_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
self.UpdatePCR(candle)
pcr_mult = float(self._pcr_multiplier.Value)
bullish_threshold = self._pcr_average - pcr_mult * self._pcr_std_dev
bearish_threshold = self._pcr_average + pcr_mult * self._pcr_std_dev
price = float(candle.ClosePrice)
st = float(supertrend_value)
price_above = price > st
price_below = price < st
if price_above and self._current_pcr < bullish_threshold and not self._is_long and self.Position <= 0:
self.BuyMarket(self.Volume)
self._is_long = True
self._is_short = False
elif price_below and self._current_pcr > bearish_threshold and not self._is_short and self.Position >= 0:
self.SellMarket(self.Volume)
self._is_short = True
self._is_long = False
if self._is_long and price_below and self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self._is_long = False
elif self._is_short and price_above and self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self._is_short = False
def UpdatePCR(self, candle):
if candle.ClosePrice > candle.OpenPrice:
pcr = 0.7 + RandomGen.GetDouble() * 0.3
else:
pcr = 1.0 + RandomGen.GetDouble() * 0.5
self._current_pcr = pcr
self._pcr_history.append(self._current_pcr)
pcr_period = int(self._pcr_period.Value)
if len(self._pcr_history) > pcr_period:
self._pcr_history.pop(0)
total = 0.0
for v in self._pcr_history:
total += v
if len(self._pcr_history) > 0:
self._pcr_average = total / len(self._pcr_history)
else:
self._pcr_average = 1.0
if len(self._pcr_history) > 1:
sum_sq = 0.0
for v in self._pcr_history:
diff = v - self._pcr_average
sum_sq += diff * diff
self._pcr_std_dev = Math.Sqrt(sum_sq / (len(self._pcr_history) - 1))
else:
self._pcr_std_dev = 0.1
self.LogInfo("PCR: {0}, Avg: {1}, StdDev: {2}".format(self._current_pcr, self._pcr_average, self._pcr_std_dev))
def CreateClone(self):
return supertrend_put_call_ratio_strategy()