Estratégia PowerHouse SwiftEdge AI v2.10
Estratégia de rompimento baseada em pivôs com filtro de momentum e gerenciamento de risco simples. A entrada comprada ocorre quando o preço rompe o máximo de pivô recente com momentum suficiente e distância mínima do sinal anterior. A entrada vendida é acionada em uma quebra do mínimo de pivô com momentum negativo. As posições usam take-profit e stop-loss especificados em pontos.
Cálculo e execução
Este é um exemplo determinístico; o título histórico não implica um modelo de IA treinado. C# e Python compartilham esta interpretação explícita da descrição acima.
Use velas base concluídas. Um máximo pivô supera estritamente as máximas de PivotPeriod barras vizinhas de cada lado; um mínimo é estritamente inferior às mínimas. Igualdade exclui o pivô. A confirmação ocorre apenas após concluir as barras da direita, sem sinais retroativos. Guarde separadamente o último máximo e mínimo confirmados.
Compra exige Close anterior <= máximo pivô e Close atual > máximo pivô; venda exige o cruzamento inverso do mínimo pivô. Momentum é Close atual - Close de MomentumPeriod barras atrás: estritamente acima de MomentumThreshold para compra ou abaixo do seu negativo para venda. Ambos os lados exigem pelo menos MinSignalBars barras concluídas desde a última entrada aceita; zero desativa o intervalo. Sinais rejeitados não são adiados. Não há filtro EMA nem espera de seis horas.
Entre com Volume sem posição ou reverta completamente com Volume + abs(Position). Uma ordem pendente bloqueia novas ordens. As distâncias são unidades absolutas do preço cotado: um ponto equivale a uma diferença de 1, não a tick, multiplicador de PriceStep ou percentual. Ajuste ao instrumento. A base é a execução real, ponderada por volume em aumentos parciais do mesmo lado; reduzir preserva a base e reverter inicia uma nova.
A proteção local fecha toda a posição a mercado quando o bid de compras ou ask de vendas atinge stop ou take. Fechamentos das velas também verificam a proteção; toques nas máximas/mínimas não simulam eventos intrabar ordenados. A proteção independe do intervalo entre entradas e tem prioridade sobre uma nova entrada na mesma vela. Em lacunas, a execução pode diferir do disparador.
Padrões: PivotPeriod=2, MomentumPeriod=10, MomentumThreshold=0, MinSignalBars=5, StopLossPoints=50, TakeProfitPoints=100, CandleType=TimeSpan.FromMinutes(5).TimeFrame(). Períodos e distâncias de saída positivos; limiar e intervalo não negativos.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Trades confirmed-pivot breakouts with signed close momentum and finished-bar spacing,
/// protected by fixed price-point distances from actual fill prices.
/// </summary>
public class PowerHouseSwiftEdgeAiV210Strategy : Strategy
{
private readonly StrategyParam<int> _pivotPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<decimal> _momentumThreshold;
private readonly StrategyParam<int> _minSignalBars;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private readonly List<(decimal high, decimal low, decimal close)> _bars = [];
private decimal? _pivotHigh;
private decimal? _pivotLow;
private long _barCount;
private long? _lastSignal;
private Order _pendingOrder;
private decimal _filledPosition;
private decimal _entryPrice;
/// <summary>Initializes two-bar pivots, ten-bar momentum, five-bar spacing and 50/100 price-point exits.</summary>
public PowerHouseSwiftEdgeAiV210Strategy()
{
_pivotPeriod = Param(nameof(PivotPeriod), 2).SetGreaterThanZero()
.SetDisplay("Pivot Period", "Strict neighboring bars on each side of a confirmed pivot", "Signals");
_momentumPeriod = Param(nameof(MomentumPeriod), 10).SetGreaterThanZero()
.SetDisplay("Momentum Period", "Finished-bar close lookback", "Signals");
_momentumThreshold = Param(nameof(MomentumThreshold), 0m).SetRange(0m, decimal.MaxValue)
.SetDisplay("Momentum Threshold", "Minimum signed close change in absolute price units", "Signals");
_minSignalBars = Param(nameof(MinSignalBars), 5).SetRange(0, int.MaxValue)
.SetDisplay("Minimum Signal Bars", "Finished-bar interval between accepted entries; zero disables spacing", "Signals");
_stopLossPoints = Param(nameof(StopLossPoints), 50m).SetGreaterThanZero()
.SetDisplay("Stop Loss Points", "Fixed absolute price distance from the actual fill basis", "Protection");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 100m).SetGreaterThanZero()
.SetDisplay("Take Profit Points", "Fixed absolute price distance from the actual fill basis", "Protection");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Finished candles supplying pivots, momentum and spacing", "General");
OrderRegistering += order => _pendingOrder = order;
OwnTradeReceived += ObserveFill;
}
/// <summary>Strict neighboring bars on each side of a pivot.</summary>
public int PivotPeriod { get => _pivotPeriod.Value; set => _pivotPeriod.Value = value; }
/// <summary>Lookback for the signed close-price difference.</summary>
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
/// <summary>Nonnegative absolute-price threshold, exceeded strictly in the entry direction.</summary>
public decimal MomentumThreshold { get => _momentumThreshold.Value; set => _momentumThreshold.Value = value; }
/// <summary>Minimum finished-bar interval between accepted entry orders; zero disables it.</summary>
public int MinSignalBars { get => _minSignalBars.Value; set => _minSignalBars.Value = value; }
/// <summary>Stop distance in absolute quote-price points, not ticks or percent.</summary>
public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
/// <summary>Take-profit distance in absolute quote-price points, not ticks or percent.</summary>
public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
/// <summary>Base finished-candle type; five minutes by default.</summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, DataType.Level1)];
private void ResetState()
{
_bars.Clear();
_pivotHigh = _pivotLow = null;
_barCount = 0;
_lastSignal = null;
_pendingOrder = null;
_filledPosition = _entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
if (PivotPeriod <= 0 || MomentumPeriod <= 0 || MomentumThreshold < 0m
|| MinSignalBars < 0 || StopLossPoints <= 0m || TakeProfitPoints <= 0m)
throw new ArgumentException("Pivot/momentum periods and point distances must be positive; thresholds and spacing cannot be negative.");
ResetState();
foreach (var field in new[] { Level1Fields.BestBidPrice, Level1Fields.BestAskPrice })
{
var quotes = new Subscription(DataType.Level1, Security);
quotes.MarketData.BuildField = field;
SubscribeLevel1(quotes).Bind(ObserveQuote).Start();
}
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private bool IsPending => _pendingOrder is not null && _pendingOrder.State is not (OrderStates.Done or OrderStates.Failed);
private void Submit(Sides side, decimal volume, string comment)
=> RegisterOrder(new Order { Security = Security, Portfolio = Portfolio, Type = OrderTypes.Market, Side = side, Volume = volume, Comment = comment });
private void ObserveFill(Subscription subscription, MyTrade trade)
{
var previous = _filledPosition;
var signed = trade.Order.Side == Sides.Buy ? trade.Trade.Volume : -trade.Trade.Volume;
_filledPosition += signed;
if (_filledPosition == 0m)
_entryPrice = 0m;
else if (previous == 0m || Math.Sign(_filledPosition) != Math.Sign(previous))
_entryPrice = trade.Trade.Price;
else if (Math.Sign(signed) == Math.Sign(previous))
_entryPrice = (_entryPrice * Math.Abs(previous) + trade.Trade.Price * Math.Abs(signed)) / Math.Abs(_filledPosition);
}
private bool TryRiskExit(decimal price)
{
if (Position == 0m || _entryPrice <= 0m || IsPending || !IsFormedAndOnlineAndAllowTrading())
return false;
var profit = (price - _entryPrice) * Math.Sign(Position);
var comment = profit <= -StopLossPoints ? "SwiftEdge stop loss" : profit >= TakeProfitPoints ? "SwiftEdge take profit" : null;
if (comment is null)
return false;
Submit(Position > 0m ? Sides.Sell : Sides.Buy, Math.Abs(Position), comment);
return true;
}
private void ObserveQuote(Level1ChangeMessage quote)
{
var field = Position > 0m ? Level1Fields.BestBidPrice : Level1Fields.BestAskPrice;
if (quote.TryGetDecimal(field) is decimal price && price > 0m)
TryRiskExit(price);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
_barCount++;
var width = checked(2 * PivotPeriod + 1);
_bars.Add((candle.HighPrice, candle.LowPrice, candle.ClosePrice));
if (_bars.Count > Math.Max(width, checked(MomentumPeriod + 1)))
_bars.RemoveAt(0);
if (_bars.Count >= width)
{
var start = _bars.Count - width;
var center = start + PivotPeriod;
var high = true;
var low = true;
for (var i = start; i < _bars.Count; i++)
{
if (i == center)
continue;
high &= _bars[center].high > _bars[i].high;
low &= _bars[center].low < _bars[i].low;
}
if (high)
_pivotHigh = _bars[center].high;
if (low)
_pivotLow = _bars[center].low;
}
if (TryRiskExit(candle.ClosePrice) || IsPending || !IsFormedAndOnlineAndAllowTrading()
|| _bars.Count <= MomentumPeriod || _bars.Count < 2
|| _lastSignal is long last && _barCount - last < MinSignalBars)
return;
var previous = _bars[^2].close;
var momentum = candle.ClosePrice - _bars[_bars.Count - MomentumPeriod - 1].close;
Sides? side = null;
if (_pivotHigh is decimal pivotHigh && previous <= pivotHigh && candle.ClosePrice > pivotHigh
&& momentum > MomentumThreshold && Position <= 0m)
side = Sides.Buy;
else if (_pivotLow is decimal pivotLow && previous >= pivotLow && candle.ClosePrice < pivotLow
&& momentum < -MomentumThreshold && Position >= 0m)
side = Sides.Sell;
if (side is Sides direction)
{
_lastSignal = _barCount;
Submit(direction, Volume + Math.Abs(Position), "SwiftEdge pivot breakout");
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import ArgumentException, Decimal, Int32, Math, TimeSpan
from StockSharp.Messages import CandleStates, DataType, ITickTradeMessage, Level1Fields, OrderStates, OrderTypes, Sides
from StockSharp.BusinessEntities import Order, Subscription
from StockSharp.Algo.Strategies import Strategy
class power_house_swift_edge_ai_v2_10_strategy(Strategy):
def __init__(self):
super(power_house_swift_edge_ai_v2_10_strategy, self).__init__()
self._pivot_period = self.Param("PivotPeriod", 2).SetGreaterThanZero() \
.SetDisplay("Pivot Period", "Strict neighboring bars on each side of a confirmed pivot", "Signals")
self._momentum_period = self.Param("MomentumPeriod", 10).SetGreaterThanZero() \
.SetDisplay("Momentum Period", "Finished-bar close lookback", "Signals")
self._momentum_threshold = self.Param("MomentumThreshold", Decimal.Zero).SetRange(Decimal.Zero, Decimal.MaxValue) \
.SetDisplay("Momentum Threshold", "Minimum signed close change in absolute price units", "Signals")
self._min_signal_bars = self.Param("MinSignalBars", 5).SetRange(0, Int32.MaxValue) \
.SetDisplay("Minimum Signal Bars", "Finished-bar interval between accepted entries; zero disables spacing", "Signals")
self._stop_loss_points = self.Param("StopLossPoints", Decimal(50)).SetGreaterThanZero() \
.SetDisplay("Stop Loss Points", "Fixed absolute price distance from the actual fill basis", "Protection")
self._take_profit_points = self.Param("TakeProfitPoints", Decimal(100)).SetGreaterThanZero() \
.SetDisplay("Take Profit Points", "Fixed absolute price distance from the actual fill basis", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Finished candles supplying pivots, momentum and spacing", "General")
tick_type = clr.GetClrType(ITickTradeMessage)
self._tick_price = tick_type.GetProperty("Price")
self._tick_volume = tick_type.GetProperty("Volume")
self._reset_state()
self.OrderRegistering += self._observe_order
self.OwnTradeReceived += self._observe_fill
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def _reset_state(self):
self._bars = []
self._pivot_high = self._pivot_low = None
self._bar_count = 0
self._last_signal = None
self._pending_order = None
self._filled_position = self._entry_price = Decimal.Zero
def OnReseted(self):
super(power_house_swift_edge_ai_v2_10_strategy, self).OnReseted()
self._reset_state()
def _observe_order(self, order):
self._pending_order = order
def OnStarted2(self, time):
super(power_house_swift_edge_ai_v2_10_strategy, self).OnStarted2(time)
if self._pivot_period.Value <= 0 or self._momentum_period.Value <= 0 or self._momentum_threshold.Value < 0 \
or self._min_signal_bars.Value < 0 or self._stop_loss_points.Value <= 0 or self._take_profit_points.Value <= 0:
raise ArgumentException("Pivot/momentum periods and point distances must be positive; thresholds and spacing cannot be negative.")
self._reset_state()
for field in (Level1Fields.BestBidPrice, Level1Fields.BestAskPrice):
quotes = Subscription(DataType.Level1, self.Security)
quotes.MarketData.BuildField = field
self.SubscribeLevel1(quotes).Bind(self._observe_quote).Start()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _is_pending(self):
order = self._pending_order
return order is not None and order.State != OrderStates.Done and order.State != OrderStates.Failed
def _submit(self, side, volume, comment):
order = Order()
order.Security = self.Security
order.Portfolio = self.Portfolio
order.Type = OrderTypes.Market
order.Side = side
order.Volume = volume
order.Comment = comment
self.RegisterOrder(order)
def _observe_fill(self, subscription, trade):
previous = self._filled_position
price = self._tick_price.GetValue(trade.Trade)
volume = self._tick_volume.GetValue(trade.Trade)
signed = volume if trade.Order.Side == Sides.Buy else -volume
self._filled_position += signed
if self._filled_position == 0:
self._entry_price = Decimal.Zero
elif previous == 0 or Math.Sign(self._filled_position) != Math.Sign(previous):
self._entry_price = price
elif Math.Sign(signed) == Math.Sign(previous):
self._entry_price = (self._entry_price * abs(previous) + price * abs(signed)) / abs(self._filled_position)
def _try_risk_exit(self, price):
if self.Position == 0 or self._entry_price <= 0 or self._is_pending() or not self.IsFormedAndOnlineAndAllowTrading():
return False
profit = (price - self._entry_price) * Math.Sign(self.Position)
comment = "SwiftEdge stop loss" if profit <= -self._stop_loss_points.Value \
else "SwiftEdge take profit" if profit >= self._take_profit_points.Value else None
if comment is None:
return False
self._submit(Sides.Sell if self.Position > 0 else Sides.Buy, abs(self.Position), comment)
return True
def _observe_quote(self, quote):
field = Level1Fields.BestBidPrice if self.Position > 0 else Level1Fields.BestAskPrice
if quote.Changes.ContainsKey(field) and quote.Changes[field] > 0:
self._try_risk_exit(quote.Changes[field])
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
period = self._pivot_period.Value
momentum_period = self._momentum_period.Value
width = 2 * period + 1
self._bars.append((candle.HighPrice, candle.LowPrice, candle.ClosePrice))
if len(self._bars) > max(width, momentum_period + 1):
self._bars.pop(0)
if len(self._bars) >= width:
start = len(self._bars) - width
center = start + period
neighbors = [self._bars[index] for index in range(start, len(self._bars)) if index != center]
if all(self._bars[center][0] > bar[0] for bar in neighbors):
self._pivot_high = self._bars[center][0]
if all(self._bars[center][1] < bar[1] for bar in neighbors):
self._pivot_low = self._bars[center][1]
if self._try_risk_exit(candle.ClosePrice) or self._is_pending() or not self.IsFormedAndOnlineAndAllowTrading() \
or len(self._bars) <= momentum_period or len(self._bars) < 2 \
or self._last_signal is not None and self._bar_count - self._last_signal < self._min_signal_bars.Value:
return
previous = self._bars[-2][2]
momentum = candle.ClosePrice - self._bars[-momentum_period - 1][2]
side = None
if self._pivot_high is not None and previous <= self._pivot_high and candle.ClosePrice > self._pivot_high \
and momentum > self._momentum_threshold.Value and self.Position <= 0:
side = Sides.Buy
elif self._pivot_low is not None and previous >= self._pivot_low and candle.ClosePrice < self._pivot_low \
and momentum < -self._momentum_threshold.Value and self.Position >= 0:
side = Sides.Sell
if side is not None:
self._last_signal = self._bar_count
self._submit(side, self.Volume + abs(self.Position), "SwiftEdge pivot breakout")
def CreateClone(self):
return power_house_swift_edge_ai_v2_10_strategy()