Litecoin Trailing Stop Strategy
The Litecoin Trailing Stop Strategy uses the Kaufman Adaptive Moving Average (KAMA) to detect bullish and bearish trends. It opens long positions when KAMA is rising and short positions when it is falling. After a configurable delay, a percentage-based trailing stop protects profits.
Details
- Entry Criteria: KAMA slope with cooldown between entries.
- Long/Short: both directions.
- Exit Criteria: trailing stop.
- Stops: trailing stop after delay.
- Default Values:
KamaLength = 50BarsBetweenEntries = 30TrailingStopPercent = 12DelayBars = 50
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: KAMA
- Stops: Trailing stop
- Complexity: Basic
- Timeframe: Medium-term
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk level: Medium
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>
/// Litecoin trailing stop strategy based on KAMA trend detection.
/// </summary>
public class LitecoinTrailingStopStrategy : Strategy
{
private readonly StrategyParam<int> _kamaLength;
private readonly StrategyParam<int> _barsBetweenEntries;
private readonly StrategyParam<decimal> _trailingStopPercent;
private readonly StrategyParam<int> _delayBars;
private readonly StrategyParam<DataType> _candleType;
private KaufmanAdaptiveMovingAverage _kama;
private decimal _prevKama;
private int _barsSinceEntry;
private int _barsSinceLastTrade;
private decimal _entryPrice;
private decimal _highestPrice;
private decimal _lowestPrice;
/// <summary>
/// KAMA period.
/// </summary>
public int KamaLength
{
get => _kamaLength.Value;
set => _kamaLength.Value = value;
}
/// <summary>
/// Minimum bars between entries.
/// </summary>
public int BarsBetweenEntries
{
get => _barsBetweenEntries.Value;
set => _barsBetweenEntries.Value = value;
}
/// <summary>
/// Trailing stop percent.
/// </summary>
public decimal TrailingStopPercent
{
get => _trailingStopPercent.Value;
set => _trailingStopPercent.Value = value;
}
/// <summary>
/// Bars before trailing starts.
/// </summary>
public int DelayBars
{
get => _delayBars.Value;
set => _delayBars.Value = value;
}
/// <summary>
/// Candle data type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public LitecoinTrailingStopStrategy()
{
_kamaLength = Param(nameof(KamaLength), 20)
.SetGreaterThanZero()
.SetDisplay("KAMA Length", "Period for KAMA indicator", "General")
.SetOptimize(20, 100, 5);
_barsBetweenEntries = Param(nameof(BarsBetweenEntries), 200)
.SetGreaterThanZero()
.SetDisplay("Bars Between Entries", "Minimum bars between new positions", "General")
.SetOptimize(10, 60, 5);
_trailingStopPercent = Param(nameof(TrailingStopPercent), 15m)
.SetGreaterThanZero()
.SetDisplay("Trailing Stop %", "Percent for trailing stop", "Risk")
.SetOptimize(5m, 20m, 1m);
_delayBars = Param(nameof(DelayBars), 50)
.SetGreaterThanZero()
.SetDisplay("Delay Bars", "Bars before trailing starts", "Risk")
.SetOptimize(10, 100, 10);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevKama = 0m;
_barsSinceEntry = 0;
_barsSinceLastTrade = 1000;
_entryPrice = 0m;
_highestPrice = 0m;
_lowestPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_kama = new KaufmanAdaptiveMovingAverage { Length = KamaLength };
_prevKama = 0m;
_barsSinceEntry = 0;
_barsSinceLastTrade = 1000;
_entryPrice = 0m;
_highestPrice = 0m;
_lowestPrice = 0m;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_kama, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _kama);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal kamaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_kama.IsFormed)
return;
var bullish = _prevKama != 0m && kamaValue > _prevKama;
var bearish = _prevKama != 0m && kamaValue < _prevKama;
if (_barsSinceLastTrade < 10000)
_barsSinceLastTrade++;
if (Position != 0)
_barsSinceEntry++;
else
_barsSinceEntry = 0;
var canEnter = _barsSinceLastTrade >= BarsBetweenEntries;
if (bullish && canEnter && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceLastTrade = 0;
_barsSinceEntry = 0;
_entryPrice = candle.ClosePrice;
_highestPrice = _entryPrice;
LogInfo($"Long entry at {candle.ClosePrice}");
}
else if (bearish && canEnter && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceLastTrade = 0;
_barsSinceEntry = 0;
_entryPrice = candle.ClosePrice;
_lowestPrice = _entryPrice;
LogInfo($"Short entry at {candle.ClosePrice}");
}
if (Position > 0)
{
_highestPrice = Math.Max(_highestPrice, candle.HighPrice);
if (_barsSinceEntry >= DelayBars)
{
var trailPercent = TrailingStopPercent / 100m;
var stopPrice = _highestPrice * (1 - trailPercent);
if (candle.LowPrice <= stopPrice)
{
SellMarket(Math.Abs(Position));
LogInfo($"Long exit by trailing stop at {candle.ClosePrice}");
_highestPrice = 0m;
}
}
}
else if (Position < 0)
{
_lowestPrice = Math.Min(_lowestPrice, candle.LowPrice);
if (_barsSinceEntry >= DelayBars)
{
var trailPercent = TrailingStopPercent / 100m;
var stopPrice = _lowestPrice * (1 + trailPercent);
if (candle.HighPrice >= stopPrice)
{
BuyMarket(Math.Abs(Position));
LogInfo($"Short exit by trailing stop at {candle.ClosePrice}");
_lowestPrice = 0m;
}
}
}
_prevKama = kamaValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import KaufmanAdaptiveMovingAverage
from StockSharp.Algo.Strategies import Strategy
class litecoin_trailing_stop_strategy(Strategy):
def __init__(self):
super(litecoin_trailing_stop_strategy, self).__init__()
self._kama_length = self.Param("KamaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("KAMA Length", "Period for KAMA indicator", "General")
self._bars_between_entries = self.Param("BarsBetweenEntries", 200) \
.SetGreaterThanZero() \
.SetDisplay("Bars Between Entries", "Minimum bars between new positions", "General")
self._trailing_stop_percent = self.Param("TrailingStopPercent", 15.0) \
.SetGreaterThanZero() \
.SetDisplay("Trailing Stop %", "Percent for trailing stop", "Risk")
self._delay_bars = self.Param("DelayBars", 50) \
.SetGreaterThanZero() \
.SetDisplay("Delay Bars", "Bars before trailing starts", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_kama = 0.0
self._bars_since_entry = 0
self._bars_since_last_trade = 1000
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(litecoin_trailing_stop_strategy, self).OnReseted()
self._prev_kama = 0.0
self._bars_since_entry = 0
self._bars_since_last_trade = 1000
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
def OnStarted2(self, time):
super(litecoin_trailing_stop_strategy, self).OnStarted2(time)
self._prev_kama = 0.0
self._bars_since_entry = 0
self._bars_since_last_trade = 1000
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 0.0
self._kama = KaufmanAdaptiveMovingAverage()
self._kama.Length = self._kama_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._kama, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._kama)
self.DrawOwnTrades(area)
def OnProcess(self, candle, kama_value):
if candle.State != CandleStates.Finished:
return
if not self._kama.IsFormed:
return
kv = float(kama_value)
bullish = self._prev_kama != 0.0 and kv > self._prev_kama
bearish = self._prev_kama != 0.0 and kv < self._prev_kama
if self._bars_since_last_trade < 10000:
self._bars_since_last_trade += 1
if self.Position != 0:
self._bars_since_entry += 1
else:
self._bars_since_entry = 0
can_enter = self._bars_since_last_trade >= self._bars_between_entries.Value
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if bullish and can_enter and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._bars_since_last_trade = 0
self._bars_since_entry = 0
self._entry_price = close
self._highest_price = self._entry_price
elif bearish and can_enter and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._bars_since_last_trade = 0
self._bars_since_entry = 0
self._entry_price = close
self._lowest_price = self._entry_price
if self.Position > 0:
self._highest_price = max(self._highest_price, high)
if self._bars_since_entry >= self._delay_bars.Value:
trail_pct = float(self._trailing_stop_percent.Value) / 100.0
stop_price = self._highest_price * (1.0 - trail_pct)
if low <= stop_price:
self.SellMarket(abs(self.Position))
self._highest_price = 0.0
elif self.Position < 0:
self._lowest_price = min(self._lowest_price, low)
if self._bars_since_entry >= self._delay_bars.Value:
trail_pct = float(self._trailing_stop_percent.Value) / 100.0
stop_price = self._lowest_price * (1.0 + trail_pct)
if high >= stop_price:
self.BuyMarket(abs(self.Position))
self._lowest_price = 0.0
self._prev_kama = kv
def CreateClone(self):
return litecoin_trailing_stop_strategy()