La estrategia Zonal Trading replica el concepto clásico de "zonas" de Bill Williams. Monitorea el color del Awesome Oscillator (AO) y el Accelerator Oscillator (AC). Una barra verde significa que el valor del oscilador aumentó en comparación con la barra anterior, mientras que una barra roja significa que disminuyó. Cuando ambos osciladores se vuelven verdes, la estrategia abre una posición larga. Cuando ambos se vuelven rojos, abre una posición corta. Cualquier color opuesto cierra las posiciones existentes.
Detalles
Criterios de entrada:
Largo: AO aumenta y AC aumenta.
Corto: AO disminuye y AC disminuye.
Criterios de salida:
Largo: AO o AC disminuye.
Corto: AO o AC aumenta.
Stops: ninguno por defecto.
Parámetros:
AoCandleType – marco temporal para el Awesome Oscillator (H4 por defecto).
AcCandleType – marco temporal para el Accelerator Oscillator (H4 por defecto).
BuyOpen, SellOpen – habilitan o deshabilitan las entradas largas y cortas.
BuyClose, SellClose – habilitan o deshabilitan las salidas para posiciones largas y cortas.
Indicadores: Awesome Oscillator (5/34), Accelerator Oscillator (AO menos SMA(5)).
Tipo: seguimiento de momentum, funciona en cualquier mercado y marco temporal donde los osciladores están disponibles.
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>
/// Zone trading strategy based on Bill Williams' Awesome and Accelerator Oscillators.
/// Buys when both oscillators turn green and sells when both turn red.
/// </summary>
public class ZonalTradingOscillatorStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _medians = new();
private readonly List<decimal> _aoValues = new();
private decimal? _prevAo;
private decimal? _prevAc;
private int _aoTrend;
private int _acTrend;
private int _lastSignal;
/// <summary>
/// Candle type for analysis.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ZonalTradingOscillatorStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle type for oscillators", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_medians.Clear();
_aoValues.Clear();
_prevAo = null;
_prevAc = null;
_aoTrend = 0;
_acTrend = 0;
_lastSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_medians.Add((candle.HighPrice + candle.LowPrice) / 2m);
if (_medians.Count > 34)
_medians.RemoveAt(0);
if (_medians.Count < 34)
return;
var ao = GetAverage(_medians, 5) - GetAverage(_medians, 34);
_aoValues.Add(ao);
if (_aoValues.Count > 5)
_aoValues.RemoveAt(0);
if (_aoValues.Count < 5)
{
_prevAo = ao;
return;
}
var ac = ao - GetAverage(_aoValues, 5);
if (_prevAo is not null && _prevAc is not null)
{
_aoTrend = ao > _prevAo ? 1 : ao < _prevAo ? -1 : _aoTrend;
_acTrend = ac > _prevAc ? 1 : ac < _prevAc ? -1 : _acTrend;
if (_aoTrend > 0 && _acTrend > 0 && _lastSignal != 1 && Position <= 0)
{
BuyMarket();
_lastSignal = 1;
}
else if (_aoTrend < 0 && _acTrend < 0 && _lastSignal != -1 && Position >= 0)
{
SellMarket();
_lastSignal = -1;
}
}
_prevAo = ao;
_prevAc = ac;
}
private static decimal GetAverage(List<decimal> values, int length)
{
var start = values.Count - length;
var sum = 0m;
for (var i = start; i < values.Count; i++)
sum += values[i];
return sum / length;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class zonal_trading_oscillator_strategy(Strategy):
def __init__(self):
super(zonal_trading_oscillator_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._medians = []
self._ao_values = []
self._prev_ao = None
self._prev_ac = None
self._ao_trend = 0
self._ac_trend = 0
self._last_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _get_average(self, values, length):
start = len(values) - length
s = 0.0
for i in range(start, len(values)):
s += values[i]
return s / length
def OnStarted2(self, time):
super(zonal_trading_oscillator_strategy, self).OnStarted2(time)
self._medians = []
self._ao_values = []
self._prev_ao = None
self._prev_ac = None
self._ao_trend = 0
self._ac_trend = 0
self._last_signal = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
median = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
self._medians.append(median)
if len(self._medians) > 34:
self._medians.pop(0)
if len(self._medians) < 34:
return
ao = self._get_average(self._medians, 5) - self._get_average(self._medians, 34)
self._ao_values.append(ao)
if len(self._ao_values) > 5:
self._ao_values.pop(0)
if len(self._ao_values) < 5:
self._prev_ao = ao
return
ac = ao - self._get_average(self._ao_values, 5)
if self._prev_ao is not None and self._prev_ac is not None:
if ao > self._prev_ao:
self._ao_trend = 1
elif ao < self._prev_ao:
self._ao_trend = -1
if ac > self._prev_ac:
self._ac_trend = 1
elif ac < self._prev_ac:
self._ac_trend = -1
if self._ao_trend > 0 and self._ac_trend > 0 and self._last_signal != 1 and self.Position <= 0:
self.BuyMarket()
self._last_signal = 1
elif self._ao_trend < 0 and self._ac_trend < 0 and self._last_signal != -1 and self.Position >= 0:
self.SellMarket()
self._last_signal = -1
self._prev_ao = ao
self._prev_ac = ac
def OnReseted(self):
super(zonal_trading_oscillator_strategy, self).OnReseted()
self._medians = []
self._ao_values = []
self._prev_ao = None
self._prev_ac = None
self._ao_trend = 0
self._ac_trend = 0
self._last_signal = 0
def CreateClone(self):
return zonal_trading_oscillator_strategy()