Стратегия TDS Global
Стратегия воспроизводит оригинального советника MetaTrader «TDSGlobal», построенного на концепции Triple Screen Александра Элдера. Работа ведётся по дневным свечам: направление тренда определяется наклоном MACD (12, 23, 9), а фильтрация входов осуществляется с помощью Williams %R за 24 периода. Длинные сделки разрешаются, когда MACD растёт и %R указывает на перепроданность, короткие — когда MACD падает и %R выходит в зону перекупленности.
При появлении сигнала стратегия выставляет отложенный стоп-ордер за максимум или минимум предыдущей свечи. Для сохранения дистанции до рынка используется настраиваемый буфер (аналог исходного «16 пунктов»). После срабатывания ордера позиция сопровождается защитным стопом, опциональным тейк-профитом и трейлинг-стопом, измеряемыми в шагах цены.
Логика торговли
- Данные: по умолчанию дневные свечи (можно выбрать другой таймфрейм).
- Фильтр тренда: сравниваются два последних значения основной линии MACD. Рост даёт приоритет покупкам, падение — продажам.
- Осциллятор: используется предыдущее значение Williams %R. Ниже
WilliamsBuyLevel(по умолчанию -75) разрешается поиск длинных входов, вышеWilliamsSellLevel(по умолчанию -25) — коротких. - Входы:
- Long: buy stop на шаг выше максимума предыдущей свечи. Если получившийся уровень ближе к рынку, чем
EntryBufferStepsшагов цены, ордер переносится дальше на требуемое расстояние. - Short: sell stop на шаг ниже минимума предыдущей свечи. При недостаточной дистанции уровень смещается на
EntryBufferStepsшагов ниже последнего закрытия.
- Long: buy stop на шаг выше максимума предыдущей свечи. Если получившийся уровень ближе к рынку, чем
- Управление риском:
- Стоп-лосс изначально привязывается к противоположному экстремуму предыдущей свечи (минимум для лонга, максимум для шорта).
- Тейк-профит отстоит на
TakeProfitStepsшагов цены. Значение 999 имитирует очень далёкую цель исходного советника. - Трейлинг-стоп активен при
TrailingStopSteps> 0 и подтягивается только в прибыльную сторону.
- Обработка заявок:
- При изменении уровней входа или защитных ордеров существующие стоп-заявки отменяются и выставляются заново.
- Смена направления MACD приводит к отмене неактуальных отложенных ордеров.
- После открытия позиции сохранённые уровни используются для инициализации рабочего стопа и тейка.
- Смещение по минутам: опция
UseSymbolStaggerвключает исходную логику распределения минут для пар EURUSD, GBPUSD, USDCHF и USDJPY, чтобы ордера не выставлялись одновременно.
Параметры
MacdFastLength,MacdSlowLength,MacdSignalLength— периоды MACD для оценки наклона.WilliamsLength— период расчёта Williams %R.WilliamsBuyLevel,WilliamsSellLevel— пороги перепроданности/перекупленности.EntryBufferSteps— минимальное смещение входного стоп-ордера от текущей цены (в шагах).TakeProfitSteps— дистанция до тейк-профита в шагах (уменьшите, чтобы включить фиксированную цель).TrailingStopSteps— расстояние трейлинг-стопа; 0 отключает сопровождение.UseSymbolStagger— включает оконные интервалы по инструментам.CandleType— тип свечей для расчётов.
Примечания
- Объём контролируется стандартным параметром
Volume; при отсутствии значения используется 1 лот. - Отложенные ордера и сопровождение позиций рассчитываются по завершённым свечам, поэтому заполнение между закрытиями аппроксимируется сохранённой ценой входа.
- Значение тейк-профита по умолчанию оставлено большим, как у исходного бота; измените его, если нужен конкретный таргет.
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>
/// Triple screen style daily breakout strategy using MACD slope and Williams %R.
/// Places stop orders beyond the prior day's extremes and manages trailing exits.
/// </summary>
public class TdsGlobalStrategy : Strategy
{
private readonly StrategyParam<int> _macdFastLength;
private readonly StrategyParam<int> _macdSlowLength;
private readonly StrategyParam<int> _macdSignalLength;
private readonly StrategyParam<int> _williamsLength;
private readonly StrategyParam<decimal> _williamsBuyLevel;
private readonly StrategyParam<decimal> _williamsSellLevel;
private readonly StrategyParam<int> _entryBufferSteps;
private readonly StrategyParam<decimal> _takeProfitSteps;
private readonly StrategyParam<decimal> _trailingStopSteps;
private readonly StrategyParam<bool> _useSymbolStagger;
private readonly StrategyParam<DataType> _candleType;
private decimal _priceStep;
private decimal? _macdPrev1;
private decimal? _macdPrev2;
private decimal? _williamsPrev1;
private decimal? _prevHigh;
private decimal? _prevLow;
private decimal? _prevClose;
private bool _hasPendingOrder;
private Sides? _pendingSide;
private decimal? _pendingEntryPrice;
private decimal? _pendingStopPrice;
private decimal? _pendingTakePrice;
private decimal? _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private DateTimeOffset? _lastEntryTime;
/// <summary>
/// Fast EMA period used by MACD.
/// </summary>
public int MacdFastLength
{
get => _macdFastLength.Value;
set => _macdFastLength.Value = value;
}
/// <summary>
/// Slow EMA period used by MACD.
/// </summary>
public int MacdSlowLength
{
get => _macdSlowLength.Value;
set => _macdSlowLength.Value = value;
}
/// <summary>
/// Signal period for MACD.
/// </summary>
public int MacdSignalLength
{
get => _macdSignalLength.Value;
set => _macdSignalLength.Value = value;
}
/// <summary>
/// Williams %R lookback length.
/// </summary>
public int WilliamsLength
{
get => _williamsLength.Value;
set => _williamsLength.Value = value;
}
/// <summary>
/// Oversold threshold for Williams %R.
/// </summary>
public decimal WilliamsBuyLevel
{
get => _williamsBuyLevel.Value;
set => _williamsBuyLevel.Value = value;
}
/// <summary>
/// Overbought threshold for Williams %R.
/// </summary>
public decimal WilliamsSellLevel
{
get => _williamsSellLevel.Value;
set => _williamsSellLevel.Value = value;
}
/// <summary>
/// Minimum distance from market price when placing stop entries (in steps).
/// </summary>
public int EntryBufferSteps
{
get => _entryBufferSteps.Value;
set => _entryBufferSteps.Value = value;
}
/// <summary>
/// Take profit distance expressed in price steps.
/// </summary>
public decimal TakeProfitSteps
{
get => _takeProfitSteps.Value;
set => _takeProfitSteps.Value = value;
}
/// <summary>
/// Trailing stop distance expressed in price steps.
/// </summary>
public decimal TrailingStopSteps
{
get => _trailingStopSteps.Value;
set => _trailingStopSteps.Value = value;
}
/// <summary>
/// Enable symbol specific minute windows for order placement.
/// </summary>
public bool UseSymbolStagger
{
get => _useSymbolStagger.Value;
set => _useSymbolStagger.Value = value;
}
/// <summary>
/// Candle type used for calculations (defaults to daily candles).
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="TdsGlobalStrategy"/> class.
/// </summary>
public TdsGlobalStrategy()
{
_macdFastLength = Param(nameof(MacdFastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA length", "MACD");
_macdSlowLength = Param(nameof(MacdSlowLength), 23)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA length", "MACD");
_macdSignalLength = Param(nameof(MacdSignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal line length", "MACD");
_williamsLength = Param(nameof(WilliamsLength), 24)
.SetGreaterThanZero()
.SetDisplay("Williams Length", "%R lookback", "Filters");
_williamsBuyLevel = Param(nameof(WilliamsBuyLevel), 30m)
.SetDisplay("Williams Buy", "Oversold threshold", "Filters");
_williamsSellLevel = Param(nameof(WilliamsSellLevel), 70m)
.SetDisplay("Williams Sell", "Overbought threshold", "Filters");
_entryBufferSteps = Param(nameof(EntryBufferSteps), 16)
.SetGreaterThanZero()
.SetDisplay("Entry Buffer", "Minimum distance from market in steps", "Risk");
_takeProfitSteps = Param(nameof(TakeProfitSteps), 999m)
.SetDisplay("Take Profit Steps", "Target distance in price steps", "Risk");
_trailingStopSteps = Param(nameof(TrailingStopSteps), 10m)
.SetDisplay("Trailing Stop Steps", "Trailing stop distance in steps", "Risk");
_useSymbolStagger = Param(nameof(UseSymbolStagger), false)
.SetDisplay("Use Time Windows", "Apply symbol specific minute windows", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_priceStep = 0m;
_macdPrev1 = null;
_macdPrev2 = null;
_williamsPrev1 = null;
_prevHigh = null;
_prevLow = null;
_prevClose = null;
ResetPendingOrder();
ResetPositionState();
_lastEntryTime = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_priceStep = Security?.PriceStep ?? 1m;
if (_priceStep <= 0)
_priceStep = 1m;
var macd = new MACD
{
ShortMa = { Length = MacdFastLength },
LongMa = { Length = MacdSlowLength },
};
var williams = new RelativeStrengthIndex { Length = WilliamsLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(macd, williams, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal macdLine, decimal williams)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
{
UpdateHistory(macdLine, williams, candle);
return;
}
ManageOpenPosition(candle);
if (Math.Abs(Position) > 0)
{
UpdateHistory(macdLine, williams, candle);
return;
}
if (_macdPrev1 is null || _macdPrev2 is null || _williamsPrev1 is null ||
_prevHigh is null || _prevLow is null || _prevClose is null)
{
UpdateHistory(macdLine, williams, candle);
return;
}
if (UseSymbolStagger && !IsWithinAllowedWindow(candle.CloseTime))
{
UpdateHistory(macdLine, williams, candle);
return;
}
var direction = _macdPrev1 > _macdPrev2 ? 1 : _macdPrev1 < _macdPrev2 ? -1 : 0;
var oversold = _williamsPrev1 <= WilliamsBuyLevel;
var overbought = _williamsPrev1 >= WilliamsSellLevel;
var volume = Volume;
if (volume <= 0)
volume = 1m;
var cooldownPassed = _lastEntryTime is null || candle.CloseTime - _lastEntryTime >= TimeSpan.FromHours(24);
if (direction > 0 && oversold && cooldownPassed)
{
PlaceBuyStop(volume, candle.CloseTime);
}
else if (direction < 0 && overbought && cooldownPassed)
{
PlaceSellStop(volume, candle.CloseTime);
}
else if (_hasPendingOrder)
{
if ((_pendingSide == Sides.Buy && direction < 0) ||
(_pendingSide == Sides.Sell && direction > 0))
{
ResetPendingOrder();
}
}
UpdateHistory(macdLine, williams, candle);
}
private void ManageOpenPosition(ICandleMessage candle)
{
var position = Position;
if (position > 0)
{
if (_entryPrice is null)
{
_entryPrice = _pendingEntryPrice ?? candle.ClosePrice;
_stopPrice = _pendingStopPrice;
_takePrice = _pendingTakePrice;
ResetPendingOrder();
}
if (TrailingStopSteps > 0)
{
var trailing = candle.ClosePrice - TrailingStopSteps * _priceStep;
if (_stopPrice is null || trailing > _stopPrice)
_stopPrice = trailing;
}
if (_stopPrice is not null && candle.LowPrice <= _stopPrice)
{
if (Position > 0) SellMarket(Math.Abs(Position)); else if (Position < 0) BuyMarket(Math.Abs(Position));
ResetPositionState();
ResetPendingOrder();
return;
}
if (_takePrice is not null && candle.HighPrice >= _takePrice)
{
if (Position > 0) SellMarket(Math.Abs(Position)); else if (Position < 0) BuyMarket(Math.Abs(Position));
ResetPositionState();
ResetPendingOrder();
}
}
else if (position < 0)
{
if (_entryPrice is null)
{
_entryPrice = _pendingEntryPrice ?? candle.ClosePrice;
_stopPrice = _pendingStopPrice;
_takePrice = _pendingTakePrice;
ResetPendingOrder();
}
if (TrailingStopSteps > 0)
{
var trailing = candle.ClosePrice + TrailingStopSteps * _priceStep;
if (_stopPrice is null || trailing < _stopPrice)
_stopPrice = trailing;
}
if (_stopPrice is not null && candle.HighPrice >= _stopPrice)
{
if (Position > 0) SellMarket(Math.Abs(Position)); else if (Position < 0) BuyMarket(Math.Abs(Position));
ResetPositionState();
ResetPendingOrder();
return;
}
if (_takePrice is not null && candle.LowPrice <= _takePrice)
{
if (Position > 0) SellMarket(Math.Abs(Position)); else if (Position < 0) BuyMarket(Math.Abs(Position));
ResetPositionState();
ResetPendingOrder();
}
}
else
{
ResetPositionState();
}
}
private void PlaceBuyStop(decimal volume, DateTimeOffset time)
{
if (_prevHigh is null || _prevLow is null || _prevClose is null)
return;
var entryPrice = Math.Max(_prevHigh.Value + _priceStep, _prevClose.Value + EntryBufferSteps * _priceStep);
var stopPrice = _prevLow.Value - _priceStep;
var takePrice = TakeProfitSteps > 0 ? entryPrice + TakeProfitSteps * _priceStep : (decimal?)null;
if (_hasPendingOrder && _pendingSide == Sides.Buy &&
_pendingEntryPrice == entryPrice &&
_pendingStopPrice == stopPrice &&
_pendingTakePrice == takePrice)
{
return;
}
BuyMarket(volume);
_lastEntryTime = time;
_pendingSide = Sides.Buy;
_pendingEntryPrice = entryPrice;
_pendingStopPrice = stopPrice;
_pendingTakePrice = takePrice;
_hasPendingOrder = true;
}
private void PlaceSellStop(decimal volume, DateTimeOffset time)
{
if (_prevHigh is null || _prevLow is null || _prevClose is null)
return;
var entryPrice = Math.Min(_prevLow.Value - _priceStep, _prevClose.Value - EntryBufferSteps * _priceStep);
var stopPrice = _prevHigh.Value + _priceStep;
var takePrice = TakeProfitSteps > 0 ? entryPrice - TakeProfitSteps * _priceStep : (decimal?)null;
if (_hasPendingOrder && _pendingSide == Sides.Sell &&
_pendingEntryPrice == entryPrice &&
_pendingStopPrice == stopPrice &&
_pendingTakePrice == takePrice)
{
return;
}
SellMarket(volume);
_lastEntryTime = time;
_pendingSide = Sides.Sell;
_pendingEntryPrice = entryPrice;
_pendingStopPrice = stopPrice;
_pendingTakePrice = takePrice;
_hasPendingOrder = true;
}
private void ResetPendingOrder()
{
_hasPendingOrder = false;
_pendingSide = null;
_pendingEntryPrice = null;
_pendingStopPrice = null;
_pendingTakePrice = null;
}
private void ResetPositionState()
{
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
private void UpdateHistory(decimal macdLine, decimal williams, ICandleMessage candle)
{
if (_macdPrev1 is not null)
_macdPrev2 = _macdPrev1;
_macdPrev1 = macdLine;
_williamsPrev1 = williams;
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_prevClose = candle.ClosePrice;
}
private bool IsWithinAllowedWindow(DateTimeOffset time)
{
if (!UseSymbolStagger)
return true;
var minute = time.Minute;
var code = Security?.Code?.ToUpperInvariant();
switch (code)
{
case "USDCHF":
return (minute >= 0 && minute <= 1) || (minute >= 8 && minute <= 9) ||
(minute >= 16 && minute <= 17) || (minute >= 24 && minute <= 25) ||
(minute >= 32 && minute <= 33) || (minute >= 40 && minute <= 41) ||
(minute >= 48 && minute <= 49);
case "GBPUSD":
return (minute >= 2 && minute <= 3) || (minute >= 10 && minute <= 11) ||
(minute >= 18 && minute <= 19) || (minute >= 26 && minute <= 27) ||
(minute >= 34 && minute <= 35) || (minute >= 42 && minute <= 43) ||
(minute >= 50 && minute <= 51);
case "USDJPY":
return (minute >= 4 && minute <= 5) || (minute >= 12 && minute <= 13) ||
(minute >= 20 && minute <= 21) || (minute >= 28 && minute <= 29) ||
(minute >= 36 && minute <= 37) || (minute >= 44 && minute <= 45) ||
(minute >= 52 && minute <= 53);
case "EURUSD":
return (minute >= 6 && minute <= 7) || (minute >= 14 && minute <= 15) ||
(minute >= 22 && minute <= 23) || (minute >= 30 && minute <= 31) ||
(minute >= 38 && minute <= 39) || (minute >= 46 && minute <= 47) ||
(minute >= 54 && minute <= 59);
default:
return true;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergence, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class tds_global_strategy(Strategy):
def __init__(self):
super(tds_global_strategy, self).__init__()
self._macd_fast_length = self.Param("MacdFastLength", 12)
self._macd_slow_length = self.Param("MacdSlowLength", 23)
self._macd_signal_length = self.Param("MacdSignalLength", 9)
self._williams_length = self.Param("WilliamsLength", 24)
self._williams_buy_level = self.Param("WilliamsBuyLevel", 30.0)
self._williams_sell_level = self.Param("WilliamsSellLevel", 70.0)
self._entry_buffer_steps = self.Param("EntryBufferSteps", 16)
self._take_profit_steps = self.Param("TakeProfitSteps", 999.0)
self._trailing_stop_steps = self.Param("TrailingStopSteps", 10.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._price_step = 1.0
self._macd_prev1 = None
self._macd_prev2 = None
self._williams_prev1 = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._entry_price = None
self._stop_price = None
self._take_price = None
@property
def MacdFastLength(self):
return self._macd_fast_length.Value
@MacdFastLength.setter
def MacdFastLength(self, value):
self._macd_fast_length.Value = value
@property
def MacdSlowLength(self):
return self._macd_slow_length.Value
@MacdSlowLength.setter
def MacdSlowLength(self, value):
self._macd_slow_length.Value = value
@property
def MacdSignalLength(self):
return self._macd_signal_length.Value
@MacdSignalLength.setter
def MacdSignalLength(self, value):
self._macd_signal_length.Value = value
@property
def WilliamsLength(self):
return self._williams_length.Value
@WilliamsLength.setter
def WilliamsLength(self, value):
self._williams_length.Value = value
@property
def WilliamsBuyLevel(self):
return self._williams_buy_level.Value
@WilliamsBuyLevel.setter
def WilliamsBuyLevel(self, value):
self._williams_buy_level.Value = value
@property
def WilliamsSellLevel(self):
return self._williams_sell_level.Value
@WilliamsSellLevel.setter
def WilliamsSellLevel(self, value):
self._williams_sell_level.Value = value
@property
def EntryBufferSteps(self):
return self._entry_buffer_steps.Value
@EntryBufferSteps.setter
def EntryBufferSteps(self, value):
self._entry_buffer_steps.Value = value
@property
def TakeProfitSteps(self):
return self._take_profit_steps.Value
@TakeProfitSteps.setter
def TakeProfitSteps(self, value):
self._take_profit_steps.Value = value
@property
def TrailingStopSteps(self):
return self._trailing_stop_steps.Value
@TrailingStopSteps.setter
def TrailingStopSteps(self, value):
self._trailing_stop_steps.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(tds_global_strategy, self).OnStarted2(time)
self._price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self._price_step <= 0.0:
self._price_step = 1.0
self._macd_prev1 = None
self._macd_prev2 = None
self._williams_prev1 = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._entry_price = None
self._stop_price = None
self._take_price = None
macd = MovingAverageConvergenceDivergence()
macd.ShortMa.Length = self.MacdFastLength
macd.LongMa.Length = self.MacdSlowLength
williams = RelativeStrengthIndex()
williams.Length = self.WilliamsLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(macd, williams, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle, macd_line, williams):
if candle.State != CandleStates.Finished:
return
macd_val = float(macd_line)
williams_val = float(williams)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
self._manage_open_position(candle)
if abs(self.Position) > 0:
self._update_history(macd_val, williams_val, candle)
return
if self._macd_prev1 is None or self._macd_prev2 is None or \
self._williams_prev1 is None or self._prev_high is None or \
self._prev_low is None or self._prev_close is None:
self._update_history(macd_val, williams_val, candle)
return
direction = 1 if self._macd_prev1 > self._macd_prev2 else (-1 if self._macd_prev1 < self._macd_prev2 else 0)
oversold = self._williams_prev1 <= float(self.WilliamsBuyLevel)
overbought = self._williams_prev1 >= float(self.WilliamsSellLevel)
if direction > 0 and oversold:
self.BuyMarket()
entry = close
tp_steps = float(self.TakeProfitSteps)
self._entry_price = entry
self._stop_price = self._prev_low - self._price_step
self._take_price = entry + tp_steps * self._price_step if tp_steps > 0.0 else None
elif direction < 0 and overbought:
self.SellMarket()
entry = close
tp_steps = float(self.TakeProfitSteps)
self._entry_price = entry
self._stop_price = self._prev_high + self._price_step
self._take_price = entry - tp_steps * self._price_step if tp_steps > 0.0 else None
self._update_history(macd_val, williams_val, candle)
def _manage_open_position(self, candle):
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self.Position > 0:
if self._entry_price is None:
self._entry_price = close
trail_steps = float(self.TrailingStopSteps)
if trail_steps > 0.0:
trailing = close - trail_steps * self._price_step
if self._stop_price is None or trailing > self._stop_price:
self._stop_price = trailing
if self._stop_price is not None and low <= self._stop_price:
self.SellMarket()
self._reset_position()
return
if self._take_price is not None and high >= self._take_price:
self.SellMarket()
self._reset_position()
elif self.Position < 0:
if self._entry_price is None:
self._entry_price = close
trail_steps = float(self.TrailingStopSteps)
if trail_steps > 0.0:
trailing = close + trail_steps * self._price_step
if self._stop_price is None or trailing < self._stop_price:
self._stop_price = trailing
if self._stop_price is not None and high >= self._stop_price:
self.BuyMarket()
self._reset_position()
return
if self._take_price is not None and low <= self._take_price:
self.BuyMarket()
self._reset_position()
else:
self._reset_position()
def _update_history(self, macd_val, williams_val, candle):
if self._macd_prev1 is not None:
self._macd_prev2 = self._macd_prev1
self._macd_prev1 = macd_val
self._williams_prev1 = williams_val
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._prev_close = float(candle.ClosePrice)
def _reset_position(self):
self._entry_price = None
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(tds_global_strategy, self).OnReseted()
self._price_step = 1.0
self._macd_prev1 = None
self._macd_prev2 = None
self._williams_prev1 = None
self._prev_high = None
self._prev_low = None
self._prev_close = None
self._reset_position()
def CreateClone(self):
return tds_global_strategy()