Início
/
Exemplos de estratégias
Ver no GitHub
Estratégia de Divergência Sazonal HMA
Esta estratégia combina a Média Móvel Hull (HMA) com o agrupamento sazonal de interesse aberto para encontrar divergências entre o preço e o posicionamento do mercado. Ela pressupõe que, quando o preço se move temporariamente contra a direção do interesse aberto crescente, uma continuação de tendência é provável. O sistema é projetado para operar tanto comprado quanto vendido, usando a inclinação da HMA para medir o momentum e os dados sazonais de interesse aberto para medir os níveis de participação.
Os testes indicam um retorno anual médio de cerca de 40%. Funciona melhor no mercado de criptomoedas.
Uma configuração de operação ocorre quando a HMA muda em relação à barra anterior enquanto o interesse aberto sazonal confirma o movimento, mas o preço imprime na direção oposta. Essa divergência de alta ou de baixa entre o preço e o posicionamento frequentemente sinaliza o fim de um recuo de curto prazo dentro de uma tendência maior. A estratégia aguarda essas condições antes de entrar e posiciona um stop baseado em volatilidade para gerenciar o risco.
As posições são encerradas quando a inclinação da HMA se inverte, sinalizando que o momentum mudou. Como o nível de stop usa um múltiplo do Average True Range (ATR), o risco se adapta à volatilidade do mercado. Isso ajuda a evitar saídas prematuras durante períodos de expansão e mantém as perdas contidas quando a volatilidade contrai.
Detalhes
Critérios de entrada :
Comprado : HMA(t) > HMA(t-1) && OI_Cluster_Seasonal(t) > OI_Cluster_Seasonal(t-1) && Price(t) < Price(t-1) (divergência de alta).
Vendido : HMA(t) < HMA(t-1) && OI_Cluster_Seasonal(t) < OI_Cluster_Seasonal(t-1) && Price(t) > Price(t-1) (divergência de baixa).
Comprado/Vendido : Ambos os lados.
Critérios de saída :
Comprado : HMA(t) < HMA(t-1) (HMA começa a cair).
Vendido : HMA(t) > HMA(t-1) (HMA começa a subir).
Stops : Sim, stop-loss colocado em N * ATR a partir da entrada.
Valores padrão :
HMA period = 9.
OI_Cluster_Seasonal = OI sazonal em níveis de cluster ao longo de cinco anos.
N = 2 (stop-loss = 2 * ATR).
Filtros :
Categoria: Seguidor de tendência
Direção: Ambos
Indicadores: Múltiplos
Stops: Sim
Complexidade: Complexo
Período: Médio prazo
Sazonalidade: Sim
Redes neurais: Sim
Divergência: Sim
Nível de risco: Alto
namespace StockSharp.Samples.Strategies;
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;
/// <summary>
/// Moving average crossover strategy.
/// Enters long when fast MA crosses above slow MA.
/// Enters short when fast MA crosses below slow MA.
/// Implements stop-loss as a percentage of entry price.
/// </summary>
public class MaCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private bool _isLongPosition;
/// <summary>
/// Fast MA period length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow MA period length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// The type of candles to use for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MaCrossoverStrategy()
{
_fastLength = Param(nameof(FastLength), 100)
.SetGreaterThanZero()
.SetDisplay("Fast MA Length", "Period of the fast moving average", "MA Settings")
.SetOptimize(5, 20, 5);
_slowLength = Param(nameof(SlowLength), 400)
.SetGreaterThanZero()
.SetDisplay("Slow MA Length", "Period of the slow moving average", "MA Settings")
.SetOptimize(20, 100, 10);
_stopLossPercent = Param(nameof(StopLossPercent), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk Management")
.SetOptimize(1.0m, 5.0m, 1.0m);
_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();
// Initialize variables
_entryPrice = 0;
_isLongPosition = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
var fastMa = new ExponentialMovingAverage { Length = FastLength };
var slowMa = new ExponentialMovingAverage { Length = SlowLength };
// Create and setup subscription for candles
var subscription = SubscribeCandles(CandleType);
// Previous values for crossover detection
var previousFastValue = 0m;
var previousSlowValue = 0m;
var wasFastLessThanSlow = false;
var isInitialized = false;
subscription
.Bind(fastMa, slowMa, (candle, fastValue, slowValue) =>
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Check if strategy is ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Initialize on first complete values
if (!isInitialized && fastMa.IsFormed && slowMa.IsFormed)
{
previousFastValue = fastValue;
previousSlowValue = slowValue;
wasFastLessThanSlow = fastValue < slowValue;
isInitialized = true;
LogInfo($"Strategy initialized. Fast MA: {fastValue}, Slow MA: {slowValue}");
return;
}
if (!isInitialized)
return;
// Current crossover state
var isFastLessThanSlow = fastValue < slowValue;
LogInfo($"Candle: {candle.OpenTime}, Close: {candle.ClosePrice}, Fast MA: {fastValue}, Slow MA: {slowValue}");
// Check for crossovers
if (wasFastLessThanSlow != isFastLessThanSlow)
{
// Crossover happened
if (!isFastLessThanSlow) // Fast MA crossed above Slow MA
{
// Buy signal
if (Position <= 0)
{
_entryPrice = candle.ClosePrice;
_isLongPosition = true;
BuyMarket(Volume + Math.Abs(Position));
}
}
else // Fast MA crossed below Slow MA
{
// Sell signal
if (Position >= 0)
{
_entryPrice = candle.ClosePrice;
_isLongPosition = false;
SellMarket(Volume + Math.Abs(Position));
}
}
// Update the crossover state
wasFastLessThanSlow = isFastLessThanSlow;
}
// Update previous values
previousFastValue = fastValue;
previousSlowValue = slowValue;
})
.Start();
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fastMa);
DrawIndicator(area, slowMa);
DrawOwnTrades(area);
}
}
private void CheckStopLoss(decimal currentPrice)
{
if (_entryPrice == 0)
return;
var stopLossThreshold = _stopLossPercent.Value / 100.0m;
if (_isLongPosition && Position > 0)
{
// For long positions, exit if price falls below entry price - stop percentage
var stopPrice = _entryPrice * (1.0m - stopLossThreshold);
if (currentPrice <= stopPrice)
{
SellMarket(Math.Abs(Position));
LogInfo($"Long stop-loss triggered at {currentPrice}. Entry was {_entryPrice}, Stop level: {stopPrice}");
}
}
else if (!_isLongPosition && Position < 0)
{
// For short positions, exit if price rises above entry price + stop percentage
var stopPrice = _entryPrice * (1.0m + stopLossThreshold);
if (currentPrice >= stopPrice)
{
BuyMarket(Math.Abs(Position));
LogInfo($"Short stop-loss triggered at {currentPrice}. Entry was {_entryPrice}, Stop level: {stopPrice}");
}
}
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ma_crossover_strategy(Strategy):
"""
Moving average crossover strategy.
Enters long when fast MA crosses above slow MA.
Enters short when fast MA crosses below slow MA.
"""
def __init__(self):
super(ma_crossover_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 100).SetDisplay("Fast MA Length", "Period of the fast moving average", "MA Settings")
self._slow_length = self.Param("SlowLength", 400).SetDisplay("Slow MA Length", "Period of the slow moving average", "MA Settings")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0).SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._entry_price = 0.0
self._is_long_position = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ma_crossover_strategy, self).OnReseted()
self._entry_price = 0.0
self._is_long_position = False
def OnStarted2(self, time):
super(ma_crossover_strategy, self).OnStarted2(time)
fast_ma = ExponentialMovingAverage()
fast_ma.Length = self._fast_length.Value
slow_ma = ExponentialMovingAverage()
slow_ma.Length = self._slow_length.Value
self._was_fast_less = False
self._is_initialized = False
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast_ma, slow_ma, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ma)
self.DrawIndicator(area, slow_ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fv = float(fast_val)
sv = float(slow_val)
if not self._is_initialized:
self._was_fast_less = fv < sv
self._is_initialized = True
return
is_fast_less = fv < sv
if self._was_fast_less != is_fast_less:
if not is_fast_less:
if self.Position <= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long_position = True
self.BuyMarket()
else:
if self.Position >= 0:
self._entry_price = float(candle.ClosePrice)
self._is_long_position = False
self.SellMarket()
self._was_fast_less = is_fast_less
def CreateClone(self):
return ma_crossover_strategy()