MA Stochastic 戦略
MA Stochastic は、移動平均のトレンドフィルターとストキャスティクスオシレーターの押し目を組み合わせます。 価格が平均を上回りながら上昇トレンドにあり、ストキャスティクスが売られすぎゾーンに下落したとき、次の上昇転換での買いを準備します。
テストでは平均年間リターンは約151%を示しています。株式市場で最も良いパフォーマンスを発揮します。
ショートトレードは下降トレンドでこのロジックを反映し、ストキャスティクスが買われすぎに達したときにラリーを売ります。
固定パーセントストップが、トレンドが突然反転した場合の大きな損失を防ぎます。
詳細
- エントリー条件: インジケーターシグナル
- ロング/ショート: 両方
- エグジット条件: ストップロスまたは反対シグナル
- ストップ: はい、パーセントベース
- デフォルト値:
CandleType= 15 minuteStopLossPercent= 2MaPeriod= 50StochPeriod= 14StochDPeriod= 3StochOversold= 20StochOverbought= 80 価格がSMAより上なら上昇トレンド、下なら下降トレンドです。上昇トレンドで%KがStochOversoldを下回ると買いを準備し、価格がSMAの上にある間に%Kが次に上昇した足で買います。下降トレンドで%KがStochOverboughtを上回ると売りを準備し、%Kの次の下落で売ります。トレンドを外れると準備は取り消され、反対シグナルでドテンします。
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: 両方
- インジケーター: Moving Average, Stochastic
- ストップ: はい
- 複雑さ: 中級
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// MA Stochastic strategy.
/// Price above the MaPeriod SMA is an uptrend, below it a downtrend. Stochastic %K dipping below StochOversold in an uptrend
/// prepares a long that is bought on the next upturn of %K while price stays above the SMA; %K reaching StochOverbought in a downtrend
/// prepares a short sold on the next downturn. Leaving the trend cancels the setup. An opposite signal reverses the position,
/// and a percent stop limits the loss.
/// </summary>
public class MaStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochDPeriod;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevK;
// The prepared side: 1 long, -1 short, 0 none.
private int _setup;
/// <summary>
/// Period of the trend SMA.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Lookback period of stochastic %K.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of stochastic %D.
/// </summary>
public int StochDPeriod
{
get => _stochDPeriod.Value;
set => _stochDPeriod.Value = value;
}
/// <summary>
/// Level that prepares a long.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// Level that prepares a short.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MaStochasticStrategy()
{
_maPeriod = Param(nameof(MaPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the trend SMA", "Indicators");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Lookback period of stochastic %K", "Indicators");
_stochDPeriod = Param(nameof(StochDPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Smoothing period of stochastic %D", "Indicators");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Stochastic Oversold", "Level that prepares a long", "Indicators");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Stochastic Overbought", "Level that prepares a short", "Indicators");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_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();
_prevK = null;
_setup = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevK = null;
_setup = 0;
var sma = new SimpleMovingAverage { Length = MaPeriod };
var stochastic = new StochasticOscillator
{
K = { Length = StochPeriod },
D = { Length = StochDPeriod },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(sma, stochastic, ProcessCandle)
.Start();
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true, isLocalStop: true);
// The stop has to see prices between candles, not only at their close.
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveProtectionQuote).Start();
}
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, stochastic);
}
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue smaValue, IIndicatorValue stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!smaValue.IsFormed || !stochasticValue.IsFormed)
return;
if (stochasticValue is not IStochasticOscillatorValue { K: decimal k })
return;
var ma = smaValue.GetValue<decimal>();
var close = candle.ClosePrice;
var prevK = _prevK;
_prevK = k;
var uptrend = close > ma;
var downtrend = close < ma;
var signal = 0;
if (prevK is decimal lastK)
{
if (_setup == 1 && uptrend && k > lastK)
signal = 1;
else if (_setup == -1 && downtrend && k < lastK)
signal = -1;
}
if (signal != 0)
_setup = 0;
if (uptrend && k < StochOversold)
_setup = 1;
else if (downtrend && k > StochOverbought)
_setup = -1;
else if ((_setup == 1 && !uptrend) || (_setup == -1 && !downtrend))
_setup = 0;
if (signal == 0 || !IsFormedAndOnlineAndAllowTrading())
return;
if (signal > 0 && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (signal < 0 && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes, Level1Fields
from StockSharp.BusinessEntities import Subscription
from StockSharp.Algo.Indicators import SimpleMovingAverage, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class ma_stochastic_strategy(Strategy):
"""
MA Stochastic strategy.
Price above the MaPeriod SMA is an uptrend, below it a downtrend. Stochastic %K dipping below StochOversold in an uptrend
prepares a long that is bought on the next upturn of %K while price stays above the SMA; %K reaching StochOverbought in a downtrend
prepares a short sold on the next downturn. Leaving the trend cancels the setup. An opposite signal reverses the position,
and a percent stop limits the loss.
"""
def __init__(self):
super(ma_stochastic_strategy, self).__init__()
self._ma_period = self.Param("MaPeriod", 50).SetGreaterThanZero().SetDisplay("MA Period", "Period of the trend SMA", "Indicators")
self._stoch_period = self.Param("StochPeriod", 14).SetGreaterThanZero().SetDisplay("Stochastic Period", "Lookback period of stochastic %K", "Indicators")
self._stoch_d_period = self.Param("StochDPeriod", 3).SetGreaterThanZero().SetDisplay("Stochastic %D", "Smoothing period of stochastic %D", "Indicators")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Stochastic Oversold", "Level that prepares a long", "Indicators")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Stochastic Overbought", "Level that prepares a short", "Indicators")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_k = None
# The prepared side: 1 long, -1 short, 0 none.
self._setup = 0
def OnReseted(self):
super(ma_stochastic_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ma_stochastic_strategy, self).OnStarted2(time)
self._reset_state()
sma = SimpleMovingAverage()
sma.Length = self._ma_period.Value
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_period.Value
stochastic.D.Length = self._stoch_d_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(sma, stochastic, self._process_candle).Start()
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True, isLocalStop=True)
# The stop has to see prices between candles, not only at their close.
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_protection_quote).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, stochastic)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _process_candle(self, candle, sma_value, stochastic_value):
if candle.State != CandleStates.Finished:
return
if not sma_value.IsFormed or not stochastic_value.IsFormed or stochastic_value.K is None:
return
ma = sma_value.GetValue[Decimal](None)
k = stochastic_value.K
close = candle.ClosePrice
prev_k = self._prev_k
self._prev_k = k
uptrend = close > ma
downtrend = close < ma
signal = 0
if prev_k is not None:
if self._setup == 1 and uptrend and k > prev_k:
signal = 1
elif self._setup == -1 and downtrend and k < prev_k:
signal = -1
if signal != 0:
self._setup = 0
if uptrend and k < Decimal(self._stoch_oversold.Value):
self._setup = 1
elif downtrend and k > Decimal(self._stoch_overbought.Value):
self._setup = -1
elif (self._setup == 1 and not uptrend) or (self._setup == -1 and not downtrend):
self._setup = 0
if signal == 0 or not self.IsFormedAndOnlineAndAllowTrading():
return
if signal > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif signal < 0 and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return ma_stochastic_strategy()