Estrategia Maximus vX Lite
Conversión del asesor experto de MetaTrader 5 "maximus_vX lite" a la API de alto nivel de StockSharp. La estrategia busca zonas de consolidación por encima y por debajo del precio actual y espera a que el precio se mueva un número configurable de puntos alejándose de esas zonas antes de entrar. El tamaño de la posición se determina a partir de un presupuesto de porcentaje de riesgo opcional, y el beneficio flotante puede desencadenar una liquidación forzada de toda la exposición abierta.
Lógica de la estrategia
- Escaneo histórico – en cada vela finalizada la estrategia mantiene hasta
HistoryDepthvelas y usa una ventana deslizanteRangeLookbackpara detectar máximos y mínimos compactos que forman áreas de consolidación. - Canal superior – cuando se detecta un bloque superior válido, el canal se ancla alrededor del cierre actual con un ancho
de
RangePoints. Si ningún bloque histórico cumple los requisitos, el canal recurre al mismo ancho ajustado al precio actual. - Canal inferior – el bloque inferior se toma directamente de los máximos/mínimos históricos que satisfacen las condiciones
de rango o, si no existen, de un nivel sintético alrededor del cierre actual menos
RangePoints. - Entradas largas – se permiten dos configuraciones largas:
- Ruptura por encima de la consolidación inferior: el precio debe superar
_lowerMaxporDistancePointsy el canal superior debe estar disponible. El take profit usa dos tercios de la distancia entre_lowerMaxy_upperMin, con un mínimo igual aRangePoints. - Ruptura por encima del canal superior: el precio debe superar
_upperMaxporDistancePoints. El take profit se establece en2 * RangePoints.
- Ruptura por encima de la consolidación inferior: el precio debe superar
- Entradas cortas – la lógica simétrica se dispara cuando el precio cae por debajo de
_upperMino_lowerMinporDistancePoints. La configuración corta primaria también usa el objetivo dinámico de dos tercios, mientras que la secundaria usa2 * RangePoints. - Stops y salidas –
StopLossPointsdefine un stop protector fijo cuando es mayor que cero.MinProfitPercentmonitorea el capital flotante frente al último balance plano y cierra todas las posiciones una vez superado el umbral. Las comprobaciones manuales de stop/objetivo emulan el comportamiento del asesor experto original dentro de la estrategia. - Dimensionamiento de posición – cuando
RiskPercentes mayor que cero y hay un stop definido, el volumen de la orden se calcula a partir del valor del portafolio y la distancia del stop. De lo contrario, la estrategia reutiliza la propiedadVolume.
Parámetros
DelayOpen(predeterminado2) – número de barras del período de tiempo durante las cuales se permite añadir al mismo lado.DistancePoints(predeterminado850) – distancia mínima desde el borde de consolidación antes de entrar.RangePoints(predeterminado500) – ancho de las cajas de consolidación.HistoryDepth(predeterminado1000) – número de velas guardadas en memoria para escaneos históricos.RangeLookback(predeterminado40) – longitud de ventana usada para calcular máximos y mínimos locales.CandleType(predeterminadoTimeSpan.FromMinutes(15).TimeFrame()) – período de tiempo usado para los cálculos.RiskPercent(predeterminado5m) – porcentaje del valor del portafolio arriesgado por operación. Poner en cero para usar volumen fijo.StopLossPoints(predeterminado1000) – distancia del stop protector; cero deshabilita el stop.MinProfitPercent(predeterminado1m) – porcentaje de beneficio flotante que fuerza el cierre de todas las posiciones.
Detalles
- Largo/Corto: Ambas direcciones
- Criterios de salida: Stop fijo o take profit, bloqueo de capital mediante
MinProfitPercent - Stops: Stop fijo opcional desde
StopLossPoints - Indicadores: Ninguno (precio puro con análisis de ventana deslizante)
- Marco temporal: Configurable mediante
CandleType(predeterminado 15 minutos) - Complejidad: Intermedio (combina escaneo de historial, objetivos dinámicos y dimensionamiento de riesgo)
- Nivel de riesgo: Alto cuando se usa porcentaje de riesgo debido a la naturaleza de ruptura
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>
/// Implements the Maximus vX Lite consolidation breakout strategy.
/// </summary>
public class MaximusVXLiteStrategy : Strategy
{
private readonly StrategyParam<int> _delayOpen;
private readonly StrategyParam<int> _distancePoints;
private readonly StrategyParam<int> _rangePoints;
private readonly StrategyParam<int> _historyDepth;
private readonly StrategyParam<int> _rangeLookback;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _riskPercent;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<decimal> _minProfitPercent;
private readonly List<CandleInfo> _history = new();
private decimal _upperMax;
private decimal _upperMin;
private decimal _lowerMax;
private decimal _lowerMin;
private decimal _priceStep = 1m;
private decimal _extDistance;
private decimal _extRange;
private decimal _extStopLoss;
private DateTimeOffset? _lastBuyTime;
private DateTimeOffset? _lastSellTime;
private decimal _lastKnownBalance;
private decimal? _activeStop;
private decimal? _activeTake;
private readonly struct CandleInfo
{
public CandleInfo(decimal high, decimal low)
{
High = high;
Low = low;
}
public decimal High { get; }
public decimal Low { get; }
}
/// <summary>
/// Number of timeframe intervals allowed before averaging into the same direction.
/// </summary>
public int DelayOpen
{
get => _delayOpen.Value;
set => _delayOpen.Value = value;
}
/// <summary>
/// Minimum distance in points from the consolidation band before entering a trade.
/// </summary>
public int DistancePoints
{
get => _distancePoints.Value;
set => _distancePoints.Value = value;
}
/// <summary>
/// Size of the consolidation range in points.
/// </summary>
public int RangePoints
{
get => _rangePoints.Value;
set => _rangePoints.Value = value;
}
/// <summary>
/// Number of historical candles considered when searching for ranges.
/// </summary>
public int HistoryDepth
{
get => _historyDepth.Value;
set => _historyDepth.Value = value;
}
/// <summary>
/// Window length used to calculate local highs and lows.
/// </summary>
public int RangeLookback
{
get => _rangeLookback.Value;
set => _rangeLookback.Value = value;
}
/// <summary>
/// Candle type used for calculations and signals.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Risk budget per trade expressed as percent of portfolio value.
/// </summary>
public decimal RiskPercent
{
get => _riskPercent.Value;
set => _riskPercent.Value = value;
}
/// <summary>
/// Stop loss distance in points.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Floating profit percentage that triggers forced position close.
/// </summary>
public decimal MinProfitPercent
{
get => _minProfitPercent.Value;
set => _minProfitPercent.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="MaximusVXLiteStrategy"/> class.
/// </summary>
public MaximusVXLiteStrategy()
{
_delayOpen = Param(nameof(DelayOpen), 2)
.SetNotNegative()
.SetDisplay("Delay Open", "How many timeframe periods allow averaging in the same direction", "Trading Rules");
_distancePoints = Param(nameof(DistancePoints), 850)
.SetGreaterThanZero()
.SetDisplay("Distance", "Minimum distance from consolidation band before trading", "Trading Rules");
_rangePoints = Param(nameof(RangePoints), 500)
.SetGreaterThanZero()
.SetDisplay("Range", "Width of consolidation channel in points", "Trading Rules");
_historyDepth = Param(nameof(HistoryDepth), 200)
.SetGreaterThanZero()
.SetDisplay("History Depth", "Number of candles inspected for consolidation zones", "Data");
_rangeLookback = Param(nameof(RangeLookback), 40)
.SetGreaterThanZero()
.SetDisplay("Range Lookback", "Candles used to calculate local maxima and minima", "Data");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe used by the strategy", "General");
_riskPercent = Param(nameof(RiskPercent), 5m)
.SetNotNegative()
.SetDisplay("Risk Percent", "Portfolio percent risked per trade", "Risk");
_stopLossPoints = Param(nameof(StopLossPoints), 1000)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss distance in points", "Risk");
_minProfitPercent = Param(nameof(MinProfitPercent), 1m)
.SetNotNegative()
.SetDisplay("Min Profit Percent", "Floating profit percent required to close all positions", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_history.Clear();
_upperMax = _upperMin = _lowerMax = _lowerMin = 0m;
_priceStep = 0m;
_extDistance = 0m;
_extRange = 0m;
_extStopLoss = 0m;
_lastBuyTime = null;
_lastSellTime = null;
_activeStop = null;
_activeTake = null;
_lastKnownBalance = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
UpdateDerivedValues();
_lastKnownBalance = Portfolio?.CurrentValue ?? 0m;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(3, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
UpdateDerivedValues();
try
{
UpdateHistory(candle);
}
catch (ArgumentOutOfRangeException)
{
return;
}
if (Position == 0m)
{
var balance = Portfolio?.CurrentValue;
if (balance.HasValue && balance.Value > 0m)
_lastKnownBalance = balance.Value;
_activeStop = null;
_activeTake = null;
}
try
{
FindHighLow(candle.ClosePrice);
}
catch (ArgumentOutOfRangeException)
{
return;
}
if (HandleStopsAndTargets(candle))
return;
TryEnterPositions(candle);
TryLockInProfit();
}
private void UpdateHistory(ICandleMessage candle)
{
// Store the latest completed candle at the front of the list.
_history.Insert(0, new CandleInfo(candle.HighPrice, candle.LowPrice));
while (_history.Count > HistoryDepth)
_history.RemoveAt(_history.Count - 1);
}
private void UpdateDerivedValues()
{
// Convert point-based parameters to actual price distances using the security step.
var step = Security?.PriceStep ?? 1m;
if (step <= 0m)
step = 1m;
_priceStep = step;
_extDistance = DistancePoints * step;
_extRange = RangePoints * step;
_extStopLoss = StopLossPoints * step;
}
private void FindHighLow(decimal currentClose)
{
if (_history.Count == 0)
return;
var recalc = currentClose - 100m * _priceStep > _lowerMax
|| currentClose + 100m * _priceStep < _lowerMin
|| currentClose - 100m * _priceStep > _upperMax
|| currentClose + 100m * _priceStep < _upperMin;
if (!recalc)
return;
var foundUpper = false;
for (var i = 0; i < _history.Count; i++)
{
var high = _history[i].High;
if (currentClose - _extRange <= high)
continue;
var (windowMax, windowMin) = GetRangeWindow(i);
if (windowMax == 0m && windowMin == 0m)
continue;
if (windowMax - windowMin <= _extRange && currentClose + _extRange > windowMax && currentClose + _extRange > windowMin)
{
foundUpper = true;
break;
}
}
var halfRange = RangePoints * 0.5m * _priceStep;
if (!foundUpper)
{
var baseValue = Math.Floor(currentClose + 100m * _priceStep);
_upperMax = baseValue + halfRange;
_upperMin = baseValue - halfRange;
}
else
{
var baseValue = Math.Floor((currentClose + 100m * _priceStep) * 100m) / 100m;
_upperMax = baseValue + halfRange;
_upperMin = baseValue - halfRange;
}
var lowerFound = false;
decimal lowerMax = 0m;
decimal lowerMin = 0m;
for (var i = 0; i < _history.Count; i++)
{
var high = _history[i].High;
if (currentClose - _extRange <= high)
continue;
var (windowMax, windowMin) = GetRangeWindow(i);
if (windowMax == 0m && windowMin == 0m)
continue;
if (windowMax - windowMin <= _extRange && currentClose - _extRange > windowMax && currentClose - _extRange > windowMin)
{
lowerMax = windowMax;
lowerMin = windowMin;
lowerFound = true;
break;
}
}
if (!lowerFound)
{
var baseValue = Math.Floor((currentClose - 100m * _priceStep) * 100m) / 100m;
lowerMax = baseValue + halfRange;
lowerMin = baseValue - halfRange;
}
_lowerMax = lowerMax;
_lowerMin = lowerMin;
}
private (decimal max, decimal min) GetRangeWindow(int startIndex)
{
// Replicates ArrayMaximum/ArrayMinimum over a sliding window.
var count = Math.Min(RangeLookback, _history.Count - startIndex);
if (count <= 0)
return (0m, 0m);
var max = decimal.MinValue;
var min = decimal.MaxValue;
for (var j = 0; j < count; j++)
{
var index = startIndex + j;
if (index >= _history.Count)
break;
var item = _history[index];
if (item.High > max)
max = item.High;
if (item.Low < min)
min = item.Low;
}
return (max, min);
}
private bool HandleStopsAndTargets(ICandleMessage candle)
{
// Manually exit positions when candle extremes touch the stored stop or target.
if (Position > 0m)
{
if (_activeStop.HasValue && candle.LowPrice <= _activeStop.Value)
{
SellMarket(Position);
ResetAfterExit();
return true;
}
if (_activeTake.HasValue && candle.HighPrice >= _activeTake.Value)
{
SellMarket(Position);
ResetAfterExit();
return true;
}
}
else if (Position < 0m)
{
var volume = Math.Abs(Position);
if (_activeStop.HasValue && candle.HighPrice >= _activeStop.Value)
{
BuyMarket(volume);
ResetAfterExit();
return true;
}
if (_activeTake.HasValue && candle.LowPrice <= _activeTake.Value)
{
BuyMarket(volume);
ResetAfterExit();
return true;
}
}
return false;
}
private void TryEnterPositions(ICandleMessage candle)
{
var price = candle.ClosePrice;
if (price <= 0m)
return;
var timeFrame = CandleType.Arg is TimeSpan span ? span : TimeSpan.Zero;
var delayDuration = TimeSpan.FromTicks(timeFrame.Ticks * DelayOpen);
var now = candle.CloseTime;
var hasLong = Position > 0m;
var hasShort = Position < 0m;
var allowAdditionalBuy = !hasLong;
if (DelayOpen > 0 && hasLong)
allowAdditionalBuy = _lastBuyTime.HasValue && (_lastBuyTime.Value + delayDuration) > now;
else if (DelayOpen == 0 && hasLong)
allowAdditionalBuy = false;
var allowAdditionalSell = !hasShort;
if (DelayOpen > 0 && hasShort)
allowAdditionalSell = _lastSellTime.HasValue && (_lastSellTime.Value + delayDuration) > now;
else if (DelayOpen == 0 && hasShort)
allowAdditionalSell = false;
var volume = CalculateOrderVolume();
if (volume <= 0m)
return;
var buyPrimary = _lowerMax != 0m && _upperMin != 0m && price - _extDistance > _lowerMax;
var buySecondary = _upperMax != 0m && price - _extDistance > _upperMax;
if (allowAdditionalBuy && (buyPrimary || buySecondary) && Position <= 0m)
{
var stopPrice = StopLossPoints == 0 ? (decimal?)null : price - _extStopLoss;
decimal? takePrice;
if (buyPrimary)
{
var diff = _upperMin - _lowerMax;
var tempTp = diff / 3m * 2m * _priceStep;
if (tempTp < _extRange)
tempTp = _extRange;
takePrice = price + tempTp;
}
else
{
takePrice = price + 2m * _extRange;
}
var orderVolume = volume + (Position < 0m ? Math.Abs(Position) : 0m);
BuyMarket(orderVolume);
_activeStop = stopPrice;
_activeTake = takePrice;
_lastBuyTime = now;
return;
}
var sellPrimary = _upperMin != 0m && price + _extDistance < _upperMin;
var sellSecondary = _lowerMin != 0m && price + _extDistance < _lowerMin;
if (allowAdditionalSell && (sellPrimary || sellSecondary) && Position >= 0m)
{
var stopPrice = StopLossPoints == 0 ? (decimal?)null : price + _extStopLoss;
decimal? takePrice;
if (sellPrimary)
{
var diff = _upperMin - _lowerMax;
var tempTp = diff / 3m * 2m * _priceStep;
if (tempTp < _extRange)
tempTp = _extRange;
takePrice = price - tempTp;
}
else
{
takePrice = price - 2m * _extRange;
}
var orderVolume = volume + (Position > 0m ? Math.Abs(Position) : 0m);
SellMarket(orderVolume);
_activeStop = stopPrice;
_activeTake = takePrice;
_lastSellTime = now;
}
}
private decimal CalculateOrderVolume()
{
// Use risk budget when a stop loss is available, otherwise fall back to base volume.
var baseVolume = Volume;
if (RiskPercent > 0m && _extStopLoss > 0m)
{
var capital = Portfolio?.CurrentValue ?? 0m;
if (capital > 0m)
{
var riskBudget = capital * (RiskPercent / 100m);
if (riskBudget > 0m)
{
var rawVolume = riskBudget / _extStopLoss;
if (rawVolume > 0m)
baseVolume = rawVolume;
}
}
}
var step = Security?.VolumeStep ?? 1m;
if (step <= 0m)
step = 1m;
var adjusted = Math.Floor(baseVolume / step) * step;
if (adjusted <= 0m)
adjusted = step;
return adjusted;
}
private void TryLockInProfit()
{
if (Position == 0m)
return;
var balance = _lastKnownBalance;
if (balance <= 0m)
return;
var equity = Portfolio?.CurrentValue ?? balance;
var profitPercent = (equity - balance) / balance * 100m;
if (profitPercent > MinProfitPercent)
{
ClosePosition();
ResetAfterExit();
}
}
private void ClosePosition()
{
if (Position > 0m)
SellMarket(Position);
else if (Position < 0m)
BuyMarket(Math.Abs(Position));
}
private void ResetAfterExit()
{
// Clear timers and protective orders so the next setup starts clean.
_activeStop = null;
_activeTake = null;
_lastBuyTime = null;
_lastSellTime = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Messages import DataType, CandleStates
from System import TimeSpan, Math
class maximus_vx_lite_strategy(Strategy):
def __init__(self):
super(maximus_vx_lite_strategy, self).__init__()
self._delay_open = self.Param("DelayOpen", 2)
self._distance_points = self.Param("DistancePoints", 850)
self._range_points = self.Param("RangePoints", 500)
self._history_depth = self.Param("HistoryDepth", 200)
self._range_lookback = self.Param("RangeLookback", 40)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._stop_loss_points = self.Param("StopLossPoints", 1000)
self._history = []
self._upper_max = 0.0
self._upper_min = 0.0
self._lower_max = 0.0
self._lower_min = 0.0
self._price_step = 1.0
self._ext_distance = 0.0
self._ext_range = 0.0
self._ext_stop_loss = 0.0
self._last_buy_time = None
self._last_sell_time = None
self._active_stop = None
self._active_take = None
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(maximus_vx_lite_strategy, self).OnStarted2(time)
self._update_derived_values()
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self._process_candle).Start()
def _update_derived_values(self):
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if step <= 0:
step = 1.0
self._price_step = step
self._ext_distance = self._distance_points.Value * step
self._ext_range = self._range_points.Value * step
self._ext_stop_loss = self._stop_loss_points.Value * step
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._update_derived_values()
self._update_history(candle)
if self.Position == 0:
self._active_stop = None
self._active_take = None
self._find_high_low(float(candle.ClosePrice))
if self._handle_stops_and_targets(candle):
return
self._try_enter_positions(candle)
def _update_history(self, candle):
self._history.insert(0, (float(candle.HighPrice), float(candle.LowPrice)))
while len(self._history) > self._history_depth.Value:
self._history.pop()
def _find_high_low(self, current_close):
if len(self._history) == 0:
return
recalc = (current_close - 100.0 * self._price_step > self._lower_max or
current_close + 100.0 * self._price_step < self._lower_min or
current_close - 100.0 * self._price_step > self._upper_max or
current_close + 100.0 * self._price_step < self._upper_min)
if not recalc:
return
half_range = self._range_points.Value * 0.5 * self._price_step
found_upper = False
for i in range(len(self._history)):
high = self._history[i][0]
if current_close - self._ext_range <= high:
continue
window_max, window_min = self._get_range_window(i)
if window_max == 0 and window_min == 0:
continue
if (window_max - window_min <= self._ext_range and
current_close + self._ext_range > window_max and
current_close + self._ext_range > window_min):
found_upper = True
break
base_value = Math.Floor(current_close + 100.0 * self._price_step)
if found_upper:
base_value = Math.Floor((current_close + 100.0 * self._price_step) * 100.0) / 100.0
self._upper_max = base_value + half_range
self._upper_min = base_value - half_range
lower_found = False
lower_max = 0.0
lower_min = 0.0
for i in range(len(self._history)):
high = self._history[i][0]
if current_close - self._ext_range <= high:
continue
window_max, window_min = self._get_range_window(i)
if window_max == 0 and window_min == 0:
continue
if (window_max - window_min <= self._ext_range and
current_close - self._ext_range > window_max and
current_close - self._ext_range > window_min):
lower_max = window_max
lower_min = window_min
lower_found = True
break
if not lower_found:
base_value = Math.Floor((current_close - 100.0 * self._price_step) * 100.0) / 100.0
lower_max = base_value + half_range
lower_min = base_value - half_range
self._lower_max = lower_max
self._lower_min = lower_min
def _get_range_window(self, start_index):
count = min(self._range_lookback.Value, len(self._history) - start_index)
if count <= 0:
return (0.0, 0.0)
max_val = -1e18
min_val = 1e18
for j in range(count):
index = start_index + j
if index >= len(self._history):
break
h, l = self._history[index]
if h > max_val:
max_val = h
if l < min_val:
min_val = l
return (max_val, min_val)
def _handle_stops_and_targets(self, candle):
if self.Position > 0:
if self._active_stop is not None and float(candle.LowPrice) <= self._active_stop:
self.SellMarket(self.Position)
self._reset_after_exit()
return True
if self._active_take is not None and float(candle.HighPrice) >= self._active_take:
self.SellMarket(self.Position)
self._reset_after_exit()
return True
elif self.Position < 0:
vol = abs(self.Position)
if self._active_stop is not None and float(candle.HighPrice) >= self._active_stop:
self.BuyMarket(vol)
self._reset_after_exit()
return True
if self._active_take is not None and float(candle.LowPrice) <= self._active_take:
self.BuyMarket(vol)
self._reset_after_exit()
return True
return False
def _try_enter_positions(self, candle):
price = float(candle.ClosePrice)
if price <= 0:
return
now = candle.CloseTime
has_long = self.Position > 0
has_short = self.Position < 0
allow_buy = not has_long
allow_sell = not has_short
if self._delay_open.Value == 0:
if has_long:
allow_buy = False
if has_short:
allow_sell = False
buy_primary = self._lower_max != 0 and self._upper_min != 0 and price - self._ext_distance > self._lower_max
buy_secondary = self._upper_max != 0 and price - self._ext_distance > self._upper_max
if allow_buy and (buy_primary or buy_secondary) and self.Position <= 0:
stop_price = price - self._ext_stop_loss if self._stop_loss_points.Value > 0 else None
if buy_primary:
diff = self._upper_min - self._lower_max
temp_tp = diff / 3.0 * 2.0 * self._price_step
if temp_tp < self._ext_range:
temp_tp = self._ext_range
take_price = price + temp_tp
else:
take_price = price + 2.0 * self._ext_range
order_volume = float(self.Volume) + (abs(self.Position) if self.Position < 0 else 0)
self.BuyMarket(order_volume)
self._active_stop = stop_price
self._active_take = take_price
self._last_buy_time = now
return
sell_primary = self._upper_min != 0 and price + self._ext_distance < self._upper_min
sell_secondary = self._lower_min != 0 and price + self._ext_distance < self._lower_min
if allow_sell and (sell_primary or sell_secondary) and self.Position >= 0:
stop_price = price + self._ext_stop_loss if self._stop_loss_points.Value > 0 else None
if sell_primary:
diff = self._upper_min - self._lower_max
temp_tp = diff / 3.0 * 2.0 * self._price_step
if temp_tp < self._ext_range:
temp_tp = self._ext_range
take_price = price - temp_tp
else:
take_price = price - 2.0 * self._ext_range
order_volume = float(self.Volume) + (abs(self.Position) if self.Position > 0 else 0)
self.SellMarket(order_volume)
self._active_stop = stop_price
self._active_take = take_price
self._last_sell_time = now
def _reset_after_exit(self):
self._active_stop = None
self._active_take = None
self._last_buy_time = None
self._last_sell_time = None
def OnReseted(self):
super(maximus_vx_lite_strategy, self).OnReseted()
self._history = []
self._upper_max = 0.0
self._upper_min = 0.0
self._lower_max = 0.0
self._lower_min = 0.0
self._price_step = 1.0
self._ext_distance = 0.0
self._ext_range = 0.0
self._ext_stop_loss = 0.0
self._last_buy_time = None
self._last_sell_time = None
self._active_stop = None
self._active_take = None
def CreateClone(self):
return maximus_vx_lite_strategy()