4本WMA戦略(TP・SL付き)
4本の移動平均のクロスオーバーを使用し、オプションのテイクプロフィット、ストップロス、代替エグジット条件を備えた戦略。
詳細
- エントリー条件:
- ロング: Long MA1がLong MA2を上抜けるクロス
- ショート: Short MA1がShort MA2を下抜けるクロス
- ロング/ショート: 設定可能
- ストップ: パーセンテージベースのTPとSL
- デフォルト値:
LongMa1Length= 10LongMa2Length= 20ShortMa1Length= 30ShortMa2Length= 40MaType= WmaEnableTpSl= trueTakeProfitPercent= 1mStopLossPercent= 1mDirection= BothEnableAltExit= falseAltExitMaOption= LongMa1CandleType= TimeSpan.FromMinutes(1).TimeFrame()
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: 移動平均
- ストップ: はい
- 複雑さ: 基本
- 時間軸: 短期
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// Four WMA strategy with TP and SL.
/// A long opens when Long MA1 crosses above Long MA2 and a short when Short MA1 crosses below Short MA2, in the directions Direction
/// allows; an opposite entry reverses the position. With EnableTpSl percent take profit and stop loss protect each position. With
/// EnableAltExit a long also closes when the close crosses below the moving average chosen by AltExitMaOption and a short when it
/// crosses above it.
/// </summary>
public class FourWmaTpSlStrategy : Strategy
{
/// <summary>
/// Moving average types.
/// </summary>
public enum MaTypes
{
/// <summary>
/// Simple moving average.
/// </summary>
Sma,
/// <summary>
/// Exponential moving average.
/// </summary>
Ema,
/// <summary>
/// Weighted moving average.
/// </summary>
Wma,
/// <summary>
/// Hull moving average.
/// </summary>
Hma,
/// <summary>
/// Smoothed moving average.
/// </summary>
Rma,
}
/// <summary>
/// Allowed trade directions.
/// </summary>
public enum TradeDirections
{
/// <summary>
/// Long trades only.
/// </summary>
Long,
/// <summary>
/// Short trades only.
/// </summary>
Short,
/// <summary>
/// Both directions.
/// </summary>
Both,
}
/// <summary>
/// Moving averages the alternate exit can use.
/// </summary>
public enum AltExitMaOptions
{
/// <summary>
/// Long MA1.
/// </summary>
LongMa1,
/// <summary>
/// Long MA2.
/// </summary>
LongMa2,
/// <summary>
/// Short MA1.
/// </summary>
ShortMa1,
/// <summary>
/// Short MA2.
/// </summary>
ShortMa2,
}
private readonly StrategyParam<int> _longMa1Length;
private readonly StrategyParam<int> _longMa2Length;
private readonly StrategyParam<int> _shortMa1Length;
private readonly StrategyParam<int> _shortMa2Length;
private readonly StrategyParam<MaTypes> _maType;
private readonly StrategyParam<bool> _enableTpSl;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<TradeDirections> _direction;
private readonly StrategyParam<bool> _enableAltExit;
private readonly StrategyParam<AltExitMaOptions> _altExitMaOption;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevLongMa1;
private decimal? _prevLongMa2;
private decimal? _prevShortMa1;
private decimal? _prevShortMa2;
private decimal? _prevClose;
/// <summary>
/// Length of Long MA1.
/// </summary>
public int LongMa1Length
{
get => _longMa1Length.Value;
set => _longMa1Length.Value = value;
}
/// <summary>
/// Length of Long MA2.
/// </summary>
public int LongMa2Length
{
get => _longMa2Length.Value;
set => _longMa2Length.Value = value;
}
/// <summary>
/// Length of Short MA1.
/// </summary>
public int ShortMa1Length
{
get => _shortMa1Length.Value;
set => _shortMa1Length.Value = value;
}
/// <summary>
/// Length of Short MA2.
/// </summary>
public int ShortMa2Length
{
get => _shortMa2Length.Value;
set => _shortMa2Length.Value = value;
}
/// <summary>
/// Moving average type.
/// </summary>
public MaTypes MaType
{
get => _maType.Value;
set => _maType.Value = value;
}
/// <summary>
/// Use percent take profit and stop loss.
/// </summary>
public bool EnableTpSl
{
get => _enableTpSl.Value;
set => _enableTpSl.Value = value;
}
/// <summary>
/// Take profit percentage from entry price.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Allowed trade directions.
/// </summary>
public TradeDirections Direction
{
get => _direction.Value;
set => _direction.Value = value;
}
/// <summary>
/// Close positions on a close crossing the chosen moving average.
/// </summary>
public bool EnableAltExit
{
get => _enableAltExit.Value;
set => _enableAltExit.Value = value;
}
/// <summary>
/// Moving average used by the alternate exit.
/// </summary>
public AltExitMaOptions AltExitMaOption
{
get => _altExitMaOption.Value;
set => _altExitMaOption.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public FourWmaTpSlStrategy()
{
_longMa1Length = Param(nameof(LongMa1Length), 10)
.SetGreaterThanZero()
.SetDisplay("Long MA1", "Length of Long MA1", "Moving Averages");
_longMa2Length = Param(nameof(LongMa2Length), 20)
.SetGreaterThanZero()
.SetDisplay("Long MA2", "Length of Long MA2", "Moving Averages");
_shortMa1Length = Param(nameof(ShortMa1Length), 30)
.SetGreaterThanZero()
.SetDisplay("Short MA1", "Length of Short MA1", "Moving Averages");
_shortMa2Length = Param(nameof(ShortMa2Length), 40)
.SetGreaterThanZero()
.SetDisplay("Short MA2", "Length of Short MA2", "Moving Averages");
_maType = Param(nameof(MaType), MaTypes.Wma)
.SetDisplay("MA Type", "Moving average type", "Moving Averages");
_enableTpSl = Param(nameof(EnableTpSl), true)
.SetDisplay("Enable TP/SL", "Use percent take profit and stop loss", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 1m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take profit percentage from entry price", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_direction = Param(nameof(Direction), TradeDirections.Both)
.SetDisplay("Direction", "Allowed trade directions", "Trading");
_enableAltExit = Param(nameof(EnableAltExit), false)
.SetDisplay("Enable Alt Exit", "Close positions on a close crossing the chosen moving average", "Exit");
_altExitMaOption = Param(nameof(AltExitMaOption), AltExitMaOptions.LongMa1)
.SetDisplay("Alt Exit MA", "Moving average used by the alternate exit", "Exit");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).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();
ResetState();
}
private void ResetState()
{
_prevLongMa1 = null;
_prevLongMa2 = null;
_prevShortMa1 = null;
_prevShortMa2 = null;
_prevClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var longMa1 = CreateMovingAverage(MaType, LongMa1Length);
var longMa2 = CreateMovingAverage(MaType, LongMa2Length);
var shortMa1 = CreateMovingAverage(MaType, ShortMa1Length);
var shortMa2 = CreateMovingAverage(MaType, ShortMa2Length);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(longMa1, longMa2, shortMa1, shortMa2, ProcessCandle)
.Start();
if (EnableTpSl)
{
StartProtection(
new Unit(TakeProfitPercent, UnitTypes.Percent),
new Unit(StopLossPercent, UnitTypes.Percent),
useMarketOrders: true,
isLocalStop: true);
// The stop and target have 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, longMa1);
DrawIndicator(area, longMa2);
DrawIndicator(area, shortMa1);
DrawIndicator(area, shortMa2);
DrawOwnTrades(area);
}
}
private void ObserveProtectionQuote(Level1ChangeMessage quote)
{
// The high-level handler activates native protection before this callback, also between signal bars.
}
private static DecimalLengthIndicator CreateMovingAverage(MaTypes type, int length)
{
return type switch
{
MaTypes.Sma => new SimpleMovingAverage { Length = length },
MaTypes.Ema => new ExponentialMovingAverage { Length = length },
MaTypes.Hma => new HullMovingAverage { Length = length },
MaTypes.Rma => new SmoothedMovingAverage { Length = length },
_ => new WeightedMovingAverage { Length = length },
};
}
private void ProcessCandle(ICandleMessage candle, decimal longMa1, decimal longMa2, decimal shortMa1, decimal shortMa2)
{
if (candle.State != CandleStates.Finished)
return;
var prevLongMa1 = _prevLongMa1;
var prevLongMa2 = _prevLongMa2;
var prevShortMa1 = _prevShortMa1;
var prevShortMa2 = _prevShortMa2;
var prevClose = _prevClose;
_prevLongMa1 = longMa1;
_prevLongMa2 = longMa2;
_prevShortMa1 = shortMa1;
_prevShortMa2 = shortMa2;
_prevClose = candle.ClosePrice;
if (prevLongMa1 is not decimal lastLongMa1 || prevLongMa2 is not decimal lastLongMa2 ||
prevShortMa1 is not decimal lastShortMa1 || prevShortMa2 is not decimal lastShortMa2 || prevClose is not decimal lastClose)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var allowLong = Direction != TradeDirections.Short;
var allowShort = Direction != TradeDirections.Long;
var longSignal = lastLongMa1 <= lastLongMa2 && longMa1 > longMa2;
var shortSignal = lastShortMa1 >= lastShortMa2 && shortMa1 < shortMa2;
if (allowLong && longSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
return;
}
if (allowShort && shortSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
return;
}
if (!EnableAltExit || Position == 0)
return;
var (exitMa, lastExitMa) = AltExitMaOption switch
{
AltExitMaOptions.LongMa2 => (longMa2, lastLongMa2),
AltExitMaOptions.ShortMa1 => (shortMa1, lastShortMa1),
AltExitMaOptions.ShortMa2 => (shortMa2, lastShortMa2),
_ => (longMa1, lastLongMa1),
};
if (Position > 0 && lastClose >= lastExitMa && close < exitMa)
SellMarket(Position);
else if (Position < 0 && lastClose <= lastExitMa && close > exitMa)
BuyMarket(-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, ExponentialMovingAverage, WeightedMovingAverage, HullMovingAverage, SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
MA_SMA = 0
MA_EMA = 1
MA_WMA = 2
MA_HMA = 3
MA_RMA = 4
DIRECTION_LONG = 0
DIRECTION_SHORT = 1
DIRECTION_BOTH = 2
ALT_LONG_MA1 = 0
ALT_LONG_MA2 = 1
ALT_SHORT_MA1 = 2
ALT_SHORT_MA2 = 3
class four_wma_tp_sl_strategy(Strategy):
"""
Four WMA strategy with TP and SL.
A long opens when Long MA1 crosses above Long MA2 and a short when Short MA1 crosses below Short MA2, in the directions Direction
allows; an opposite entry reverses the position. With EnableTpSl percent take profit and stop loss protect each position. With
EnableAltExit a long also closes when the close crosses below the moving average chosen by AltExitMaOption and a short when it
crosses above it.
"""
def __init__(self):
super(four_wma_tp_sl_strategy, self).__init__()
self._long_ma1_length = self.Param("LongMa1Length", 10).SetGreaterThanZero().SetDisplay("Long MA1", "Length of Long MA1", "Moving Averages")
self._long_ma2_length = self.Param("LongMa2Length", 20).SetGreaterThanZero().SetDisplay("Long MA2", "Length of Long MA2", "Moving Averages")
self._short_ma1_length = self.Param("ShortMa1Length", 30).SetGreaterThanZero().SetDisplay("Short MA1", "Length of Short MA1", "Moving Averages")
self._short_ma2_length = self.Param("ShortMa2Length", 40).SetGreaterThanZero().SetDisplay("Short MA2", "Length of Short MA2", "Moving Averages")
self._ma_type = self.Param("MaType", MA_WMA).SetDisplay("MA Type", "Moving average type (0 Sma, 1 Ema, 2 Wma, 3 Hma, 4 Rma)", "Moving Averages")
self._enable_tp_sl = self.Param("EnableTpSl", True).SetDisplay("Enable TP/SL", "Use percent take profit and stop loss", "Risk")
self._take_profit_percent = self.Param("TakeProfitPercent", 1.0).SetNotNegative().SetDisplay("Take Profit %", "Take profit percentage from entry price", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._direction = self.Param("Direction", DIRECTION_BOTH).SetDisplay("Direction", "Allowed trade directions (0 Long, 1 Short, 2 Both)", "Trading")
self._enable_alt_exit = self.Param("EnableAltExit", False).SetDisplay("Enable Alt Exit", "Close positions on a close crossing the chosen moving average", "Exit")
self._alt_exit_ma_option = self.Param("AltExitMaOption", ALT_LONG_MA1).SetDisplay("Alt Exit MA", "Moving average used by the alternate exit (0 LongMa1, 1 LongMa2, 2 ShortMa1, 3 ShortMa2)", "Exit")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).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 = None
def OnReseted(self):
super(four_wma_tp_sl_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(four_wma_tp_sl_strategy, self).OnStarted2(time)
self._reset_state()
ma_type = int(self._ma_type.Value)
long_ma1 = self._create_ma(ma_type, self._long_ma1_length.Value)
long_ma2 = self._create_ma(ma_type, self._long_ma2_length.Value)
short_ma1 = self._create_ma(ma_type, self._short_ma1_length.Value)
short_ma2 = self._create_ma(ma_type, self._short_ma2_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(long_ma1, long_ma2, short_ma1, short_ma2, self._process_candle).Start()
if self._enable_tp_sl.Value:
self.StartProtection(
Unit(Decimal(self._take_profit_percent.Value), UnitTypes.Percent),
Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent),
useMarketOrders=True, isLocalStop=True)
# The stop and target have 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, long_ma1)
self.DrawIndicator(area, long_ma2)
self.DrawIndicator(area, short_ma1)
self.DrawIndicator(area, short_ma2)
self.DrawOwnTrades(area)
def _observe_protection_quote(self, quote):
# The high-level handler activates native protection before the callback, including between bars.
pass
def _create_ma(self, ma_type, length):
if ma_type == MA_SMA:
ma = SimpleMovingAverage()
elif ma_type == MA_EMA:
ma = ExponentialMovingAverage()
elif ma_type == MA_HMA:
ma = HullMovingAverage()
elif ma_type == MA_RMA:
ma = SmoothedMovingAverage()
else:
ma = WeightedMovingAverage()
ma.Length = length
return ma
def _process_candle(self, candle, long_ma1_value, long_ma2_value, short_ma1_value, short_ma2_value):
if candle.State != CandleStates.Finished:
return
current = (float(long_ma1_value), float(long_ma2_value), float(short_ma1_value), float(short_ma2_value), float(candle.ClosePrice))
prev = self._prev
self._prev = current
if prev is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
long_ma1, long_ma2, short_ma1, short_ma2, close = current
last_long_ma1, last_long_ma2, last_short_ma1, last_short_ma2, last_close = prev
direction = int(self._direction.Value)
allow_long = direction != DIRECTION_SHORT
allow_short = direction != DIRECTION_LONG
long_signal = last_long_ma1 <= last_long_ma2 and long_ma1 > long_ma2
short_signal = last_short_ma1 >= last_short_ma2 and short_ma1 < short_ma2
if allow_long and long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
return
if allow_short and short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
return
if not self._enable_alt_exit.Value or self.Position == 0:
return
option = int(self._alt_exit_ma_option.Value)
if option == ALT_LONG_MA2:
exit_ma, last_exit_ma = long_ma2, last_long_ma2
elif option == ALT_SHORT_MA1:
exit_ma, last_exit_ma = short_ma1, last_short_ma1
elif option == ALT_SHORT_MA2:
exit_ma, last_exit_ma = short_ma2, last_short_ma2
else:
exit_ma, last_exit_ma = long_ma1, last_long_ma1
if self.Position > 0 and last_close >= last_exit_ma and close < exit_ma:
self.SellMarket(self.Position)
elif self.Position < 0 and last_close <= last_exit_ma and close > exit_ma:
self.BuyMarket(-self.Position)
def CreateClone(self):
return four_wma_tp_sl_strategy()