Estratégia de sorteio aleatório de máquina de pinball
Visão geral
Esta estratégia é uma conversão StockSharp direta do MetaTrader 4 consultor especialista Pinball_machine.mq4. O robô original desenhou
inteiros aleatórios em cada tick recebido e abria uma ordem de mercado sempre que dois desses sorteios correspondiam. A versão StockSharp
preserva o mesmo comportamento de loteria: em cada vela finalizada do intervalo de tempo selecionado, o algoritmo executa dois pares de
sorteia aleatoriamente e entra em uma posição de mercado longa ou curta quando o par correspondente contém valores iguais. Stop-loss e take-profit
as distâncias também são aleatórias em cada avaliação, reproduzindo a sensação da rotina original de "pinball", onde as negociações saltam e
fora imprevisível.
Lógica de negociação
Assine as velas definidas pelo parâmetro CandleType e aguarde as barras totalmente formadas.
Para cada vela finalizada gere quatro inteiros distribuídos uniformemente em [0, RandomMaxValue]. O primeiro par pertence ao
potencial entrada longa, o segundo par pertence à potencial entrada curta.
Desenhe dois inteiros adicionais entre MinStopLossPoints/MaxStopLossPoints e MinTakeProfitPoints/MaxTakeProfitPoints para
determinar as distâncias de proteção (expressas em etapas de preços) compartilhadas por ambos os lados da avaliação.
Se o primeiro e o segundo números inteiros aleatórios corresponderem, envie uma ordem de compra a mercado com volume TradeVolume. Se o terceiro e o quarto
valores coincidem, envie uma ordem de venda a mercado com o mesmo volume. Ambas as condições podem disparar dentro da mesma vela, exatamente como em
a versão MQL onde as ordens de compra e venda eram eventos independentes.
Anexe imediatamente uma ordem de stop-loss e de take-profit (se a distância desenhada for maior que zero). As distâncias são interpretadas
como múltiplos do PriceStep do instrumento, espelhando o multiplicador Point usado em MetaTrader.
Gerenciamento de pedidos e controles de risco
StartProtection() é invocado quando a estratégia é iniciada para que StockSharp gerencie ordens de proteção em nome da estratégia.
Cada entrada mede a posição resultante (Position ± TradeVolume) e a passa para SetStopLoss e SetTakeProfit, que
permite que a plataforma consolide ordens de proteção mesmo quando várias negociações estão em execução ao mesmo tempo.
Se os parâmetros de distância mínima ou máxima forem definidos como zero ou um número negativo, a proteção correspondente é
pulou para esse ciclo.
Parâmetros
Parâmetro
Descrição
TradeVolume
Tamanho do pedido em lotes/contratos enviados para cada entrada aleatória.
CandleType
Prazo das velas que acionam os sorteios aleatórios. Períodos mais curtos emulam o EA original baseado em ticks mais de perto.
RandomMaxValue
Limite superior inclusivo para sorteios de inteiros. Um valor maior diminui a probabilidade de correspondência de números e, portanto, reduz a frequência de negociação.
MinStopLossPoints
Limite inferior (em etapas de preço) para a distância de stop-loss gerada aleatoriamente.
MaxStopLossPoints
Limite superior (em etapas de preço) para a distância do stop loss.
MinTakeProfitPoints
Limite inferior (em etapas de preço) para a distância de lucro gerada aleatoriamente.
MaxTakeProfitPoints
Limite superior (em etapas de preço) para a distância de realização do lucro.
RandomSeed
Semente do gerador de números pseudo-aleatórios. Zero mantém o comportamento baseado no tempo; qualquer outro valor torna a sequência reproduzível.
Notas de implementação
O script MetaTrader foi orientado por ticks; a porta StockSharp usa conclusões de vela porque o API de alto nível opera em eventos de série temporal. Definir um CandleType muito curto (por exemplo, velas de um segundo ou de tick) restaura a natureza acelerada do original.
Os valores de stop-loss e take-profit são gerados uma vez por avaliação e reutilizados para as ramificações longas e curtas, exatamente como na fonte EA.
Certifique-se de que o instrumento negociado expõe um PriceStep válido, caso contrário, as distâncias de proteção expressas em pontos podem precisar de ajuste manual.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Pinball Machine: Pseudo-random entry with ATR-based risk management.
/// Uses candle hash to generate deterministic random signals.
/// </summary>
public class PinballMachineRandomDrawStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private int _candleCount;
public PinballMachineRandomDrawStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_candleCount = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_candleCount = 0;
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
_candleCount++;
var close = candle.ClosePrice;
// Exit management
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m)
{
BuyMarket();
_entryPrice = 0;
}
}
// Pseudo-random entry based on candle price hash
if (Position == 0)
{
var hash = (int)(close * 100m) ^ _candleCount;
var mod = Math.Abs(hash) % 10;
if (mod < 3)
{
_entryPrice = close;
BuyMarket();
}
else if (mod > 6)
{
_entryPrice = close;
SellMarket();
}
}
}
}
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
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class pinball_machine_random_draw_strategy(Strategy):
def __init__(self):
super(pinball_machine_random_draw_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR Length", "ATR period for stops", "Indicators")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(pinball_machine_random_draw_strategy, self).OnReseted()
self._entry_price = 0
self._candle_count = 0
def OnStarted2(self, time):
super(pinball_machine_random_draw_strategy, self).OnStarted2(time)
self._entry_price = 0
self._candle_count = 0
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if atr_val <= 0:
return
self._candle_count += 1
close = candle.ClosePrice
if self.Position > 0:
if close >= self._entry_price + atr_val * 2 or close <= self._entry_price - atr_val * 1.5:
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if close <= self._entry_price - atr_val * 2 or close >= self._entry_price + atr_val * 1.5:
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
h = int(float(close) * 100) ^ self._candle_count
mod = abs(h) % 10
if mod < 3:
self._entry_price = close
self.BuyMarket()
elif mod > 6:
self._entry_price = close
self.SellMarket()
def CreateClone(self):
return pinball_machine_random_draw_strategy()