BTCUSD 調整可能SLTP戦略
この戦略はSMA(10)とSMA(25)のクロスオーバーにEMA(150)フィルターを組み合わせてBTCUSDを取引します。ロングエントリーはプルバックを待ちます:クロスオーバー後に戻り率が追跡され、価格がそのレベルを再度上抜けるとロングポジションが建てられます。ショートエントリーは価格がEMAを下回っている間に弱気クロスオーバーが発生した時点で即座に実行されます。
エグジットは調整可能なテイクプロフィット、ストップロス、ブレイクイーブン距離を使用します。EMA(150)の下でSMA(10)がSMA(25)を下抜ける場合もロングポジションは決済されます。
詳細
- エントリー条件:
- ロング:SMA(10)がSMA(25)を上抜け、その後価格が設定パーセンテージ分戻り、戻りレベルを再上抜け。
- ショート:EMA(150)の下でSMA(10)がSMA(25)を下抜け。
- ロング/ショート: ロングとショート。
- エグジット条件:
- 設定可能なテイクプロフィット、ストップロス、ブレイクイーブン距離。
- EMA(150)の下でSMA(10)がSMA(25)を下抜けた際にロング決済。
- ストップ: あり(ポイント単位で調整可能)。
- デフォルト値:
FastSmaLength= 10SlowSmaLength= 25EmaFilterLength= 150TakeProfitDistance= 1000StopLossDistance= 250BreakEvenTrigger= 500RetracementPercentage= 0.01
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: ロング & ショート
- インジケーター: SMA, EMA
- ストップ: あり
- 複雑さ: 中程度
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// BTCUSD Adjustable SLTP strategy.
/// A bullish SMA(FastSmaLength)/SMA(SlowSmaLength) crossover arms a long setup at close * (1 - RetracementPercentage); the long opens
/// when the close crosses back above that level. A bearish crossover cancels the setup, closes a long while the close is below
/// EMA(EmaFilterLength) and in that case also opens a short. Take profit, stop loss and break-even trigger are distances in price
/// steps from the entry close; once the trigger is reached the stop moves to the entry price.
/// </summary>
public class BtcusdAdjustableSltpStrategy : Strategy
{
private readonly StrategyParam<int> _fastSmaLength;
private readonly StrategyParam<int> _slowSmaLength;
private readonly StrategyParam<int> _emaFilterLength;
private readonly StrategyParam<decimal> _takeProfitDistance;
private readonly StrategyParam<decimal> _stopLossDistance;
private readonly StrategyParam<decimal> _breakEvenTrigger;
private readonly StrategyParam<decimal> _retracementPercentage;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
private decimal? _prevClose;
private decimal? _retracementLevel;
private decimal _entryPrice;
private bool _breakEven;
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastSmaLength
{
get => _fastSmaLength.Value;
set => _fastSmaLength.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowSmaLength
{
get => _slowSmaLength.Value;
set => _slowSmaLength.Value = value;
}
/// <summary>
/// EMA filter period.
/// </summary>
public int EmaFilterLength
{
get => _emaFilterLength.Value;
set => _emaFilterLength.Value = value;
}
/// <summary>
/// Take profit distance in price steps.
/// </summary>
public decimal TakeProfitDistance
{
get => _takeProfitDistance.Value;
set => _takeProfitDistance.Value = value;
}
/// <summary>
/// Stop loss distance in price steps.
/// </summary>
public decimal StopLossDistance
{
get => _stopLossDistance.Value;
set => _stopLossDistance.Value = value;
}
/// <summary>
/// Profit in price steps that moves the stop to the entry price.
/// </summary>
public decimal BreakEvenTrigger
{
get => _breakEvenTrigger.Value;
set => _breakEvenTrigger.Value = value;
}
/// <summary>
/// Retracement below the crossover close as a fraction (0.01 = 1%).
/// </summary>
public decimal RetracementPercentage
{
get => _retracementPercentage.Value;
set => _retracementPercentage.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BtcusdAdjustableSltpStrategy()
{
_fastSmaLength = Param(nameof(FastSmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("Fast SMA Length", "Fast SMA period", "Indicators");
_slowSmaLength = Param(nameof(SlowSmaLength), 25)
.SetGreaterThanZero()
.SetDisplay("Slow SMA Length", "Slow SMA period", "Indicators");
_emaFilterLength = Param(nameof(EmaFilterLength), 150)
.SetGreaterThanZero()
.SetDisplay("EMA Filter Length", "EMA filter period", "Indicators");
_takeProfitDistance = Param(nameof(TakeProfitDistance), 1000m)
.SetNotNegative()
.SetDisplay("Take Profit Distance", "Take profit distance in price steps", "Risk");
_stopLossDistance = Param(nameof(StopLossDistance), 250m)
.SetNotNegative()
.SetDisplay("Stop Loss Distance", "Stop loss distance in price steps", "Risk");
_breakEvenTrigger = Param(nameof(BreakEvenTrigger), 500m)
.SetNotNegative()
.SetDisplay("Break Even Trigger", "Profit in price steps that moves the stop to the entry price", "Risk");
_retracementPercentage = Param(nameof(RetracementPercentage), 0.01m)
.SetNotNegative()
.SetDisplay("Retracement Percentage", "Retracement below the crossover close as a fraction", "Entry");
_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();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var fast = new SimpleMovingAverage { Length = FastSmaLength };
var slow = new SimpleMovingAverage { Length = SlowSmaLength };
var ema = new ExponentialMovingAverage { Length = EmaFilterLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
private void ResetState()
{
_prevFast = null;
_prevSlow = null;
_prevClose = null;
_retracementLevel = null;
_entryPrice = 0;
_breakEven = false;
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal ema)
{
if (candle.State != CandleStates.Finished)
return;
var close = candle.ClosePrice;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
var prevClose = _prevClose;
_prevFast = fast;
_prevSlow = slow;
_prevClose = close;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (CheckStops(candle))
return;
if (prevFast is not decimal pf || prevSlow is not decimal ps || prevClose is not decimal pc)
return;
var bullCross = pf <= ps && fast > slow;
var bearCross = pf >= ps && fast < slow;
if (bullCross)
{
_retracementLevel = close * (1m - RetracementPercentage);
}
else if (bearCross)
{
_retracementLevel = null;
if (close < ema)
{
if (Position >= 0)
Enter(false, close);
return;
}
}
if (_retracementLevel is decimal level && pc <= level && close > level && Position <= 0)
{
_retracementLevel = null;
Enter(true, close);
}
}
private void Enter(bool isLong, decimal price)
{
var volume = Volume + Math.Abs(Position);
if (isLong)
BuyMarket(volume);
else
SellMarket(volume);
_entryPrice = price;
_breakEven = false;
}
private bool CheckStops(ICandleMessage candle)
{
if (Position == 0 || _entryPrice <= 0)
return false;
var step = Security?.PriceStep ?? 1m;
var take = TakeProfitDistance * step;
var stopDistance = StopLossDistance * step;
var trigger = BreakEvenTrigger * step;
if (Position > 0)
{
decimal? stop = _breakEven ? _entryPrice : StopLossDistance > 0 ? _entryPrice - stopDistance : null;
if ((stop is decimal s && candle.LowPrice <= s) || (TakeProfitDistance > 0 && candle.HighPrice >= _entryPrice + take))
{
SellMarket(Position);
_entryPrice = 0;
return true;
}
if (BreakEvenTrigger > 0 && candle.HighPrice - _entryPrice >= trigger)
_breakEven = true;
}
else
{
decimal? stop = _breakEven ? _entryPrice : StopLossDistance > 0 ? _entryPrice + stopDistance : null;
if ((stop is decimal s && candle.HighPrice >= s) || (TakeProfitDistance > 0 && candle.LowPrice <= _entryPrice - take))
{
BuyMarket(-Position);
_entryPrice = 0;
return true;
}
if (BreakEvenTrigger > 0 && _entryPrice - candle.LowPrice >= trigger)
_breakEven = true;
}
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, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class btcusd_adjustable_sltp_strategy(Strategy):
"""
BTCUSD Adjustable SLTP strategy.
A bullish SMA(FastSmaLength)/SMA(SlowSmaLength) crossover arms a long setup at close * (1 - RetracementPercentage); the long opens
when the close crosses back above that level. A bearish crossover cancels the setup, closes a long while the close is below
EMA(EmaFilterLength) and in that case also opens a short. Take profit, stop loss and break-even trigger are distances in price
steps from the entry close; once the trigger is reached the stop moves to the entry price.
"""
def __init__(self):
super(btcusd_adjustable_sltp_strategy, self).__init__()
self._fast_sma_length = self.Param("FastSmaLength", 10).SetGreaterThanZero().SetDisplay("Fast SMA Length", "Fast SMA period", "Indicators")
self._slow_sma_length = self.Param("SlowSmaLength", 25).SetGreaterThanZero().SetDisplay("Slow SMA Length", "Slow SMA period", "Indicators")
self._ema_filter_length = self.Param("EmaFilterLength", 150).SetGreaterThanZero().SetDisplay("EMA Filter Length", "EMA filter period", "Indicators")
self._take_profit_distance = self.Param("TakeProfitDistance", 1000.0).SetNotNegative().SetDisplay("Take Profit Distance", "Take profit distance in price steps", "Risk")
self._stop_loss_distance = self.Param("StopLossDistance", 250.0).SetNotNegative().SetDisplay("Stop Loss Distance", "Stop loss distance in price steps", "Risk")
self._break_even_trigger = self.Param("BreakEvenTrigger", 500.0).SetNotNegative().SetDisplay("Break Even Trigger", "Profit in price steps that moves the stop to the entry price", "Risk")
self._retracement_percentage = self.Param("RetracementPercentage", 0.01).SetNotNegative().SetDisplay("Retracement Percentage", "Retracement below the crossover close as a fraction", "Entry")
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._prev_close = None
self._retracement_level = None
self._entry_price = Decimal(0)
self._break_even = False
def OnReseted(self):
super(btcusd_adjustable_sltp_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(btcusd_adjustable_sltp_strategy, self).OnStarted2(time)
self._reset_state()
fast = SimpleMovingAverage()
fast.Length = self._fast_sma_length.Value
slow = SimpleMovingAverage()
slow.Length = self._slow_sma_length.Value
ema = ExponentialMovingAverage()
ema.Length = self._ema_filter_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, ema, 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.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast, slow, ema):
if candle.State != CandleStates.Finished:
return
close = candle.ClosePrice
prev_fast = self._prev_fast
prev_slow = self._prev_slow
prev_close = self._prev_close
self._prev_fast = fast
self._prev_slow = slow
self._prev_close = close
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self._check_stops(candle):
return
if prev_fast is None or prev_slow is None or prev_close is None:
return
bull_cross = prev_fast <= prev_slow and fast > slow
bear_cross = prev_fast >= prev_slow and fast < slow
if bull_cross:
self._retracement_level = close * (Decimal(1) - Decimal(self._retracement_percentage.Value))
elif bear_cross:
self._retracement_level = None
if close < ema:
if self.Position >= 0:
self._enter(False, close)
return
level = self._retracement_level
if level is not None and prev_close <= level and close > level and self.Position <= 0:
self._retracement_level = None
self._enter(True, close)
def _enter(self, is_long, price):
volume = self.Volume + abs(self.Position)
if is_long:
self.BuyMarket(volume)
else:
self.SellMarket(volume)
self._entry_price = price
self._break_even = False
def _check_stops(self, candle):
if self.Position == 0 or self._entry_price <= 0:
return False
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
tp_steps = Decimal(self._take_profit_distance.Value)
sl_steps = Decimal(self._stop_loss_distance.Value)
be_steps = Decimal(self._break_even_trigger.Value)
take = tp_steps * step
stop_distance = sl_steps * step
trigger = be_steps * step
if self.Position > 0:
if self._break_even:
stop = self._entry_price
elif sl_steps > 0:
stop = self._entry_price - stop_distance
else:
stop = None
if (stop is not None and candle.LowPrice <= stop) or (tp_steps > 0 and candle.HighPrice >= self._entry_price + take):
self.SellMarket(self.Position)
self._entry_price = Decimal(0)
return True
if be_steps > 0 and candle.HighPrice - self._entry_price >= trigger:
self._break_even = True
else:
if self._break_even:
stop = self._entry_price
elif sl_steps > 0:
stop = self._entry_price + stop_distance
else:
stop = None
if (stop is not None and candle.HighPrice >= stop) or (tp_steps > 0 and candle.LowPrice <= self._entry_price - take):
self.BuyMarket(-self.Position)
self._entry_price = Decimal(0)
return True
if be_steps > 0 and self._entry_price - candle.LowPrice >= trigger:
self._break_even = True
return False
def CreateClone(self):
return btcusd_adjustable_sltp_strategy()