Estrategia de Búsqueda de Tazas
Esta estrategia basada en patrones busca formaciones redondeadas en forma de "taza" en los datos de precios. Cuando el precio rompe una taza completada, entra largo o corto según la dirección.
Las pruebas indican un retorno anual promedio de aproximadamente el 47%. Funciona mejor con acciones.
La estrategia compra en rupturas alcistas de tazas y vende en tazas invertidas bajistas. Las posiciones están protegidas por un stop-loss.
Detalles
- Criterios de entrada: Se forma el patrón de taza y el precio rompe el borde.
- Largo/Corto: Ambos.
- Criterios de salida: El precio revierte o alcanza el stop-loss.
- Stops: Sí.
- Valores predeterminados:
Lookback= 150WidthPercent= 5mStopLossPercent= 1mCandleType= TimeSpan.FromMinutes(15)
- Filtros:
- Categoría: Patrón
- Dirección: Largo/Corto
- Indicadores: Price Action
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: Sí
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Cup Finder strategy.
/// The previous Lookback candles are split into thirds. A cup has rims (highest highs of the outer thirds) within WidthPercent
/// of each other and a middle third that stays below the lower rim; a close above the higher rim buys. An inverted cup has
/// troughs (lowest lows of the outer thirds) within WidthPercent of each other and a middle third that stays above the higher
/// trough; a close below the lower trough sells short. An opposite breakout reverses the position and a percent stop limits the loss.
/// </summary>
public class CupFinderStrategy : Strategy
{
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<decimal> _widthPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<(decimal high, decimal low)> _bars = new();
/// <summary>
/// Candles the cup is searched in.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// Maximum difference between the two rims in percent.
/// </summary>
public decimal WidthPercent
{
get => _widthPercent.Value;
set => _widthPercent.Value = value;
}
/// <summary>
/// Stop loss percentage from entry price.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public CupFinderStrategy()
{
_lookback = Param(nameof(Lookback), 150)
.SetRange(3, 10000)
.SetDisplay("Lookback", "Candles the cup is searched in", "Pattern");
_widthPercent = Param(nameof(WidthPercent), 5m)
.SetNotNegative()
.SetDisplay("Width %", "Maximum difference between the two rims in percent", "Pattern");
_stopLossPercent = Param(nameof(StopLossPercent), 1m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_bars.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bars.Clear();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(new Unit(), StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : new Unit(), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var ready = _bars.Count >= Lookback;
var bullish = false;
var bearish = false;
if (ready)
{
var bars = _bars.ToArray();
var third = bars.Length / 3;
var left = bars.Take(third).ToArray();
var middle = bars.Skip(third).Take(bars.Length - 2 * third).ToArray();
var right = bars.Skip(bars.Length - third).ToArray();
var leftRim = left.Max(b => b.high);
var rightRim = right.Max(b => b.high);
var middleHigh = middle.Max(b => b.high);
var rim = Math.Max(leftRim, rightRim);
var isCup = rim > 0 && (rim - Math.Min(leftRim, rightRim)) / rim * 100m <= WidthPercent && middleHigh < Math.Min(leftRim, rightRim);
bullish = isCup && candle.ClosePrice > rim;
var leftTrough = left.Min(b => b.low);
var rightTrough = right.Min(b => b.low);
var middleLow = middle.Min(b => b.low);
var trough = Math.Min(leftTrough, rightTrough);
var higherTrough = Math.Max(leftTrough, rightTrough);
var isInverted = higherTrough > 0 && (higherTrough - trough) / higherTrough * 100m <= WidthPercent && middleLow > higherTrough;
bearish = isInverted && candle.ClosePrice < trough;
}
_bars.Enqueue((candle.HighPrice, candle.LowPrice));
while (_bars.Count > Lookback)
_bars.Dequeue();
if (!ready || !IsFormedAndOnlineAndAllowTrading())
return;
if (bullish && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (bearish && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from collections import deque
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class cup_finder_strategy(Strategy):
"""
Cup Finder strategy.
The previous Lookback candles are split into thirds. A cup has rims (highest highs of the outer thirds) within WidthPercent
of each other and a middle third that stays below the lower rim; a close above the higher rim buys. An inverted cup has
troughs (lowest lows of the outer thirds) within WidthPercent of each other and a middle third that stays above the higher
trough; a close below the lower trough sells short. An opposite breakout reverses the position and a percent stop limits the loss.
"""
def __init__(self):
super(cup_finder_strategy, self).__init__()
self._lookback = self.Param("Lookback", 150).SetRange(3, 10000).SetDisplay("Lookback", "Candles the cup is searched in", "Pattern")
self._width_percent = self.Param("WidthPercent", 5.0).SetNotNegative().SetDisplay("Width %", "Maximum difference between the two rims in percent", "Pattern")
self._stop_loss_percent = self.Param("StopLossPercent", 1.0).SetNotNegative().SetDisplay("Stop Loss %", "Stop loss percentage from entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._bars = deque()
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(cup_finder_strategy, self).OnReseted()
self._bars = deque()
def OnStarted2(self, time):
super(cup_finder_strategy, self).OnStarted2(time)
self._bars = deque()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
stop = float(self._stop_loss_percent.Value)
self.StartProtection(Unit(), Unit(Decimal(stop), UnitTypes.Percent) if stop > 0 else Unit(), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
lookback = self._lookback.Value
width = float(self._width_percent.Value)
close = float(candle.ClosePrice)
ready = len(self._bars) >= lookback
bullish = False
bearish = False
if ready:
bars = list(self._bars)
third = len(bars) // 3
left = bars[:third]
middle = bars[third:len(bars) - third]
right = bars[len(bars) - third:]
left_rim = max(b[0] for b in left)
right_rim = max(b[0] for b in right)
middle_high = max(b[0] for b in middle)
rim = max(left_rim, right_rim)
is_cup = rim > 0 and (rim - min(left_rim, right_rim)) / rim * 100.0 <= width and middle_high < min(left_rim, right_rim)
bullish = is_cup and close > rim
left_trough = min(b[1] for b in left)
right_trough = min(b[1] for b in right)
middle_low = min(b[1] for b in middle)
trough = min(left_trough, right_trough)
higher_trough = max(left_trough, right_trough)
is_inverted = higher_trough > 0 and (higher_trough - trough) / higher_trough * 100.0 <= width and middle_low > higher_trough
bearish = is_inverted and close < trough
self._bars.append((float(candle.HighPrice), float(candle.LowPrice)))
while len(self._bars) > lookback:
self._bars.popleft()
if not ready or not self.IsFormedAndOnlineAndAllowTrading():
return
if bullish and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif bearish and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
def CreateClone(self):
return cup_finder_strategy()