Demo GPT - Estrategia de Scalping de Day Trading
Entra en largo tras dos velas verdes consecutivas cuando la segunda abre por encima del cierre de la primera y el volumen supera su media de 20 barras. Cierra la posición en dos velas rojas consecutivas donde la segunda abre por debajo del cierre de la primera bajo la misma condición de volumen. Calcula EMA(20), VWAP y niveles Camarilla como referencia.
Reglas ejecutadas y valores predeterminados
Predeterminados: EmaLength=20, VolumeLookback=20, CandleType=1 minuto. Tras al menos 20 velas terminadas anteriores, volumeHigh = TotalVolume actual > media de los 20 volúmenes anteriores, excluyendo la vela actual de su media. Solo desde plano, compra largo cuando las dos velas son verdes (Close > Open), la actual tiene Open > Close previo y se confirma el volumen. Cierra toda la posición larga restante cuando ambas son rojas (Close < Open), Open actual < Close previo y hay volumen confirmado. Sin hueco estricto de apertura o filtro de volumen no hay señal, nunca abre cortos. Solo para referencia: EMA20 de cierres; VWAP diario sum(precio típico × volumen)/sum(volumen), reiniciado cada día; Camarilla del día anterior H3/L3 = cierre anterior ± 1.1 × (máximo − mínimo) / 4. No filtran operaciones. Sin stop-loss adicional.
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>
/// Two-candle scalp: long on two green candles with a gap up and a volume
/// spike; exit on two red candles with a gap down and a volume spike.
/// EMA20, session VWAP and prior-day Camarilla levels are reference-only.
/// </summary>
public class DemoGptDayTradingScalpingStrategy : Strategy
{
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _volumeLookback;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema;
private readonly List<decimal> _priorVolumes = [];
private decimal? _previousClose;
private bool _previousGreen;
private bool _previousRed;
private DateTime? _sessionDate;
private decimal _sessionHigh;
private decimal _sessionLow;
private decimal _sessionClose;
private decimal _sessionSumPriceVolume;
private decimal _sessionSumVolume;
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int VolumeLookback { get => _volumeLookback.Value; set => _volumeLookback.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>Reference-only EMA of close prices.</summary>
public decimal ReferenceEma { get; private set; }
/// <summary>Current session VWAP, weighted by total candle volume.</summary>
public decimal ReferenceVwap { get; private set; }
/// <summary>Prior-day Camarilla H3 level.</summary>
public decimal ReferenceCamarillaH3 { get; private set; }
/// <summary>Prior-day Camarilla L3 level.</summary>
public decimal ReferenceCamarillaL3 { get; private set; }
public DemoGptDayTradingScalpingStrategy()
{
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "Reference-only exponential moving average", "Indicators");
_volumeLookback = Param(nameof(VolumeLookback), 20)
.SetGreaterThanZero()
.SetDisplay("Volume Lookback", "Preceding candles in the volume confirmation", "Signals");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Scalping candle interval", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_priorVolumes.Clear();
_previousClose = null;
_previousGreen = false;
_previousRed = false;
_sessionDate = null;
_sessionHigh = 0m;
_sessionLow = 0m;
_sessionClose = 0m;
_sessionSumPriceVolume = 0m;
_sessionSumVolume = 0m;
ReferenceEma = 0m;
ReferenceVwap = 0m;
ReferenceCamarillaH3 = 0m;
ReferenceCamarillaL3 = 0m;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaLength };
_priorVolumes.Clear();
_previousClose = null;
_previousGreen = false;
_previousRed = false;
_sessionDate = null;
_sessionSumPriceVolume = 0m;
_sessionSumVolume = 0m;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_ema, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue)
{
if (candle.State != CandleStates.Finished)
return;
ReferenceEma = emaValue;
var date = candle.OpenTime.Date;
if (_sessionDate != date)
{
if (_sessionDate is not null)
{
var range = _sessionHigh - _sessionLow;
ReferenceCamarillaH3 = _sessionClose + range * 1.1m / 4m;
ReferenceCamarillaL3 = _sessionClose - range * 1.1m / 4m;
}
_sessionDate = date;
_sessionHigh = candle.HighPrice;
_sessionLow = candle.LowPrice;
_sessionSumPriceVolume = 0m;
_sessionSumVolume = 0m;
}
_sessionHigh = Math.Max(_sessionHigh, candle.HighPrice);
_sessionLow = Math.Min(_sessionLow, candle.LowPrice);
_sessionClose = candle.ClosePrice;
var volume = Math.Max(0m, candle.TotalVolume);
_sessionSumPriceVolume += ((candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m) * volume;
_sessionSumVolume += volume;
ReferenceVwap = _sessionSumVolume > 0m
? _sessionSumPriceVolume / _sessionSumVolume
: candle.ClosePrice;
decimal volumeSum = 0m;
foreach (var previousVolume in _priorVolumes)
volumeSum += previousVolume;
var volumeConfirmed = _priorVolumes.Count >= VolumeLookback &&
candle.TotalVolume > volumeSum / VolumeLookback;
var green = candle.ClosePrice > candle.OpenPrice;
var red = candle.ClosePrice < candle.OpenPrice;
var upGap = _previousClose is decimal lastCloseUp && candle.OpenPrice > lastCloseUp;
var downGap = _previousClose is decimal lastCloseDown && candle.OpenPrice < lastCloseDown;
// The previous-candle flags are updated only AFTER testing signals.
var buy = _previousGreen && green && upGap && volumeConfirmed;
var exit = _previousRed && red && downGap && volumeConfirmed;
_previousClose = candle.ClosePrice;
_previousGreen = green;
_previousRed = red;
_priorVolumes.Add(volume);
if (_priorVolumes.Count > VolumeLookback)
_priorVolumes.RemoveAt(0);
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position == 0m && buy)
BuyMarket();
else if (Position > 0m && exit)
SellMarket(Position);
}
}
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 demo_gpt_day_trading_scalping_strategy(Strategy):
"""Long-only two-candle volume-confirmed gap strategy, with reference levels."""
def __init__(self):
super(demo_gpt_day_trading_scalping_strategy, self).__init__()
self._ema_length = self.Param("EmaLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("EMA Length", "Reference EMA", "Indicators")
self._volume_lookback = self.Param("VolumeLookback", 20) \
.SetGreaterThanZero() \
.SetDisplay("Volume Lookback", "Preceding candles for volume confirmation", "Signals")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1))) \
.SetDisplay("Candle Type", "Scalping candles", "General")
self._ema = None
self._prior_volumes = []
self._previous_close = None
self._previous_green = False
self._previous_red = False
self._session_date = None
self._session_high = 0.0
self._session_low = 0.0
self._session_close = 0.0
self._session_price_volume = 0.0
self._session_volume = 0.0
self.reference_ema = 0.0
self.reference_vwap = 0.0
self.reference_camarilla_h3 = 0.0
self.reference_camarilla_l3 = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(demo_gpt_day_trading_scalping_strategy, self).OnReseted()
self._ema = None
self._prior_volumes = []
self._previous_close = None
self._previous_green = False
self._previous_red = False
self._session_date = None
self._session_high = 0.0
self._session_low = 0.0
self._session_close = 0.0
self._session_price_volume = 0.0
self._session_volume = 0.0
self.reference_ema = 0.0
self.reference_vwap = 0.0
self.reference_camarilla_h3 = 0.0
self.reference_camarilla_l3 = 0.0
def OnStarted2(self, time):
super(demo_gpt_day_trading_scalping_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
self._prior_volumes = []
self._previous_close = None
self._previous_green = False
self._previous_red = False
self._session_date = None
self._session_price_volume = 0.0
self._session_volume = 0.0
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
self.reference_ema = float(ema_value)
date = candle.OpenTime.Date
hi = float(candle.HighPrice)
lo = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self._session_date != date:
if self._session_date is not None:
range_val = self._session_high - self._session_low
self.reference_camarilla_h3 = self._session_close + range_val * 1.1 / 4.0
self.reference_camarilla_l3 = self._session_close - range_val * 1.1 / 4.0
self._session_date = date
self._session_high = hi
self._session_low = lo
self._session_price_volume = 0.0
self._session_volume = 0.0
self._session_high = max(self._session_high, hi)
self._session_low = min(self._session_low, lo)
self._session_close = close
volume = max(0.0, float(candle.TotalVolume))
self._session_price_volume += ((hi + lo + close) / 3.0) * volume
self._session_volume += volume
self.reference_vwap = (
self._session_price_volume / self._session_volume
if self._session_volume > 0 else close
)
length = int(self._volume_lookback.Value)
volume_confirmed = (
len(self._prior_volumes) >= length and
float(candle.TotalVolume) > sum(self._prior_volumes) / length
)
green = close > float(candle.OpenPrice)
red = close < float(candle.OpenPrice)
up_gap = self._previous_close is not None and float(candle.OpenPrice) > self._previous_close
down_gap = self._previous_close is not None and float(candle.OpenPrice) < self._previous_close
buy = self._previous_green and green and up_gap and volume_confirmed
exit_signal = self._previous_red and red and down_gap and volume_confirmed
self._previous_close = close
self._previous_green = green
self._previous_red = red
self._prior_volumes.append(volume)
if len(self._prior_volumes) > length:
self._prior_volumes.pop(0)
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position == 0 and buy:
self.BuyMarket()
elif self.Position > 0 and exit_signal:
self.SellMarket(self.Position)
def CreateClone(self):
return demo_gpt_day_trading_scalping_strategy()