SilverTrend V3 Strategy (C#)
Overview
The SilverTrend V3 strategy is a momentum-following system that originates from the MetaTrader 5 expert advisor "SilverTrend v3". The StockSharp port reproduces the original logic while adapting it to the high-level strategy API. The core idea is to detect bullish or bearish momentum using a SilverTrend channel calculation, confirm it with the J_TPO market profile oscillator, and manage the resulting positions with protective stops, trailing logic, and a Friday session filter.
Signal Engine
SilverTrend direction
- Uses a rolling 350-bar window with a 9-bar smoothing parameter to compute dynamic support (
smin) and resistance (smax). - When the current close falls below
smin, the system flags a bearish regime; a close abovesmaxflips the regime to bullish. - The calculation iterates from the oldest bar to the most recent one to replicate the recursive nature of the original MQL code.
- Uses a rolling 350-bar window with a 9-bar smoothing parameter to compute dynamic support (
J_TPO confirmation
- Implements the original 14-period J_TPO oscillator that measures how prices cluster within a short-term distribution.
- Only allows long entries when the oscillator is positive and short entries when it is negative, filtering out weak momentum shifts.
Signal change detection
- A trade is initiated only when the newly computed SilverTrend direction differs from the previous value, ensuring that the strategy reacts to genuine regime shifts instead of noise.
Trade Management
- Market entries – The strategy trades the configured
Volume. If an opposite position is open, it is closed and reversed in one market order. - Initial stop loss – Optional. Defined in price steps relative to the fill price (converted with the instrument's
PriceStep). - Take profit – Optional. Also defined in price steps and evaluated against candle extremes to mimic the original order-modification behaviour.
- Trailing stop – Activates once price moves in favour by the configured trailing distance. For long positions the stop ratchets upward, for shorts it ratchets downward, matching the MetaTrader logic.
- Exit on opposite signal – When the previous regime points in the opposite direction, any existing position is liquidated on the next candle close.
- Friday trading block – New positions are skipped after the specified hour on Fridays to avoid weekend gaps, exactly as in the source EA.
Parameters
| Name | Default | Description |
|---|---|---|
TrailingStopPoints |
50 | Trailing stop distance measured in price steps. Set to zero to disable trailing. |
TakeProfitPoints |
50 | Take profit distance in price steps. Zero disables the target. |
InitialStopLossPoints |
0 | Initial protective stop in price steps. Zero leaves the position without an initial stop. |
FridayCutoffHour |
16 | Exchange hour after which no new entries are permitted on Friday. Use 0 to allow trading all day. |
CandleType |
1-hour candles | Data series that feeds the indicators. Any supported timeframe can be used. |
Volume |
1 lot | Trading size for each position (StockSharp Volume property). |
All distances are multiplied by PriceStep during runtime, which automatically adapts the strategy to the security's tick size (including 3/5 digit forex symbols).
Data and Environment Requirements
- Requires at least 360 completed candles before live signals are produced so that both SilverTrend and J_TPO buffers are fully formed.
- Designed for single-instrument trading via
SubscribeCandles. TheGetWorkingSecuritiesoverride ensures the strategy subscribes only to the configured security and timeframe. - Uses
StartProtection()to enable the standard StockSharp position-protection service once at start-up.
Usage Notes
- The algorithm expects trending instruments such as major forex pairs or liquid futures; adapt the timeframe to the market's volatility.
- Because the SilverTrend calculation is recursive, restarting the strategy with insufficient historical candles will delay signal formation until enough data is collected.
- The high-level API implementation uses candle extremes to simulate order management (stop loss, take profit, trailing). In live trading consider pairing the logic with actual stop/limit orders if your infrastructure requires it.
- The port stores internal state (
_previousSignal,_entryPrice, trailing stops) exactly once per finished candle, matching the "one trade per bar" behaviour of the original EA.
Conversion Details
- Faithfully reproduces the mathematical routines from
SilverTrend v3.mq5, including the nested-array J_TPO algorithm. - Applies C# best practices: parameters are exposed via
StrategyParam<T>, all comments are in English, and indentation uses tabs as mandated by the repository guidelines. - No Python version is provided in this release per the task requirements.
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>
/// SilverTrend v3 momentum strategy ported from MetaTrader 5.
/// </summary>
public class SilverTrendV3Strategy : Strategy
{
private readonly StrategyParam<int> _countBars;
private readonly StrategyParam<int> _ssp;
private readonly StrategyParam<int> _jtpoLength;
private readonly StrategyParam<int> _historyCapacity;
private readonly StrategyParam<int> _risk;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _initialStopLossPoints;
private readonly StrategyParam<int> _fridayCutoffHour;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _closeHistory = new();
private readonly List<decimal> _highHistory = new();
private readonly List<decimal> _lowHistory = new();
private decimal? _longTrailingStop;
private decimal? _shortTrailingStop;
private decimal _entryPrice;
private int _previousSignal;
private decimal _pointValue;
/// <summary>
/// Trailing stop distance expressed in price steps.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Take profit distance expressed in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initial stop loss distance expressed in price steps.
/// </summary>
public decimal InitialStopLossPoints
{
get => _initialStopLossPoints.Value;
set => _initialStopLossPoints.Value = value;
}
/// <summary>
/// Hour after which no new trades are allowed on Friday (exchange time).
/// </summary>
public int FridayCutoffHour
{
get => _fridayCutoffHour.Value;
set => _fridayCutoffHour.Value = value;
}
/// <summary>
/// Candle type used for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of bars used in the indicator history.
/// </summary>
public int CountBars
{
get => _countBars.Value;
set => _countBars.Value = value;
}
/// <summary>
/// Sliding window length for the signal filter.
/// </summary>
public int Ssp
{
get => _ssp.Value;
set => _ssp.Value = value;
}
/// <summary>
/// Length used when smoothing JTPO indicator.
/// </summary>
public int JtpoLength
{
get => _jtpoLength.Value;
set => _jtpoLength.Value = value;
}
/// <summary>
/// Maximum number of candles stored in history.
/// </summary>
public int HistoryCapacity
{
get => _historyCapacity.Value;
set => _historyCapacity.Value = value;
}
/// <summary>
/// Risk coefficient used in signal calculations.
/// </summary>
public int Risk
{
get => _risk.Value;
set => _risk.Value = value;
}
/// <summary>
/// Initialize default parameters.
/// </summary>
public SilverTrendV3Strategy()
{
_countBars = Param(nameof(CountBars), 150)
.SetGreaterThanZero()
.SetDisplay("Count Bars", "Number of candles required before trading", "Indicator");
_ssp = Param(nameof(Ssp), 9)
.SetGreaterThanZero()
.SetDisplay("SSP", "Sliding window length", "Indicator");
_jtpoLength = Param(nameof(JtpoLength), 14)
.SetGreaterThanZero()
.SetDisplay("JTPO Length", "JTPO smoothing length", "Indicator");
_historyCapacity = Param(nameof(HistoryCapacity), 220)
.SetGreaterThanZero()
.SetDisplay("History Capacity", "Maximum stored candles", "Indicator");
_risk = Param(nameof(Risk), 3)
.SetGreaterThanZero()
.SetDisplay("Risk", "Risk coefficient", "Trading");
_trailingStopPoints = Param(nameof(TrailingStopPoints), 50m)
.SetDisplay("Trailing Stop", "Trailing distance in price steps", "Risk")
.SetNotNegative();
_takeProfitPoints = Param(nameof(TakeProfitPoints), 50m)
.SetDisplay("Take Profit", "Take profit distance in price steps", "Risk")
.SetNotNegative();
_initialStopLossPoints = Param(nameof(InitialStopLossPoints), 0m)
.SetDisplay("Initial Stop Loss", "Initial stop loss in price steps", "Risk")
.SetNotNegative();
_fridayCutoffHour = Param(nameof(FridayCutoffHour), 16)
.SetDisplay("Friday Cutoff Hour", "Disable new entries after this hour on Friday", "Sessions")
.SetNotNegative();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle type for signal calculations", "General");
Volume = 1m;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closeHistory.Clear();
_highHistory.Clear();
_lowHistory.Clear();
_longTrailingStop = null;
_shortTrailingStop = null;
_entryPrice = 0m;
_previousSignal = 0;
_pointValue = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pointValue = Security?.PriceStep ?? 1m;
if (_pointValue <= 0m)
{
_pointValue = 1m;
}
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
// protection handled manually via trailing/TP/SL
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
{
return;
}
UpdateHistory(candle);
// indicators are processed manually
if (_closeHistory.Count < CountBars + Ssp + 1)
{
return;
}
var jtpo = CalculateJtpo(JtpoLength);
var signal = CalculateSilverTrendSignal();
var longSignal = _previousSignal != signal && signal > 0 && jtpo > 0m;
var shortSignal = _previousSignal != signal && signal < 0 && jtpo < 0m;
var exitLong = _previousSignal < 0;
var exitShort = _previousSignal > 0;
ManageOpenPosition(candle, exitLong, exitShort);
if (Position <= 0 && longSignal && !IsFridayBlocked(candle))
{
EnterLong(candle);
}
else if (Position >= 0 && shortSignal && !IsFridayBlocked(candle))
{
EnterShort(candle);
}
_previousSignal = signal;
}
private void UpdateHistory(ICandleMessage candle)
{
_closeHistory.Add(candle.ClosePrice);
_highHistory.Add(candle.HighPrice);
_lowHistory.Add(candle.LowPrice);
if (_closeHistory.Count > HistoryCapacity)
{
_closeHistory.RemoveAt(0);
_highHistory.RemoveAt(0);
_lowHistory.RemoveAt(0);
}
}
private void ManageOpenPosition(ICandleMessage candle, bool exitLongSignal, bool exitShortSignal)
{
if (Position > 0)
{
UpdateLongTrailing(candle);
var initialStop = InitialStopLossPoints > 0m ? _entryPrice - GetDistance(InitialStopLossPoints) : (decimal?)null;
var trailingStop = _longTrailingStop;
var stop = CombineLongStops(initialStop, trailingStop);
var takeProfit = TakeProfitPoints > 0m ? _entryPrice + GetDistance(TakeProfitPoints) : (decimal?)null;
if (exitLongSignal ||
(takeProfit.HasValue && candle.HighPrice >= takeProfit.Value) ||
(stop.HasValue && candle.LowPrice <= stop.Value))
{
SellMarket();
ResetStops();
}
}
else if (Position < 0)
{
UpdateShortTrailing(candle);
var initialStop = InitialStopLossPoints > 0m ? _entryPrice + GetDistance(InitialStopLossPoints) : (decimal?)null;
var trailingStop = _shortTrailingStop;
var stop = CombineShortStops(initialStop, trailingStop);
var takeProfit = TakeProfitPoints > 0m ? _entryPrice - GetDistance(TakeProfitPoints) : (decimal?)null;
if (exitShortSignal ||
(takeProfit.HasValue && candle.LowPrice <= takeProfit.Value) ||
(stop.HasValue && candle.HighPrice >= stop.Value))
{
BuyMarket();
ResetStops();
}
}
else
{
ResetStops();
}
}
private void EnterLong(ICandleMessage candle)
{
var volume = Volume;
if (Position < 0)
{
volume += Math.Abs(Position);
}
BuyMarket(volume);
_entryPrice = candle.ClosePrice;
_longTrailingStop = null;
_shortTrailingStop = null;
}
private void EnterShort(ICandleMessage candle)
{
var volume = Volume;
if (Position > 0)
{
volume += Position;
}
SellMarket(volume);
_entryPrice = candle.ClosePrice;
_longTrailingStop = null;
_shortTrailingStop = null;
}
private void UpdateLongTrailing(ICandleMessage candle)
{
if (TrailingStopPoints <= 0m)
{
return;
}
var distance = GetDistance(TrailingStopPoints);
var trigger = _entryPrice + distance;
if (candle.ClosePrice > trigger)
{
var newStop = candle.ClosePrice - distance;
if (!_longTrailingStop.HasValue || newStop > _longTrailingStop.Value)
{
_longTrailingStop = newStop;
}
}
}
private void UpdateShortTrailing(ICandleMessage candle)
{
if (TrailingStopPoints <= 0m)
{
return;
}
var distance = GetDistance(TrailingStopPoints);
var trigger = _entryPrice - distance;
if (candle.ClosePrice < trigger)
{
var newStop = candle.ClosePrice + distance;
if (!_shortTrailingStop.HasValue || newStop < _shortTrailingStop.Value)
{
_shortTrailingStop = newStop;
}
}
}
private decimal? CombineLongStops(decimal? initialStop, decimal? trailingStop)
{
if (initialStop == null && trailingStop == null)
{
return null;
}
if (initialStop == null)
{
return trailingStop;
}
if (trailingStop == null)
{
return initialStop;
}
return Math.Max(initialStop.Value, trailingStop.Value);
}
private decimal? CombineShortStops(decimal? initialStop, decimal? trailingStop)
{
if (initialStop == null && trailingStop == null)
{
return null;
}
if (initialStop == null)
{
return trailingStop;
}
if (trailingStop == null)
{
return initialStop;
}
return Math.Min(initialStop.Value, trailingStop.Value);
}
private void ResetStops()
{
if (Position == 0)
{
_entryPrice = 0m;
}
_longTrailingStop = null;
_shortTrailingStop = null;
}
private bool IsFridayBlocked(ICandleMessage candle)
{
if (FridayCutoffHour <= 0)
{
return false;
}
var time = candle.OpenTime;
return time.DayOfWeek == DayOfWeek.Friday && time.Hour > FridayCutoffHour;
}
private int CalculateSilverTrendSignal()
{
var k = 33 - Risk;
var uptrend = false;
var val = 0;
for (var i = CountBars - Ssp; i >= 0; i--)
{
var ssMax = GetHigh(i);
var ssMin = GetLow(i);
for (var i2 = i; i2 <= i + Ssp - 1; i2++)
{
var priceHigh = GetHigh(i2);
if (ssMax < priceHigh)
{
ssMax = priceHigh;
}
var priceLow = GetLow(i2);
if (ssMin >= priceLow)
{
ssMin = priceLow;
}
}
var smin = ssMin + (ssMax - ssMin) * k / 100m;
var smax = ssMax - (ssMax - ssMin) * k / 100m;
if (GetClose(i) < smin)
{
uptrend = false;
}
if (GetClose(i) > smax)
{
uptrend = true;
}
val = uptrend ? 1 : -1;
}
return val;
}
private decimal CalculateJtpo(int len)
{
if (_closeHistory.Count < 200)
{
return 0m;
}
decimal f8 = 0m;
decimal f10 = 0m;
decimal f18 = 0m;
decimal f20 = 0m;
decimal f30 = 0m;
decimal f40 = 0m;
decimal k = 0m;
decimal var14 = 0m;
decimal var18 = 0m;
decimal var1C = 0m;
decimal var20 = 0m;
decimal var24 = 0m;
decimal value = 0m;
var f38 = 0;
var f48 = 0;
var arr0 = new decimal[400];
var arr1 = new decimal[400];
var arr2 = new decimal[400];
var arr3 = new decimal[400];
for (var i = 200 - len - 100; i >= 0; i--)
{
var14 = 0m;
var1C = 0m;
if (f38 == 0)
{
f38 = 1;
f40 = 0m;
f30 = len - 1 >= 2 ? len - 1 : 2;
f48 = (int)f30 + 1;
f10 = GetClose(i);
arr0[f38] = f10;
k = f48;
f18 = 12m / (k * (k - 1) * (k + 1));
f20 = (f48 + 1) * 0.5m;
}
else
{
if (f38 <= f48)
{
f38 += 1;
}
else
{
f38 = f48 + 1;
}
f8 = f10;
f10 = GetClose(i);
if (f38 > f48)
{
for (var var6 = 2; var6 <= f48; var6++)
{
arr0[var6 - 1] = arr0[var6];
}
arr0[f48] = f10;
}
else
{
arr0[f38] = f10;
}
if (f30 >= f38 && f8 != f10)
{
f40 = 1m;
}
if (f30 == f38 && f40 == 0m)
{
f38 = 0;
}
}
if (f38 >= f48)
{
for (var varA = 1; varA <= f48; varA++)
{
arr2[varA] = varA;
arr3[varA] = varA;
arr1[varA] = arr0[varA];
}
for (var varA = 1; varA <= f48 - 1; varA++)
{
var24 = arr1[varA];
var var12 = varA;
for (var var6 = varA + 1; var6 <= f48; var6++)
{
if (arr1[var6] < var24)
{
var24 = arr1[var6];
var12 = var6;
}
}
var20 = arr1[varA];
arr1[varA] = arr1[var12];
arr1[var12] = var20;
var20 = arr2[varA];
arr2[varA] = arr2[var12];
arr2[var12] = var20;
}
var varIndex = 1;
while (f48 > varIndex)
{
var var6 = varIndex + 1;
var14 = 1m;
var1C = arr3[varIndex];
while (var14 != 0m && var6 < arr3.Length)
{
if (arr1[varIndex] != arr1[var6])
{
if ((var6 - varIndex) > 1)
{
var1C /= (var6 - varIndex);
for (var varE = varIndex; varE <= var6 - 1; varE++)
{
arr3[varE] = var1C;
}
}
var14 = 0m;
}
else
{
var1C += arr3[var6];
var6 += 1;
if (var6 > f48 + 1)
{
break;
}
}
}
varIndex = var6;
}
var1C = 0m;
for (var varA = 1; varA <= f48; varA++)
{
var1C += (arr3[varA] - f20) * (arr2[varA] - f20);
}
var18 = f18 * var1C;
}
else
{
var18 = 0m;
}
value = var18;
if (value == 0m)
{
value = 0.00001m;
}
}
return value;
}
private decimal GetClose(int shift)
{
var index = _closeHistory.Count - 1 - shift;
if (index < 0)
{
index = 0;
}
return _closeHistory[index];
}
private decimal GetHigh(int shift)
{
var index = _highHistory.Count - 1 - shift;
if (index < 0)
{
index = 0;
}
return _highHistory[index];
}
private decimal GetLow(int shift)
{
var index = _lowHistory.Count - 1 - shift;
if (index < 0)
{
index = 0;
}
return _lowHistory[index];
}
private decimal GetDistance(decimal points)
{
return points * _pointValue;
}
}
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.Strategies import Strategy
class silver_trend_v3_strategy(Strategy):
def __init__(self):
super(silver_trend_v3_strategy, self).__init__()
self._count_bars = self.Param("CountBars", 150)
self._ssp = self.Param("Ssp", 9)
self._jtpo_length = self.Param("JtpoLength", 14)
self._history_capacity = self.Param("HistoryCapacity", 220)
self._risk = self.Param("Risk", 3)
self._trailing_points = self.Param("TrailingStopPoints", 50.0)
self._tp_points = self.Param("TakeProfitPoints", 50.0)
self._sl_points = self.Param("InitialStopLossPoints", 0.0)
self._friday_cutoff = self.Param("FridayCutoffHour", 16)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self.Volume = 1
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(silver_trend_v3_strategy, self).OnReseted()
self._close_hist = []
self._high_hist = []
self._low_hist = []
self._long_trailing = None
self._short_trailing = None
self._entry_price = 0.0
self._prev_signal = 0
self._point_value = 0.0
def OnStarted2(self, time):
super(silver_trend_v3_strategy, self).OnStarted2(time)
self._close_hist = []
self._high_hist = []
self._low_hist = []
self._long_trailing = None
self._short_trailing = None
self._entry_price = 0.0
self._prev_signal = 0
pv = 1.0
if self.Security is not None and self.Security.PriceStep is not None and float(self.Security.PriceStep) > 0:
pv = float(self.Security.PriceStep)
self._point_value = pv
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
self._update_history(candle)
cb = int(self._count_bars.Value)
ssp = int(self._ssp.Value)
if len(self._close_hist) < cb + ssp + 1:
return
jtpo = self._calc_jtpo(int(self._jtpo_length.Value))
signal = self._calc_signal()
long_signal = self._prev_signal != signal and signal > 0 and jtpo > 0
short_signal = self._prev_signal != signal and signal < 0 and jtpo < 0
exit_long = self._prev_signal < 0
exit_short = self._prev_signal > 0
self._manage_position(candle, exit_long, exit_short)
if self.Position <= 0 and long_signal and not self._is_friday_blocked(candle):
self._enter_long(candle)
elif self.Position >= 0 and short_signal and not self._is_friday_blocked(candle):
self._enter_short(candle)
self._prev_signal = signal
def _update_history(self, candle):
self._close_hist.append(float(candle.ClosePrice))
self._high_hist.append(float(candle.HighPrice))
self._low_hist.append(float(candle.LowPrice))
cap = int(self._history_capacity.Value)
if len(self._close_hist) > cap:
self._close_hist.pop(0)
self._high_hist.pop(0)
self._low_hist.pop(0)
def _manage_position(self, candle, exit_long_signal, exit_short_signal):
if self.Position > 0:
self._update_long_trailing(candle)
initial_stop = None
if float(self._sl_points.Value) > 0:
initial_stop = self._entry_price - self._get_distance(float(self._sl_points.Value))
trailing_stop = self._long_trailing
stop = self._combine_long_stops(initial_stop, trailing_stop)
take_profit = None
if float(self._tp_points.Value) > 0:
take_profit = self._entry_price + self._get_distance(float(self._tp_points.Value))
if exit_long_signal or \
(take_profit is not None and float(candle.HighPrice) >= take_profit) or \
(stop is not None and float(candle.LowPrice) <= stop):
self.SellMarket()
self._reset_stops()
elif self.Position < 0:
self._update_short_trailing(candle)
initial_stop = None
if float(self._sl_points.Value) > 0:
initial_stop = self._entry_price + self._get_distance(float(self._sl_points.Value))
trailing_stop = self._short_trailing
stop = self._combine_short_stops(initial_stop, trailing_stop)
take_profit = None
if float(self._tp_points.Value) > 0:
take_profit = self._entry_price - self._get_distance(float(self._tp_points.Value))
if exit_short_signal or \
(take_profit is not None and float(candle.LowPrice) <= take_profit) or \
(stop is not None and float(candle.HighPrice) >= stop):
self.BuyMarket()
self._reset_stops()
else:
self._reset_stops()
def _enter_long(self, candle):
volume = float(self.Volume)
if self.Position < 0:
volume += abs(float(self.Position))
self.BuyMarket(volume)
self._entry_price = float(candle.ClosePrice)
self._long_trailing = None
self._short_trailing = None
def _enter_short(self, candle):
volume = float(self.Volume)
if self.Position > 0:
volume += float(self.Position)
self.SellMarket(volume)
self._entry_price = float(candle.ClosePrice)
self._long_trailing = None
self._short_trailing = None
def _update_long_trailing(self, candle):
tp = float(self._trailing_points.Value)
if tp <= 0:
return
dist = self._get_distance(tp)
trigger = self._entry_price + dist
close = float(candle.ClosePrice)
if close > trigger:
new_stop = close - dist
if self._long_trailing is None or new_stop > self._long_trailing:
self._long_trailing = new_stop
def _update_short_trailing(self, candle):
tp = float(self._trailing_points.Value)
if tp <= 0:
return
dist = self._get_distance(tp)
trigger = self._entry_price - dist
close = float(candle.ClosePrice)
if close < trigger:
new_stop = close + dist
if self._short_trailing is None or new_stop < self._short_trailing:
self._short_trailing = new_stop
def _combine_long_stops(self, initial, trailing):
if initial is None and trailing is None:
return None
if initial is None:
return trailing
if trailing is None:
return initial
return max(initial, trailing)
def _combine_short_stops(self, initial, trailing):
if initial is None and trailing is None:
return None
if initial is None:
return trailing
if trailing is None:
return initial
return min(initial, trailing)
def _reset_stops(self):
if self.Position == 0:
self._entry_price = 0.0
self._long_trailing = None
self._short_trailing = None
def _is_friday_blocked(self, candle):
cutoff = int(self._friday_cutoff.Value)
if cutoff <= 0:
return False
t = candle.OpenTime
return t.DayOfWeek == 5 and t.Hour > cutoff
def _calc_signal(self):
k = 33 - int(self._risk.Value)
ssp = int(self._ssp.Value)
cb = int(self._count_bars.Value)
uptrend = False
val = 0
for i in range(cb - ssp, -1, -1):
ss_max = self._get_high(i)
ss_min = self._get_low(i)
for i2 in range(i, i + ssp):
h = self._get_high(i2)
if ss_max < h:
ss_max = h
lo = self._get_low(i2)
if ss_min >= lo:
ss_min = lo
smin_val = ss_min + (ss_max - ss_min) * k / 100.0
smax_val = ss_max - (ss_max - ss_min) * k / 100.0
c = self._get_close(i)
if c < smin_val:
uptrend = False
if c > smax_val:
uptrend = True
val = 1 if uptrend else -1
return val
def _calc_jtpo(self, length):
if len(self._close_hist) < 200:
return 0.0
f8 = 0.0
f10 = 0.0
f18 = 0.0
f20 = 0.0
f30 = 0.0
f40 = 0.0
k = 0.0
var14 = 0.0
var18 = 0.0
var1C = 0.0
var20 = 0.0
var24 = 0.0
value = 0.0
f38 = 0
f48 = 0
arr0 = [0.0] * 400
arr1 = [0.0] * 400
arr2 = [0.0] * 400
arr3 = [0.0] * 400
for i in range(200 - length - 100, -1, -1):
var14 = 0.0
var1C = 0.0
if f38 == 0:
f38 = 1
f40 = 0.0
f30 = float(length - 1) if length - 1 >= 2 else 2.0
f48 = int(f30) + 1
f10 = self._get_close(i)
arr0[f38] = f10
k = float(f48)
f18 = 12.0 / (k * (k - 1.0) * (k + 1.0))
f20 = (f48 + 1) * 0.5
else:
if f38 <= f48:
f38 += 1
else:
f38 = f48 + 1
f8 = f10
f10 = self._get_close(i)
if f38 > f48:
for var6 in range(2, f48 + 1):
arr0[var6 - 1] = arr0[var6]
arr0[f48] = f10
else:
arr0[f38] = f10
if f30 >= f38 and f8 != f10:
f40 = 1.0
if f30 == f38 and f40 == 0.0:
f38 = 0
if f38 >= f48:
for varA in range(1, f48 + 1):
arr2[varA] = float(varA)
arr3[varA] = float(varA)
arr1[varA] = arr0[varA]
for varA in range(1, f48):
var24 = arr1[varA]
var12 = varA
for var6 in range(varA + 1, f48 + 1):
if arr1[var6] < var24:
var24 = arr1[var6]
var12 = var6
var20 = arr1[varA]
arr1[varA] = arr1[var12]
arr1[var12] = var20
var20 = arr2[varA]
arr2[varA] = arr2[var12]
arr2[var12] = var20
varIndex = 1
while f48 > varIndex:
var6 = varIndex + 1
var14 = 1.0
var1C = arr3[varIndex]
while var14 != 0.0 and var6 < len(arr3):
if arr1[varIndex] != arr1[var6]:
if (var6 - varIndex) > 1:
var1C /= float(var6 - varIndex)
for varE in range(varIndex, var6):
arr3[varE] = var1C
var14 = 0.0
else:
var1C += arr3[var6]
var6 += 1
if var6 > f48 + 1:
break
varIndex = var6
var1C = 0.0
for varA in range(1, f48 + 1):
var1C += (arr3[varA] - f20) * (arr2[varA] - f20)
var18 = f18 * var1C
else:
var18 = 0.0
value = var18
if value == 0.0:
value = 0.00001
return value
def _get_close(self, shift):
idx = len(self._close_hist) - 1 - shift
if idx < 0:
idx = 0
return self._close_hist[idx]
def _get_high(self, shift):
idx = len(self._high_hist) - 1 - shift
if idx < 0:
idx = 0
return self._high_hist[idx]
def _get_low(self, shift):
idx = len(self._low_hist) - 1 - shift
if idx < 0:
idx = 0
return self._low_hist[idx]
def _get_distance(self, points):
return points * self._point_value
def CreateClone(self):
return silver_trend_v3_strategy()