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>
/// Strategy that ports the Blau Triple Stochastic Index expert advisor.
/// Supports zero breakdown and trend twist entry modes with optional position permissions.
/// </summary>
public class BlauTStochIndicatorStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<SmoothingMethods> _smoothingMethod;
private readonly StrategyParam<int> _momentumLength;
private readonly StrategyParam<int> _firstSmoothing;
private readonly StrategyParam<int> _secondSmoothing;
private readonly StrategyParam<int> _thirdSmoothing;
private readonly StrategyParam<int> _phase;
private readonly StrategyParam<AppliedPriceTypes> _priceType;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<BlauEntryModes> _mode;
private readonly StrategyParam<bool> _allowLongEntries;
private readonly StrategyParam<bool> _allowShortEntries;
private readonly StrategyParam<bool> _allowLongExits;
private readonly StrategyParam<bool> _allowShortExits;
private readonly StrategyParam<int> _takeProfitPoints;
private readonly StrategyParam<int> _stopLossPoints;
private BlauTripleStochastic _indicator;
private readonly List<decimal> _indicatorValues = new();
/// <summary>
/// Available entry algorithms.
/// </summary>
public enum BlauEntryModes
{
/// <summary>
/// Trade zero line breakdown of the Blau Triple Stochastic Index.
/// </summary>
Breakdown,
/// <summary>
/// Trade twists of the indicator slope.
/// </summary>
Twist
}
/// <summary>
/// Supported smoothing techniques.
/// </summary>
public enum SmoothingMethods
{
/// <summary>
/// Exponential moving average.
/// </summary>
Ema,
/// <summary>
/// Simple moving average.
/// </summary>
Sma,
/// <summary>
/// Smoothed (RMA) moving average.
/// </summary>
Smma,
/// <summary>
/// Weighted moving average.
/// </summary>
Lwma
}
/// <summary>
/// Applied price options.
/// </summary>
public enum AppliedPriceTypes
{
/// <summary>
/// Closing price.
/// </summary>
Close,
/// <summary>
/// Opening price.
/// </summary>
Open,
/// <summary>
/// Highest price.
/// </summary>
High,
/// <summary>
/// Lowest price.
/// </summary>
Low,
/// <summary>
/// Median price (high + low) / 2.
/// </summary>
Median,
/// <summary>
/// Typical price (close + high + low) / 3.
/// </summary>
Typical,
/// <summary>
/// Weighted price (2 * close + high + low) / 4.
/// </summary>
Weighted,
/// <summary>
/// Simple price (open + close) / 2.
/// </summary>
Simple,
/// <summary>
/// Quarted price (open + close + high + low) / 4.
/// </summary>
Quarted,
/// <summary>
/// Trend-following price variant #0.
/// </summary>
TrendFollow0,
/// <summary>
/// Trend-following price variant #1.
/// </summary>
TrendFollow1,
/// <summary>
/// DeMark price.
/// </summary>
Demark
}
/// <summary>
/// Candle type for analysis.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Smoothing algorithm applied to stochastic and range series.
/// </summary>
public SmoothingMethods Smoothing
{
get => _smoothingMethod.Value;
set => _smoothingMethod.Value = value;
}
/// <summary>
/// Momentum lookback for the Blau Triple Stochastic Index.
/// </summary>
public int MomentumLength
{
get => _momentumLength.Value;
set => _momentumLength.Value = value;
}
/// <summary>
/// Length of the first smoothing stage.
/// </summary>
public int FirstSmoothing
{
get => _firstSmoothing.Value;
set => _firstSmoothing.Value = value;
}
/// <summary>
/// Length of the second smoothing stage.
/// </summary>
public int SecondSmoothing
{
get => _secondSmoothing.Value;
set => _secondSmoothing.Value = value;
}
/// <summary>
/// Length of the third smoothing stage.
/// </summary>
public int ThirdSmoothing
{
get => _thirdSmoothing.Value;
set => _thirdSmoothing.Value = value;
}
/// <summary>
/// Phase parameter retained for compatibility.
/// </summary>
public int Phase
{
get => _phase.Value;
set => _phase.Value = value;
}
/// <summary>
/// Applied price selection.
/// </summary>
public AppliedPriceTypes PriceType
{
get => _priceType.Value;
set => _priceType.Value = value;
}
/// <summary>
/// Bar shift used to evaluate the indicator.
/// </summary>
public int SignalBar
{
get => _signalBar.Value;
set => _signalBar.Value = value;
}
/// <summary>
/// Entry algorithm.
/// </summary>
public BlauEntryModes Mode
{
get => _mode.Value;
set => _mode.Value = value;
}
/// <summary>
/// Allow long entries.
/// </summary>
public bool AllowLongEntries
{
get => _allowLongEntries.Value;
set => _allowLongEntries.Value = value;
}
/// <summary>
/// Allow short entries.
/// </summary>
public bool AllowShortEntries
{
get => _allowShortEntries.Value;
set => _allowShortEntries.Value = value;
}
/// <summary>
/// Allow closing long positions.
/// </summary>
public bool AllowLongExits
{
get => _allowLongExits.Value;
set => _allowLongExits.Value = value;
}
/// <summary>
/// Allow closing short positions.
/// </summary>
public bool AllowShortExits
{
get => _allowShortExits.Value;
set => _allowShortExits.Value = value;
}
/// <summary>
/// Take profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Stop loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Initialize strategy parameters.
/// </summary>
public BlauTStochIndicatorStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for indicator calculation", "General");
_smoothingMethod = Param(nameof(Smoothing), SmoothingMethods.Ema)
.SetDisplay("Smoothing", "Moving average type for smoothing", "Indicator");
_momentumLength = Param(nameof(MomentumLength), 5)
.SetGreaterThanZero()
.SetDisplay("Momentum Length", "Lookback for highest and lowest prices", "Indicator");
_firstSmoothing = Param(nameof(FirstSmoothing), 5)
.SetGreaterThanZero()
.SetDisplay("First Smoothing", "Length of the first smoothing stage", "Indicator");
_secondSmoothing = Param(nameof(SecondSmoothing), 8)
.SetGreaterThanZero()
.SetDisplay("Second Smoothing", "Length of the second smoothing stage", "Indicator");
_thirdSmoothing = Param(nameof(ThirdSmoothing), 3)
.SetGreaterThanZero()
.SetDisplay("Third Smoothing", "Length of the third smoothing stage", "Indicator");
_phase = Param(nameof(Phase), 15)
.SetDisplay("Phase", "Compatibility phase parameter", "Indicator");
_priceType = Param(nameof(PriceType), AppliedPriceTypes.Close)
.SetDisplay("Applied Price", "Price source for momentum calculation", "Indicator");
_signalBar = Param(nameof(SignalBar), 1)
.SetGreaterThanZero()
.SetDisplay("Signal Bar", "Closed bar index used for signals", "Signals");
_mode = Param(nameof(Mode), BlauEntryModes.Twist)
.SetDisplay("Mode", "Entry algorithm", "Signals");
_allowLongEntries = Param(nameof(AllowLongEntries), true)
.SetDisplay("Allow Long Entries", "Enable opening long positions", "Permissions");
_allowShortEntries = Param(nameof(AllowShortEntries), true)
.SetDisplay("Allow Short Entries", "Enable opening short positions", "Permissions");
_allowLongExits = Param(nameof(AllowLongExits), true)
.SetDisplay("Allow Long Exits", "Enable closing long positions", "Permissions");
_allowShortExits = Param(nameof(AllowShortExits), true)
.SetDisplay("Allow Short Exits", "Enable closing short positions", "Permissions");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
.SetNotNegative()
.SetDisplay("Take Profit Points", "Take profit distance in price steps", "Risk");
_stopLossPoints = Param(nameof(StopLossPoints), 1000)
.SetNotNegative()
.SetDisplay("Stop Loss Points", "Stop loss distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_indicatorValues.Clear();
_indicator = null;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_indicator = new BlauTripleStochastic
{
Method = Smoothing,
Period = MomentumLength,
Smooth1 = FirstSmoothing,
Smooth2 = SecondSmoothing,
Smooth3 = ThirdSmoothing,
PriceType = PriceType
};
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _indicator);
DrawOwnTrades(area);
}
}
private decimal _entryPrice;
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (trade?.Trade == null) return;
if (Position != 0m && _entryPrice == 0m)
_entryPrice = trade.Trade.Price;
if (Position == 0m)
_entryPrice = 0m;
}
private void HandleProtectiveLevels(ICandleMessage candle)
{
var step = Security?.PriceStep ?? 1m;
if (step <= 0m) return;
if (Position > 0m)
{
if (StopLossPoints > 0 && candle.LowPrice <= _entryPrice - StopLossPoints * step)
{
SellMarket(Position);
return;
}
if (TakeProfitPoints > 0 && candle.HighPrice >= _entryPrice + TakeProfitPoints * step)
{
SellMarket(Position);
return;
}
}
else if (Position < 0m)
{
var abs = Math.Abs(Position);
if (StopLossPoints > 0 && candle.HighPrice >= _entryPrice + StopLossPoints * step)
{
BuyMarket(abs);
return;
}
if (TakeProfitPoints > 0 && candle.LowPrice <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket(abs);
return;
}
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
HandleProtectiveLevels(candle);
var indicatorValue = _indicator.Process(candle);
if (_indicator is null || !_indicator.IsFormed)
{
var rawValue = indicatorValue.ToDecimal();
_indicatorValues.Add(rawValue);
TrimHistory();
return;
}
var currentValue = indicatorValue.ToDecimal();
_indicatorValues.Add(currentValue);
TrimHistory();
var maxOffset = Mode == BlauEntryModes.Twist ? 2 : 1;
if (_indicatorValues.Count < SignalBar + maxOffset)
return;
var value0 = GetShiftValue(0);
var value1 = GetShiftValue(1);
var value2 = maxOffset >= 2 ? GetShiftValue(2) : 0m;
var openLong = false;
var openShort = false;
var closeLong = false;
var closeShort = false;
switch (Mode)
{
case BlauEntryModes.Breakdown:
{
if (value1 > 0m)
{
if (AllowLongEntries && value0 <= 0m)
openLong = true;
if (AllowShortExits)
closeShort = true;
}
if (value1 < 0m)
{
if (AllowShortEntries && value0 >= 0m)
openShort = true;
if (AllowLongExits)
closeLong = true;
}
break;
}
case BlauEntryModes.Twist:
{
if (value1 < value2)
{
if (AllowLongEntries && value0 >= value1)
openLong = true;
if (AllowShortExits)
closeShort = true;
}
if (value1 > value2)
{
if (AllowShortEntries && value0 <= value1)
openShort = true;
if (AllowLongExits)
closeLong = true;
}
break;
}
}
if (closeLong && Position > 0m)
SellMarket(Position);
if (closeShort && Position < 0m)
BuyMarket(-Position);
if (openLong && Position <= 0m)
{
var volume = Volume + (Position < 0m ? -Position : 0m);
if (volume > 0m)
BuyMarket(volume);
}
if (openShort && Position >= 0m)
{
var volume = Volume + (Position > 0m ? Position : 0m);
if (volume > 0m)
SellMarket(volume);
}
}
private void TrimHistory()
{
var maxLength = Math.Max(SignalBar + 5, 10);
while (_indicatorValues.Count > maxLength)
_indicatorValues.RemoveAt(0);
}
private decimal GetShiftValue(int offset)
{
var index = _indicatorValues.Count - SignalBar - offset;
if (index < 0 || index >= _indicatorValues.Count)
throw new InvalidOperationException("Insufficient indicator history for the requested shift.");
return _indicatorValues[index];
}
private class BlauTripleStochastic : BaseIndicator
{
public SmoothingMethods Method { get; set; } = SmoothingMethods.Ema;
public int Period { get; set; } = 20;
public int Smooth1 { get; set; } = 5;
public int Smooth2 { get; set; } = 8;
public int Smooth3 { get; set; } = 3;
public AppliedPriceTypes PriceType { get; set; } = AppliedPriceTypes.Close;
private Highest _highest;
private Lowest _lowest;
private IIndicator _stoch1;
private IIndicator _stoch2;
private IIndicator _stoch3;
private IIndicator _range1;
private IIndicator _range2;
private IIndicator _range3;
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
EnsureInitialized();
_lastInputTime = input.Time;
var candle = input.GetValue<ICandleMessage>();
var price = GetPrice(candle);
var highestValue = _highest!.Process(new DecimalIndicatorValue(_highest, candle.HighPrice, input.Time) { IsFinal = input.IsFinal }).ToDecimal();
var lowestValue = _lowest!.Process(new DecimalIndicatorValue(_lowest, candle.LowPrice, input.Time) { IsFinal = input.IsFinal }).ToDecimal();
if (!_highest.IsFormed || !_lowest.IsFormed)
{
IsFormed = false;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
var stoch = price - lowestValue;
var range = highestValue - lowestValue;
var stage1Stoch = ProcessStage(_stoch1!, stoch);
var stage1Range = ProcessStage(_range1!, range);
if (!_stoch1!.IsFormed || !_range1!.IsFormed)
{
IsFormed = false;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
var stage2Stoch = ProcessStage(_stoch2!, stage1Stoch);
var stage2Range = ProcessStage(_range2!, stage1Range);
if (!_stoch2!.IsFormed || !_range2!.IsFormed)
{
IsFormed = false;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
var stage3Stoch = ProcessStage(_stoch3!, stage2Stoch);
var stage3Range = ProcessStage(_range3!, stage2Range);
if (!_stoch3!.IsFormed || !_range3!.IsFormed || stage3Range == 0m)
{
IsFormed = false;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
IsFormed = true;
var value = 100m * stage3Stoch / stage3Range - 50m;
return new DecimalIndicatorValue(this, value, input.Time);
}
public override void Reset()
{
base.Reset();
_highest = null;
_lowest = null;
_stoch1 = _stoch2 = _stoch3 = null;
_range1 = _range2 = _range3 = null;
IsFormed = false;
}
private void EnsureInitialized()
{
if (_highest != null)
return;
_highest = new Highest { Length = Math.Max(1, Period) };
_lowest = new Lowest { Length = Math.Max(1, Period) };
_stoch1 = CreateSmoother(Smooth1);
_stoch2 = CreateSmoother(Smooth2);
_stoch3 = CreateSmoother(Smooth3);
_range1 = CreateSmoother(Smooth1);
_range2 = CreateSmoother(Smooth2);
_range3 = CreateSmoother(Smooth3);
}
private DateTime _lastInputTime;
private decimal ProcessStage(IIndicator indicator, decimal value)
{
return indicator.Process(new DecimalIndicatorValue(indicator, value, _lastInputTime) { IsFinal = true }).ToDecimal();
}
private IIndicator CreateSmoother(int length)
{
var len = Math.Max(1, length);
return Method switch
{
SmoothingMethods.Sma => new SimpleMovingAverage { Length = len },
SmoothingMethods.Smma => new SmoothedMovingAverage { Length = len },
SmoothingMethods.Lwma => new WeightedMovingAverage { Length = len },
_ => new ExponentialMovingAverage { Length = len },
};
}
private decimal GetPrice(ICandleMessage candle)
{
var open = candle.OpenPrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
return PriceType switch
{
AppliedPriceTypes.Open => open,
AppliedPriceTypes.High => high,
AppliedPriceTypes.Low => low,
AppliedPriceTypes.Median => (high + low) / 2m,
AppliedPriceTypes.Typical => (close + high + low) / 3m,
AppliedPriceTypes.Weighted => (2m * close + high + low) / 4m,
AppliedPriceTypes.Simple => (open + close) / 2m,
AppliedPriceTypes.Quarted => (open + close + high + low) / 4m,
AppliedPriceTypes.TrendFollow0 => close > open ? high : close < open ? low : close,
AppliedPriceTypes.TrendFollow1 => close > open ? (high + close) / 2m : close < open ? (low + close) / 2m : close,
AppliedPriceTypes.Demark => CalculateDemark(open, high, low, close),
_ => close,
};
}
private static decimal CalculateDemark(decimal open, decimal high, decimal low, decimal close)
{
var res = high + low + close;
if (close < open)
res = (res + low) / 2m;
else if (close > open)
res = (res + high) / 2m;
else
res = (res + close) / 2m;
return ((res - low) + (res - high)) / 2m;
}
}
}
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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import (
ExponentialMovingAverage,
Highest, Lowest, SimpleMovingAverage,
SmoothedMovingAverage, WeightedMovingAverage
)
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class blau_t_stoch_indicator_strategy(Strategy):
MODE_BREAKDOWN = 0
MODE_TWIST = 1
SMOOTH_EMA = 0
SMOOTH_SMA = 1
SMOOTH_SMMA = 2
SMOOTH_LWMA = 3
PRICE_CLOSE = 0
PRICE_OPEN = 1
PRICE_HIGH = 2
PRICE_LOW = 3
PRICE_MEDIAN = 4
PRICE_TYPICAL = 5
PRICE_WEIGHTED = 6
PRICE_SIMPLE = 7
PRICE_QUARTED = 8
PRICE_TRENDFOLLOW0 = 9
PRICE_TRENDFOLLOW1 = 10
PRICE_DEMARK = 11
def __init__(self):
super(blau_t_stoch_indicator_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8)))
self._smoothing_method = self.Param("Smoothing", self.SMOOTH_EMA)
self._momentum_length = self.Param("MomentumLength", 5)
self._first_smoothing = self.Param("FirstSmoothing", 5)
self._second_smoothing = self.Param("SecondSmoothing", 8)
self._third_smoothing = self.Param("ThirdSmoothing", 3)
self._phase = self.Param("Phase", 15)
self._price_type = self.Param("PriceType", self.PRICE_CLOSE)
self._signal_bar = self.Param("SignalBar", 1)
self._mode = self.Param("Mode", self.MODE_TWIST)
self._allow_long_entries = self.Param("AllowLongEntries", True)
self._allow_short_entries = self.Param("AllowShortEntries", True)
self._allow_long_exits = self.Param("AllowLongExits", True)
self._allow_short_exits = self.Param("AllowShortExits", True)
self._take_profit_points = self.Param("TakeProfitPoints", 2000)
self._stop_loss_points = self.Param("StopLossPoints", 1000)
self._indicator_values = []
self._entry_price = 0.0
self._highest = None
self._lowest = None
self._stoch1 = None
self._stoch2 = None
self._stoch3 = None
self._range1 = None
self._range2 = None
self._range3 = None
self._ind_formed = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def Smoothing(self):
return self._smoothing_method.Value
@property
def MomentumLength(self):
return self._momentum_length.Value
@property
def FirstSmoothing(self):
return self._first_smoothing.Value
@property
def SecondSmoothing(self):
return self._second_smoothing.Value
@property
def ThirdSmoothing(self):
return self._third_smoothing.Value
@property
def Phase(self):
return self._phase.Value
@property
def PriceType(self):
return self._price_type.Value
@property
def SignalBar(self):
return self._signal_bar.Value
@property
def Mode(self):
return self._mode.Value
@property
def AllowLongEntries(self):
return self._allow_long_entries.Value
@property
def AllowShortEntries(self):
return self._allow_short_entries.Value
@property
def AllowLongExits(self):
return self._allow_long_exits.Value
@property
def AllowShortExits(self):
return self._allow_short_exits.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
def OnStarted2(self, time):
super(blau_t_stoch_indicator_strategy, self).OnStarted2(time)
self._highest = Highest()
self._highest.Length = max(1, self.MomentumLength)
self._lowest = Lowest()
self._lowest.Length = max(1, self.MomentumLength)
self._stoch1 = self._create_smoother(self.FirstSmoothing)
self._stoch2 = self._create_smoother(self.SecondSmoothing)
self._stoch3 = self._create_smoother(self.ThirdSmoothing)
self._range1 = self._create_smoother(self.FirstSmoothing)
self._range2 = self._create_smoother(self.SecondSmoothing)
self._range3 = self._create_smoother(self.ThirdSmoothing)
self._ind_formed = False
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnOwnTradeReceived(self, trade):
super(blau_t_stoch_indicator_strategy, self).OnOwnTradeReceived(trade)
if trade is None or trade.Trade is None:
return
pos = float(self.Position)
if pos != 0 and self._entry_price == 0.0:
self._entry_price = float(trade.Trade.Price)
if pos == 0:
self._entry_price = 0.0
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._handle_protective_levels(candle)
ind_value = self._compute_indicator(candle)
if not self._ind_formed:
self._indicator_values.append(ind_value)
self._trim_history()
return
self._indicator_values.append(ind_value)
self._trim_history()
max_offset = 2 if self.Mode == self.MODE_TWIST else 1
if len(self._indicator_values) < self.SignalBar + max_offset:
return
value0 = self._get_shift_value(0)
value1 = self._get_shift_value(1)
value2 = self._get_shift_value(2) if max_offset >= 2 else 0.0
open_long = False
open_short = False
close_long = False
close_short = False
if self.Mode == self.MODE_BREAKDOWN:
if value1 > 0:
if self.AllowLongEntries and value0 <= 0:
open_long = True
if self.AllowShortExits:
close_short = True
if value1 < 0:
if self.AllowShortEntries and value0 >= 0:
open_short = True
if self.AllowLongExits:
close_long = True
elif self.Mode == self.MODE_TWIST:
if value1 < value2:
if self.AllowLongEntries and value0 >= value1:
open_long = True
if self.AllowShortExits:
close_short = True
if value1 > value2:
if self.AllowShortEntries and value0 <= value1:
open_short = True
if self.AllowLongExits:
close_long = True
pos = float(self.Position)
if close_long and pos > 0:
self.SellMarket(pos)
if close_short and pos < 0:
self.BuyMarket(abs(pos))
pos = float(self.Position)
if open_long and pos <= 0:
volume = float(self.Volume) + (abs(pos) if pos < 0 else 0.0)
if volume > 0:
self.BuyMarket(volume)
if open_short and pos >= 0:
volume = float(self.Volume) + (pos if pos > 0 else 0.0)
if volume > 0:
self.SellMarket(volume)
def _handle_protective_levels(self, candle):
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None else 1.0
if step <= 0:
return
pos = float(self.Position)
if pos > 0:
if self.StopLossPoints > 0 and float(candle.LowPrice) <= self._entry_price - self.StopLossPoints * step:
self.SellMarket(pos)
return
if self.TakeProfitPoints > 0 and float(candle.HighPrice) >= self._entry_price + self.TakeProfitPoints * step:
self.SellMarket(pos)
return
elif pos < 0:
ap = abs(pos)
if self.StopLossPoints > 0 and float(candle.HighPrice) >= self._entry_price + self.StopLossPoints * step:
self.BuyMarket(ap)
return
if self.TakeProfitPoints > 0 and float(candle.LowPrice) <= self._entry_price - self.TakeProfitPoints * step:
self.BuyMarket(ap)
return
def _compute_indicator(self, candle):
self._candle_time = candle.ServerTime
price = self._get_price(candle)
high_val = self._process_stage_ind(self._highest, float(candle.HighPrice))
low_val = self._process_stage_ind(self._lowest, float(candle.LowPrice))
if not self._highest.IsFormed or not self._lowest.IsFormed:
self._ind_formed = False
return 0.0
stoch = price - low_val
rng = high_val - low_val
s1 = self._process_stage(self._stoch1, stoch)
r1 = self._process_stage(self._range1, rng)
if not self._stoch1.IsFormed or not self._range1.IsFormed:
self._ind_formed = False
return 0.0
s2 = self._process_stage(self._stoch2, s1)
r2 = self._process_stage(self._range2, r1)
if not self._stoch2.IsFormed or not self._range2.IsFormed:
self._ind_formed = False
return 0.0
s3 = self._process_stage(self._stoch3, s2)
r3 = self._process_stage(self._range3, r2)
if not self._stoch3.IsFormed or not self._range3.IsFormed or r3 == 0.0:
self._ind_formed = False
return 0.0
self._ind_formed = True
return 100.0 * s3 / r3 - 50.0
def _process_stage_ind(self, indicator, value):
result = process_float(indicator, Decimal(float(value)), self._candle_time, True)
return float(result.Value)
def _process_stage(self, indicator, value):
result = process_float(indicator, Decimal(float(value)), self._candle_time, True)
return float(result.Value)
def _trim_history(self):
max_length = max(self.SignalBar + 5, 10)
while len(self._indicator_values) > max_length:
self._indicator_values.pop(0)
def _get_shift_value(self, offset):
index = len(self._indicator_values) - self.SignalBar - offset
if index < 0 or index >= len(self._indicator_values):
return 0.0
return self._indicator_values[index]
def _create_smoother(self, length):
ln = max(1, length)
if self.Smoothing == self.SMOOTH_SMA:
ind = SimpleMovingAverage()
ind.Length = ln
return ind
elif self.Smoothing == self.SMOOTH_SMMA:
ind = SmoothedMovingAverage()
ind.Length = ln
return ind
elif self.Smoothing == self.SMOOTH_LWMA:
ind = WeightedMovingAverage()
ind.Length = ln
return ind
else:
ind = ExponentialMovingAverage()
ind.Length = ln
return ind
def _get_price(self, candle):
o = float(candle.OpenPrice)
h = float(candle.HighPrice)
l = float(candle.LowPrice)
c = float(candle.ClosePrice)
pt = self.PriceType
if pt == self.PRICE_OPEN:
return o
elif pt == self.PRICE_HIGH:
return h
elif pt == self.PRICE_LOW:
return l
elif pt == self.PRICE_MEDIAN:
return (h + l) / 2.0
elif pt == self.PRICE_TYPICAL:
return (c + h + l) / 3.0
elif pt == self.PRICE_WEIGHTED:
return (2.0 * c + h + l) / 4.0
elif pt == self.PRICE_SIMPLE:
return (o + c) / 2.0
elif pt == self.PRICE_QUARTED:
return (o + c + h + l) / 4.0
elif pt == self.PRICE_TRENDFOLLOW0:
if c > o:
return h
elif c < o:
return l
return c
elif pt == self.PRICE_TRENDFOLLOW1:
if c > o:
return (h + c) / 2.0
elif c < o:
return (l + c) / 2.0
return c
elif pt == self.PRICE_DEMARK:
return self._calculate_demark(o, h, l, c)
return c
def _calculate_demark(self, o, h, l, c):
res = h + l + c
if c < o:
res = (res + l) / 2.0
elif c > o:
res = (res + h) / 2.0
else:
res = (res + c) / 2.0
return ((res - l) + (res - h)) / 2.0
def OnReseted(self):
super(blau_t_stoch_indicator_strategy, self).OnReseted()
self._indicator_values = []
self._entry_price = 0.0
self._ind_formed = False
def CreateClone(self):
return blau_t_stoch_indicator_strategy()