Anuncios de Resultados con Recompras de Acciones
La estrategia de Anuncios de Resultados con Recompras de Acciones compra acciones con programas de recompra activos unos días antes de sus anuncios de resultados y sale poco después del informe.
Detalles
- Criterios de entrada: Comprar
DaysBeforedías antes de los resultados si la empresa tiene una recompra activa. - Largo/Corto: Solo largos.
- Criterios de salida: Vender
DaysAfterdías después de la fecha de resultados. - Stops: No.
- Valores predeterminados:
DaysBefore = 5DaysAfter = 1CandleType = TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Event-driven
- Dirección: Largo
- Indicadores: Buyback + Calendar
- Stops: No
- Complejidad: Intermedio
- Marco temporal: Diario
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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>
/// Strategy that buys the primary instrument before synthetic earnings events when a synthetic buyback regime is active and exits after the event.
/// </summary>
public class EarningsAnnouncementsWithBuybacksStrategy : Strategy
{
private readonly StrategyParam<int> _daysBefore;
private readonly StrategyParam<int> _daysAfter;
private readonly StrategyParam<int> _eventCycleBars;
private readonly StrategyParam<int> _buybackLength;
private readonly StrategyParam<decimal> _buybackThreshold;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _buybackProxy = null!;
private int _barIndex;
private int _holdingRemaining;
private int _cooldownRemaining;
private decimal _latestBuybackValue;
/// <summary>
/// Number of bars before the synthetic earnings event to enter.
/// </summary>
public int DaysBefore
{
get => _daysBefore.Value;
set => _daysBefore.Value = value;
}
/// <summary>
/// Number of bars after the synthetic earnings event to exit.
/// </summary>
public int DaysAfter
{
get => _daysAfter.Value;
set => _daysAfter.Value = value;
}
/// <summary>
/// Distance between synthetic earnings events in bars.
/// </summary>
public int EventCycleBars
{
get => _eventCycleBars.Value;
set => _eventCycleBars.Value = value;
}
/// <summary>
/// Smoothing length for the synthetic buyback activity proxy.
/// </summary>
public int BuybackLength
{
get => _buybackLength.Value;
set => _buybackLength.Value = value;
}
/// <summary>
/// Minimum synthetic buyback score required to enter.
/// </summary>
public decimal BuybackThreshold
{
get => _buybackThreshold.Value;
set => _buybackThreshold.Value = value;
}
/// <summary>
/// Closed candles to wait before another position change.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Stop loss percentage.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Candle type used for price data.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="EarningsAnnouncementsWithBuybacksStrategy"/>.
/// </summary>
public EarningsAnnouncementsWithBuybacksStrategy()
{
_daysBefore = Param(nameof(DaysBefore), 3)
.SetRange(1, 10)
.SetDisplay("Days Before", "Bars before the synthetic earnings event to enter", "Trading");
_daysAfter = Param(nameof(DaysAfter), 1)
.SetRange(1, 10)
.SetDisplay("Days After", "Bars after the synthetic earnings event to exit", "Trading");
_eventCycleBars = Param(nameof(EventCycleBars), 20)
.SetRange(8, 80)
.SetDisplay("Event Cycle Bars", "Distance between synthetic earnings events", "Trading");
_buybackLength = Param(nameof(BuybackLength), 8)
.SetRange(2, 40)
.SetDisplay("Buyback Length", "Smoothing length for the synthetic buyback proxy", "Indicators");
_buybackThreshold = Param(nameof(BuybackThreshold), 0.7m)
.SetRange(-5m, 5m)
.SetDisplay("Buyback Threshold", "Minimum synthetic buyback score required to enter", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 2)
.SetRange(0, 20)
.SetDisplay("Cooldown Bars", "Closed candles to wait before another position change", "Risk");
_stopLoss = Param(nameof(StopLoss), 2.5m)
.SetRange(0.5m, 10m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
if (Security != null)
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_buybackProxy = null!;
_barIndex = 0;
_holdingRemaining = 0;
_cooldownRemaining = 0;
_latestBuybackValue = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (Security == null)
throw new InvalidOperationException("Primary security is not specified.");
_buybackProxy = new ExponentialMovingAverage { Length = BuybackLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
StartProtection(
new Unit(2, UnitTypes.Percent),
new Unit(StopLoss, UnitTypes.Percent));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var buybackSignal = CalculateBuybackSignal(candle);
_latestBuybackValue = _buybackProxy.Process(buybackSignal, candle.OpenTime, true).ToDecimal();
if (!_buybackProxy.IsFormed || !IsFormedAndOnlineAndAllowTrading())
{
_barIndex++;
return;
}
if (_cooldownRemaining > 0)
_cooldownRemaining--;
if (_holdingRemaining > 0)
{
_holdingRemaining--;
if (_holdingRemaining == 0 && Position > 0)
{
SellMarket(Position);
_cooldownRemaining = CooldownBars;
}
}
var barsToEvent = EventCycleBars - (_barIndex % EventCycleBars);
var inEntryWindow = barsToEvent <= DaysBefore && barsToEvent > 0;
var buybackActive = _latestBuybackValue >= BuybackThreshold;
if (_cooldownRemaining == 0 && Position == 0 && inEntryWindow && buybackActive)
{
BuyMarket();
_holdingRemaining = DaysAfter + 1;
_cooldownRemaining = CooldownBars;
}
_barIndex++;
}
private decimal CalculateBuybackSignal(ICandleMessage candle)
{
var priceBase = Math.Max(candle.OpenPrice, 1m);
var range = Math.Max(candle.HighPrice - candle.LowPrice, Security?.PriceStep ?? 1m);
var closeLocation = ((candle.ClosePrice - candle.LowPrice) - (candle.HighPrice - candle.ClosePrice)) / range;
var compression = 1m - Math.Min(0.2m, range / priceBase);
return (closeLocation * 2m) + compression;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class earnings_announcements_with_buybacks_strategy(Strategy):
"""Strategy that buys the primary instrument before synthetic earnings events
when a synthetic buyback regime is active and exits after the event."""
def __init__(self):
super(earnings_announcements_with_buybacks_strategy, self).__init__()
self._days_before = self.Param("DaysBefore", 3) \
.SetRange(1, 10) \
.SetDisplay("Days Before", "Bars before the synthetic earnings event to enter", "Trading")
self._days_after = self.Param("DaysAfter", 1) \
.SetRange(1, 10) \
.SetDisplay("Days After", "Bars after the synthetic earnings event to exit", "Trading")
self._event_cycle_bars = self.Param("EventCycleBars", 20) \
.SetRange(8, 80) \
.SetDisplay("Event Cycle Bars", "Distance between synthetic earnings events", "Trading")
self._buyback_length = self.Param("BuybackLength", 8) \
.SetRange(2, 40) \
.SetDisplay("Buyback Length", "Smoothing length for the synthetic buyback proxy", "Indicators")
self._buyback_threshold = self.Param("BuybackThreshold", 0.7) \
.SetRange(-5.0, 5.0) \
.SetDisplay("Buyback Threshold", "Minimum synthetic buyback score required to enter", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 2) \
.SetRange(0, 20) \
.SetDisplay("Cooldown Bars", "Closed candles to wait before another position change", "Risk")
self._stop_loss = self.Param("StopLoss", 2.5) \
.SetRange(0.5, 10.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._buyback_proxy = None
self._bar_index = 0
self._holding_remaining = 0
self._cooldown_remaining = 0
self._latest_buyback_value = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
result = []
if self.Security is not None:
result.append((self.Security, self.candle_type))
return result
def OnReseted(self):
super(earnings_announcements_with_buybacks_strategy, self).OnReseted()
self._buyback_proxy = None
self._bar_index = 0
self._holding_remaining = 0
self._cooldown_remaining = 0
self._latest_buyback_value = 0.0
def OnStarted2(self, time):
super(earnings_announcements_with_buybacks_strategy, self).OnStarted2(time)
self._buyback_proxy = ExponentialMovingAverage()
self._buyback_proxy.Length = int(self._buyback_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(float(self._stop_loss.Value), UnitTypes.Percent)
)
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
buyback_signal = self.CalculateBuybackSignal(candle)
result = process_float(self._buyback_proxy, buyback_signal, candle.OpenTime, True)
self._latest_buyback_value = float(result)
if not self._buyback_proxy.IsFormed or not self.IsFormedAndOnlineAndAllowTrading():
self._bar_index += 1
return
cooldown = int(self._cooldown_bars.Value)
days_before = int(self._days_before.Value)
days_after = int(self._days_after.Value)
event_cycle = int(self._event_cycle_bars.Value)
buyback_thresh = float(self._buyback_threshold.Value)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
if self._holding_remaining > 0:
self._holding_remaining -= 1
if self._holding_remaining == 0 and self.Position > 0:
self.SellMarket(self.Position)
self._cooldown_remaining = cooldown
bars_to_event = event_cycle - (self._bar_index % event_cycle)
in_entry_window = bars_to_event <= days_before and bars_to_event > 0
buyback_active = self._latest_buyback_value >= buyback_thresh
if self._cooldown_remaining == 0 and self.Position == 0 and in_entry_window and buyback_active:
self.BuyMarket()
self._holding_remaining = days_after + 1
self._cooldown_remaining = cooldown
self._bar_index += 1
def CalculateBuybackSignal(self, candle):
price_base = max(float(candle.OpenPrice), 1.0)
price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
range_val = max(float(candle.HighPrice) - float(candle.LowPrice), price_step)
close_location = ((float(candle.ClosePrice) - float(candle.LowPrice)) - (float(candle.HighPrice) - float(candle.ClosePrice))) / range_val
compression = 1.0 - min(0.2, range_val / price_base)
return (close_location * 2.0) + compression
def CreateClone(self):
return earnings_announcements_with_buybacks_strategy()