La estrategia Random Bias Trader emula al asesor experto "random trader" de MetaTrader utilizando el API de alto nivel de StockSharp.
En cada vela terminada, la estrategia lanza una moneda virtual y abre una posición en esa dirección cuando no hay ninguna operación activa.
Los niveles de stop-loss y take-profit se derivan de ATR(10) o de una distancia de pip fija y se dimensionan según la relación recompensa-riesgo.
El tamaño de la posición se calcula a partir del porcentaje de riesgo configurado y se limita automáticamente por los límites de volumen del instrumento.
Un activador de equilibrio opcional puede mover el límite de pérdidas al precio de entrada una vez que se alcanza una ganancia de pip específica.
Detalles
Datos: suscripción a una vela definida por CandleType.
Criterios de entrada:
Largo: No hay posición abierta, el lanzamiento de moneda devuelve largo. El precio de entrada es igual al último cierre.
Corto: No hay posición abierta, el lanzamiento de una moneda devuelve corto. El precio de entrada es igual al último cierre.
Criterios de salida:
Stop-loss: la distancia es igual a LossPipDistance × tamaño del pip o LossAtrMultiplier × ATR(10) dependiendo de LossType.
Take-profit: distancia de parada multiplicada por RewardRiskRatio.
Punto de equilibrio: cuando está habilitado, mueve la parada a la entrada después de una ganancia de BreakevenDistancePips.
Paradas: Stop-loss dinámico y toma de ganancias por operación, stop de equilibrio opcional.
Valores predeterminados:
CandleType = período de tiempo de 1 minuto
RewardRiskRatio = 2,0
LossType = pipa
LossAtrMultiplier = 5,0
LossPipDistance = 20 puntos
RiskPercentPerTrade = 1%
UseBreakeven = Habilitado
BreakevenDistancePips = 10 puntos
UseMaxMargin = Habilitado
Filtros:
Categoría: Aleatorio, neutral en cuanto a tendencias
Dirección: Ambas, determinadas por giro.
Indicadores: ATR(10) (opcional)
Complejidad: Principiante
Nivel de riesgo: Medio, depende del tamaño de la parada
Notas
Cuando el volumen basado en el riesgo se vuelve demasiado pequeño, la estrategia opcionalmente vuelve al volumen máximo negociable.
Los niveles stop y objetivo se redondean al paso del precio del instrumento antes de realizar las órdenes.
La lógica de equilibrio mantiene solo una posición abierta en cualquier momento, reflejando la lógica MetaTrader original.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Random trade generator with ATR-based risk management.
/// Opens a random long or short position on each candle when flat,
/// with stop loss and take profit based on ATR.
/// </summary>
public class RandomBiasTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _rewardRiskRatio;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _atrPeriod;
private Random _random;
private decimal _entryPrice;
private int _direction; // 1=long, -1=short, 0=flat
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal RewardRiskRatio
{
get => _rewardRiskRatio.Value;
set => _rewardRiskRatio.Value = value;
}
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
public RandomBiasTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle type", "Data");
_rewardRiskRatio = Param(nameof(RewardRiskRatio), 3m)
.SetDisplay("Reward/Risk", "Reward to risk ratio", "Risk");
_atrMultiplier = Param(nameof(AtrMultiplier), 3m)
.SetDisplay("ATR Multiplier", "ATR multiplier for stop distance", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetDisplay("ATR Period", "ATR indicator period", "Risk");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_random = new Random(42);
var atr = new AverageTrueRange { Length = AtrPeriod };
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 atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (atrValue <= 0)
return;
var stopDistance = atrValue * AtrMultiplier;
var takeDistance = stopDistance * RewardRiskRatio;
var close = candle.ClosePrice;
// Check exit for existing position
if (_direction > 0)
{
if (close >= _entryPrice + takeDistance || close <= _entryPrice - stopDistance)
{
SellMarket();
_direction = 0;
}
return;
}
else if (_direction < 0)
{
if (close <= _entryPrice - takeDistance || close >= _entryPrice + stopDistance)
{
BuyMarket();
_direction = 0;
}
return;
}
// Open new random position
if (_random.Next(4) != 0)
return;
if (_random.Next(2) == 0)
{
BuyMarket();
_entryPrice = close;
_direction = 1;
}
else
{
SellMarket();
_entryPrice = close;
_direction = -1;
}
}
/// <inheritdoc />
protected override void OnReseted()
{
_random = null;
_entryPrice = 0;
_direction = 0;
base.OnReseted();
}
}
import clr
import random
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
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class random_bias_trader_strategy(Strategy):
def __init__(self):
super(random_bias_trader_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._reward_risk_ratio = self.Param("RewardRiskRatio", 3.0)
self._atr_multiplier = self.Param("AtrMultiplier", 3.0)
self._atr_period = self.Param("AtrPeriod", 14)
self._rng = None
self._entry_price = 0.0
self._direction = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RewardRiskRatio(self):
return self._reward_risk_ratio.Value
@RewardRiskRatio.setter
def RewardRiskRatio(self, value):
self._reward_risk_ratio.Value = value
@property
def AtrMultiplier(self):
return self._atr_multiplier.Value
@AtrMultiplier.setter
def AtrMultiplier(self, value):
self._atr_multiplier.Value = value
@property
def AtrPeriod(self):
return self._atr_period.Value
@AtrPeriod.setter
def AtrPeriod(self, value):
self._atr_period.Value = value
def OnReseted(self):
super(random_bias_trader_strategy, self).OnReseted()
self._rng = None
self._entry_price = 0.0
self._direction = 0
def OnStarted2(self, time):
super(random_bias_trader_strategy, self).OnStarted2(time)
self._rng = random.Random(42)
self._entry_price = 0.0
self._direction = 0
atr = AverageTrueRange()
atr.Length = self.AtrPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(atr, self._process_candle).Start()
def _process_candle(self, candle, atr_value):
if candle.State != CandleStates.Finished:
return
atr_val = float(atr_value)
if atr_val <= 0:
return
close = float(candle.ClosePrice)
atr_mult = float(self.AtrMultiplier)
rr_ratio = float(self.RewardRiskRatio)
stop_distance = atr_val * atr_mult
take_distance = stop_distance * rr_ratio
# Check exit for existing position
if self._direction > 0:
if close >= self._entry_price + take_distance or close <= self._entry_price - stop_distance:
self.SellMarket()
self._direction = 0
return
elif self._direction < 0:
if close <= self._entry_price - take_distance or close >= self._entry_price + stop_distance:
self.BuyMarket()
self._direction = 0
return
# Open new random position
if self._rng.randint(0, 3) != 0:
return
if self._rng.randint(0, 1) == 0:
self.BuyMarket()
self._entry_price = close
self._direction = 1
else:
self.SellMarket()
self._entry_price = close
self._direction = -1
def CreateClone(self):
return random_bias_trader_strategy()