Donky MA TP SL 戦略
この戦略は、2つのテイクプロフィット目標とストップロスを伴う移動平均クロスオーバーをトレードします。速いSMAが遅いSMAを上抜けするとロング、下抜けするとショートに入ります。ポジションの半分を最初の目標で決済し、残りを2番目の目標またはストップロスで決済します。
詳細
- エントリー条件:
- ロング: 速いSMAが遅いSMAを上抜け。
- ショート: 速いSMAが遅いSMAを下抜け。
- ロング/ショート: 両方。
- エグジット条件: 2つの固定テイクプロフィットレベルまたは固定ストップロス。
- ストップ: はい。
- デフォルト値:
FastLength= 10SlowLength= 30TakeProfit1Pct= 0.03mTakeProfit2Pct= 0.06mStopLossPct= 0.01mCandleType= TimeSpan.FromMinutes(1)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: SMA
- ストップ: はい
- 複雑さ: 基本
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 低
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>
/// Donky MA TP SL strategy.
/// The fast SMA crossing above the slow SMA goes long and crossing below goes short, reversing an opposite position.
/// Half of the position closes at the first take-profit level and the remainder at the second level or at the stop-loss.
/// The levels are fractions of the entry price (0.03 means 3%).
/// </summary>
public class DonkyMaTpSlStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _takeProfit1Pct;
private readonly StrategyParam<decimal> _takeProfit2Pct;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal _entryPrice;
private bool _firstTargetDone;
/// <summary>
/// Fast SMA length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow SMA length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// First take-profit distance as a fraction of the entry price.
/// </summary>
public decimal TakeProfit1Pct
{
get => _takeProfit1Pct.Value;
set => _takeProfit1Pct.Value = value;
}
/// <summary>
/// Second take-profit distance as a fraction of the entry price.
/// </summary>
public decimal TakeProfit2Pct
{
get => _takeProfit2Pct.Value;
set => _takeProfit2Pct.Value = value;
}
/// <summary>
/// Stop-loss distance as a fraction of the entry price.
/// </summary>
public decimal StopLossPct
{
get => _stopLossPct.Value;
set => _stopLossPct.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DonkyMaTpSlStrategy()
{
_fastLength = Param(nameof(FastLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast SMA length", "Indicators");
_slowLength = Param(nameof(SlowLength), 30)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow SMA length", "Indicators");
_takeProfit1Pct = Param(nameof(TakeProfit1Pct), 0.03m)
.SetNotNegative()
.SetDisplay("Take Profit 1", "First target as a fraction of the entry price", "Risk");
_takeProfit2Pct = Param(nameof(TakeProfit2Pct), 0.06m)
.SetNotNegative()
.SetDisplay("Take Profit 2", "Second target as a fraction of the entry price", "Risk");
_stopLossPct = Param(nameof(StopLossPct), 0.01m)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop distance as a fraction of the entry price", "Risk");
_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()
{
_prevFast = null;
_prevSlow = null;
_entryPrice = 0m;
_firstTargetDone = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fast = new SimpleMovingAverage { Length = FastLength };
var slow = new SimpleMovingAverage { Length = SlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (ManagePosition(candle))
return;
if (prevFast is not decimal pf || prevSlow is not decimal ps)
return;
var crossUp = pf <= ps && fast > slow;
var crossDown = pf >= ps && fast < slow;
if (crossUp && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
_firstTargetDone = false;
}
else if (crossDown && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
_firstTargetDone = false;
}
}
// Returns true when the position was closed completely on this candle.
private bool ManagePosition(ICandleMessage candle)
{
if (Position == 0 || _entryPrice <= 0)
return false;
if (Position > 0)
{
var stop = _entryPrice * (1 - StopLossPct);
var target1 = _entryPrice * (1 + TakeProfit1Pct);
var target2 = _entryPrice * (1 + TakeProfit2Pct);
if (StopLossPct > 0 && candle.LowPrice <= stop)
{
SellMarket(Position);
return true;
}
if (TakeProfit2Pct > 0 && candle.HighPrice >= target2)
{
SellMarket(Position);
return true;
}
if (!_firstTargetDone && TakeProfit1Pct > 0 && candle.HighPrice >= target1)
{
_firstTargetDone = true;
SellMarket(Position / 2);
}
}
else
{
var stop = _entryPrice * (1 + StopLossPct);
var target1 = _entryPrice * (1 - TakeProfit1Pct);
var target2 = _entryPrice * (1 - TakeProfit2Pct);
var volume = Math.Abs(Position);
if (StopLossPct > 0 && candle.HighPrice >= stop)
{
BuyMarket(volume);
return true;
}
if (TakeProfit2Pct > 0 && candle.LowPrice <= target2)
{
BuyMarket(volume);
return true;
}
if (!_firstTargetDone && TakeProfit1Pct > 0 && candle.LowPrice <= target1)
{
_firstTargetDone = true;
BuyMarket(volume / 2);
}
}
return false;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class donky_ma_tp_sl_strategy(Strategy):
"""
Donky MA TP SL strategy.
The fast SMA crossing above the slow SMA goes long and crossing below goes short, reversing an opposite position.
Half of the position closes at the first take-profit level and the remainder at the second level or at the stop-loss.
The levels are fractions of the entry price (0.03 means 3%).
"""
def __init__(self):
super(donky_ma_tp_sl_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 10).SetGreaterThanZero().SetDisplay("Fast Length", "Fast SMA length", "Indicators")
self._slow_length = self.Param("SlowLength", 30).SetGreaterThanZero().SetDisplay("Slow Length", "Slow SMA length", "Indicators")
self._take_profit1_pct = self.Param("TakeProfit1Pct", 0.03).SetNotNegative().SetDisplay("Take Profit 1", "First target as a fraction of the entry price", "Risk")
self._take_profit2_pct = self.Param("TakeProfit2Pct", 0.06).SetNotNegative().SetDisplay("Take Profit 2", "Second target as a fraction of the entry price", "Risk")
self._stop_loss_pct = self.Param("StopLossPct", 0.01).SetNotNegative().SetDisplay("Stop Loss", "Stop distance as a fraction of the entry price", "Risk")
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_fast = None
self._prev_slow = None
self._entry_price = Decimal(0)
self._first_target_done = False
def OnReseted(self):
super(donky_ma_tp_sl_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(donky_ma_tp_sl_strategy, self).OnStarted2(time)
self._reset_state()
fast = SimpleMovingAverage()
fast.Length = self._fast_length.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._manage_position(candle):
return
if prev_fast is None or prev_slow is None:
return
cross_up = prev_fast <= prev_slow and fast > slow
cross_down = prev_fast >= prev_slow and fast < slow
if cross_up and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._entry_price = candle.ClosePrice
self._first_target_done = False
elif cross_down and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._entry_price = candle.ClosePrice
self._first_target_done = False
# Returns True when the position was closed completely on this candle.
def _manage_position(self, candle):
if self.Position == 0 or self._entry_price <= 0:
return False
tp1 = Decimal(self._take_profit1_pct.Value)
tp2 = Decimal(self._take_profit2_pct.Value)
sl = Decimal(self._stop_loss_pct.Value)
one = Decimal(1)
two = Decimal(2)
if self.Position > 0:
stop = self._entry_price * (one - sl)
target1 = self._entry_price * (one + tp1)
target2 = self._entry_price * (one + tp2)
if sl > 0 and candle.LowPrice <= stop:
self.SellMarket(self.Position)
return True
if tp2 > 0 and candle.HighPrice >= target2:
self.SellMarket(self.Position)
return True
if not self._first_target_done and tp1 > 0 and candle.HighPrice >= target1:
self._first_target_done = True
self.SellMarket(self.Position / two)
else:
stop = self._entry_price * (one + sl)
target1 = self._entry_price * (one - tp1)
target2 = self._entry_price * (one - tp2)
volume = abs(self.Position)
if sl > 0 and candle.HighPrice >= stop:
self.BuyMarket(volume)
return True
if tp2 > 0 and candle.LowPrice <= target2:
self.BuyMarket(volume)
return True
if not self._first_target_done and tp1 > 0 and candle.LowPrice <= target1:
self._first_target_done = True
self.BuyMarket(volume / two)
return False
def CreateClone(self):
return donky_ma_tp_sl_strategy()