Estratégia de Operação Contrária MA da Segunda-feira
Esta estratégia recria o assessor especialista MetaTrader "Contrarian trade MA" usando a API de alto nível do StockSharp. Combina contexto semanal com um filtro de entrada apenas na segunda-feira para operar contra extremos. O sistema aguarda uma nova semana de trading, mede o quanto a semana anterior fechou em relação ao maior máximo e ao menor mínimo ao longo da janela de lookback, e verifica se o preço abriu a nova semana no lado oposto de uma média móvel deslocada. Se o mercado terminar a primeira vela diária da semana fora desses limites, uma posição contrária é aberta.
A lógica depende apenas de velas completadas. Uma série diária (padrão) impulsiona entradas e saídas, enquanto uma série semanal fornece os níveis extremos e o sinal de média móvel. Cada vez que uma vela de segunda-feira é completada, a estratégia avalia se a semana anterior terminou acima da faixa de máximos recentes ou abaixo da faixa de mínimos recentes, ou se o valor anterior da média móvel está do outro lado do preço de abertura semanal atual. A suposição é que tais movimentos sobreextendidos tendem a reverter à média durante a semana.
Como funciona
Velas semanais alimentam dois indicadores:
Highest/Lowest encontram o máximo e mínimo extremos ao longo de CalcPeriod semanas.
Uma média móvel configurável (MaPeriod, MaMethod, MaShift, AppliedPrice) processa as mesmas velas semanais.
Velas diárias (ou qualquer TradeCandleType selecionado) acionam decisões de trading assim que se completam.
Na primeira vela completada cujo OpenTime.DayOfWeek == Monday, a estratégia avalia as condições de entrada:
Comprado se o fechamento semanal anterior estiver acima do maior máximo do lookback ou se o valor anterior da MA for maior que o preço de abertura semanal atual (significando que o preço abriu abaixo da MA).
Vendido se o fechamento semanal anterior estiver abaixo do menor mínimo do lookback ou se o valor anterior da MA for menor que o preço de abertura semanal atual (preço abriu acima da MA).
As ordens são enviadas com BuyMarket ou SellMarket usando o volume da estratégia sem médias. Apenas uma posição pode estar aberta de cada vez.
Gestão de saída
Uma distância de stop-loss fixa é calculada como StopLossPips * Security.PriceStep. Quando habilitado (> 0), a estratégia monitora máximas e mínimas de velas diárias; se o preço tocar o nível de stop dentro do dia, a posição é fechada a mercado.
Uma saída baseada em tempo fecha qualquer posição aberta após sete dias desde a entrada (604800 segundos no EA original). A verificação é realizada em cada vela diária completada.
A estratégia nunca abre uma nova operação até que a anterior esteja completamente fechada.
Indicadores e dados
Extremos semanais: indicadores Highest e Lowest anexados à série MaCandleType (padrão: velas de 1 semana).
Média móvel semanal: os métodos Simple, Exponential, Smoothed ou LinearWeighted estão disponíveis. A média móvel pode ser deslocada para frente por MaShift barras para imitar a configuração do MetaTrader e pode consumir diferentes fontes de preço (AppliedPrice).
Período primário:TradeCandleType define quais velas impulsionam o timing das operações; o padrão são velas diárias para que as entradas sejam avaliadas após o primeiro dia da semana de trading ser fechado.
Parâmetros
Nome
Tipo
Padrão
Descrição
CalcPeriod
int
4
Número de velas de período de tempo superior usadas para calcular o maior máximo e menor mínimo.
StopLossPips
int
300
Distância de stop-loss em passos de preço. Defina como 0 para desabilitar o stop de proteção.
MaPeriod
int
7
Comprimento da média móvel semanal.
MaShift
int
0
Deslocamento para frente da média móvel em barras. Espelha o parâmetro de deslocamento de MA do MetaTrader.
MaMethod
MovingAverageMethod
LinearWeighted
Método de cálculo da média móvel (Simple, Exponential, Smoothed, LinearWeighted).
AppliedPrice
AppliedPriceType
Weighted
Fonte de preço alimentada à média móvel (Close, Open, High, Low, Median, Typical, Weighted).
TradeCandleType
DataType
TimeSpan.FromMinutes(5).TimeFrame()
Período primário que aciona entradas e gerencia stops/saídas.
MaCandleType
DataType
TimeSpan.FromDays(7).TimeFrame()
Período superior usado para a média móvel e para calcular os extremos.
Notas
A distância de stop-loss adapta-se ao instrumento multiplicando a contagem de pips por Security.PriceStep. Instrumentos sem um passo definido desativarão efetivamente o stop.
Como a estratégia avalia velas completadas, as entradas ocorrem no fechamento da barra de segunda-feira e não no primeiro tick da semana. Isso mantém o comportamento determinístico entre os backtests.
A lógica assume apenas uma posição aberta; qualquer operação aberta é fechada pelo stop-loss ou pelo timeout de sete dias antes que um novo sinal seja considerado.
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>
/// Contrarian Trade MA Monday strategy using SMA crossover with mean reversion.
/// Buys when fast SMA crosses above slow SMA, sells on reverse.
/// </summary>
public class ContrarianTradeMaMondayStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private SimpleMovingAverage _fast;
private SimpleMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// Fast SMA period.
/// </summary>
public int FastPeriod
{
get => _fastPeriod.Value;
set => _fastPeriod.Value = value;
}
/// <summary>
/// Slow SMA period.
/// </summary>
public int SlowPeriod
{
get => _slowPeriod.Value;
set => _slowPeriod.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="ContrarianTradeMaMondayStrategy"/> class.
/// </summary>
public ContrarianTradeMaMondayStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast Period", "Fast SMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("Slow Period", "Slow SMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fast = null;
_slow = null;
_prevFast = 0;
_prevSlow = 0;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new SimpleMovingAverage { Length = FastPeriod };
_slow = new SimpleMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_fast.IsFormed || !_slow.IsFormed)
{
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (_cooldown > 0)
{
_cooldown--;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// SMA crossover
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
_prevFast = fastValue;
_prevSlow = slowValue;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class contrarian_trade_ma_monday_strategy(Strategy):
def __init__(self):
super(contrarian_trade_ma_monday_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast Period", "Fast SMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 100) \
.SetDisplay("Slow Period", "Slow SMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(contrarian_trade_ma_monday_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(contrarian_trade_ma_monday_strategy, self).OnStarted2(time)
self._fast = SimpleMovingAverage()
self._fast.Length = self.fast_period
self._slow = SimpleMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return contrarian_trade_ma_monday_strategy()