Weight Oscillator Direct 策略
概述
该策略将 MetaTrader 专家顾问 Exp_WeightOscillator_Direct 移植到 StockSharp 的高级 API。它把 RSI、资金流量指数 (MFI)、威廉指标 (Williams %R) 和 DeMarker 四个振荡指标按权重合成为一个综合信号,并通过可选的移动平均进行平滑处理。当 Trend Mode 为 “Direct” 时,策略沿着综合振荡指标的斜率开仓;当设置为 “Against” 时,则执行反向交易。
指标流水线
- RSI:输出范围 0..100。
- MFI:同样归一化到 0..100,用于衡量成交量动能。
- Williams %R:先平移 +100,使其落入 0..100 区间。
- DeMarker:乘以 100,与其它振荡指标保持一致。
- 平滑处理:选择 Simple、Exponential、Smoothed (RMA)、Weighted、Jurik 或 Kaufman 自适应均线之一。
- 综合振荡器:对上述值按权重求平均并进行平滑,得到最终交易信号。
每根已完成的 K 线都会保存一次综合振荡器的数值。通过 Signal Bar 参数可以跳过最近的若干根 K 线,完全复刻原始 EA 的信号延迟逻辑。
交易逻辑
- 等待所有基础指标和平滑均线形成。
- 计算当前完成 K 线的综合振荡器并写入历史序列。
- 取出
Signal Bar、Signal Bar + 1、Signal Bar + 2对应的三个历史值,记为current、previous、prior。 - 判断斜率变化:
- 上升:
previous < prior且current > previous。 - 下降:
previous > prior且current < previous。
- 上升:
- 根据 Trend Mode:
- Direct:上升触发做多,下降触发做空。
- Against:信号方向取反,上升做空,下降做多。
- 处理进出场开关:
- 若启用 Close Shorts/Longs on Signal,先平掉相反方向的持仓。
- 若启用 Allow Long/Short Entries,再以
Volume + |Position|的数量下市价单,从而在一次委托中完成反手。
- 如果 Stop Loss Points 或 Take Profit Points 大于 0,则通过
StartProtection以价格步长 (PriceStep) 为单位启用止损止盈。
参数说明
| 分组 | 名称 | 说明 |
|---|---|---|
| General | Candle Type | 指标计算和信号使用的 K 线周期。 |
| Trading | Trend Mode | Direct 顺势,Against 逆势。 |
| Trading | Signal Bar | 跳过的已完成 K 线数量(默认 1 表示上一根 K 线)。 |
| Oscillator | RSI / MFI / WPR / DeMarker Weight | 各振荡指标的权重,设为 0 可禁用该指标。 |
| Oscillator | RSI / MFI / WPR / DeMarker Period | 各指标的计算周期。 |
| Oscillator | Smoothing Method | 选择平滑均线类型(Simple、Exponential、Smoothed、Weighted、Jurik、Kaufman)。 |
| Oscillator | Smoothing Length | 平滑均线的周期长度。 |
| Risk Management | Stop Loss Points | 止损距离(价格步长),0 表示关闭。 |
| Risk Management | Take Profit Points | 止盈距离(价格步长),0 表示关闭。 |
| Trading | Allow Long/Short Entries | 是否允许开多 / 开空。 |
| Trading | Close Shorts/Longs on Signal | 是否在反向信号出现时强制平仓。 |
所有参数都以 StrategyParam 暴露,可在 StockSharp Designer 中进行优化。
使用提示
- 启动前请设定基础
Volume,策略在反手时会自动加上已有仓位的绝对值以一次成交完成换向。 - 仅订阅
GetWorkingSecurities()返回的那一组蜡烛数据。 - 止损和止盈使用
PriceStep转换为绝对价格,因此要确保品种的步长设置正确。 - “Against” 模式只改变信号方向,其余逻辑与原 EA 保持一致。
- Williams %R 与 DeMarker 在内部做了归一化,与 RSI、MFI 处于同一量纲。
与 MQL 版本的差异
- 原策略还提供
ParMA、JurX、VIDYA、T3等平滑方式。StockSharp 版提供 Jurik 与 Kaufman 等高质量替代方案,并默认使用 Jurik。 - Money Flow Index 总是使用 K 线的成交量。在 MetaTrader 中可以指定 tick 量或真实成交量,而在 StockSharp 中取决于数据源。
- 资金管理通过
StartProtection实现,以价格步长定义距离,更适合 StockSharp 生态,并能得到与原策略相同的效果。
快速开始
- 将策略连接到目标投资组合与证券。
- 设置各振荡器的权重、周期及进出场开关。
- 按市场特性选择合适的平滑方法和周期。
- 如需风险控制,配置止损 / 止盈步长。
- 启动策略,所有交易均在 K 线收盘后执行,确保结果可复现。
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>
/// Trading strategy that combines several oscillators into a weighted composite signal.
/// </summary>
public class WeightOscillatorDirectStrategy : Strategy
{
/// <summary>
/// Defines how the strategy reacts to the oscillator slope.
/// </summary>
public enum WeightOscillatorTrendModes
{
/// <summary>
/// Trade in the direction of the oscillator slope.
/// </summary>
Direct,
/// <summary>
/// Trade against the oscillator slope.
/// </summary>
Against,
}
/// <summary>
/// Available smoothing methods for the blended oscillator.
/// </summary>
public enum WeightOscillatorSmoothingMethods
{
/// <summary>
/// Simple moving average.
/// </summary>
Simple,
/// <summary>
/// Exponential moving average.
/// </summary>
Exponential,
/// <summary>
/// Smoothed (RMA) moving average.
/// </summary>
Smoothed,
/// <summary>
/// Linear weighted moving average.
/// </summary>
Weighted,
/// <summary>
/// Jurik moving average.
/// </summary>
Jurik,
/// <summary>
/// Kaufman adaptive moving average.
/// </summary>
Kaufman,
}
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<WeightOscillatorTrendModes> _trendMode;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<decimal> _rsiWeight;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<decimal> _mfiWeight;
private readonly StrategyParam<int> _mfiPeriod;
private readonly StrategyParam<decimal> _wprWeight;
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _deMarkerWeight;
private readonly StrategyParam<int> _deMarkerPeriod;
private readonly StrategyParam<WeightOscillatorSmoothingMethods> _smoothingMethod;
private readonly StrategyParam<int> _smoothingLength;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private readonly StrategyParam<bool> _buyOpenEnabled;
private readonly StrategyParam<bool> _sellOpenEnabled;
private readonly StrategyParam<bool> _buyCloseEnabled;
private readonly StrategyParam<bool> _sellCloseEnabled;
private RelativeStrengthIndex _rsi = null!;
private MoneyFlowIndex _mfi = null!;
private WilliamsR _wpr = null!;
private DeMarker _deMarker = null!;
private IIndicator _smoothing = null!;
private readonly List<decimal> _oscillatorHistory = new();
/// <summary>
/// Initializes a new instance of the <see cref="WeightOscillatorDirectStrategy"/> class.
/// </summary>
public WeightOscillatorDirectStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for indicator calculations", "General");
_trendMode = Param(nameof(TrendMode), WeightOscillatorTrendModes.Direct)
.SetDisplay("Trend Mode", "Trade with the oscillator slope or against it", "Trading");
_signalBar = Param(nameof(SignalBar), 2)
.SetDisplay("Signal Bar", "Number of closed bars to skip before evaluating signals", "Trading")
.SetRange(1, 5)
;
_rsiWeight = Param(nameof(RsiWeight), 1m)
.SetDisplay("RSI Weight", "Weight of RSI in the composite score", "Oscillator")
.SetRange(0m, 5m)
;
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "Number of bars used for RSI", "Oscillator")
.SetRange(2, 200)
;
_mfiWeight = Param(nameof(MfiWeight), 1m)
.SetDisplay("MFI Weight", "Weight of Money Flow Index", "Oscillator")
.SetRange(0m, 5m)
;
_mfiPeriod = Param(nameof(MfiPeriod), 14)
.SetDisplay("MFI Period", "Number of bars used for MFI", "Oscillator")
.SetRange(2, 200)
;
_wprWeight = Param(nameof(WprWeight), 1m)
.SetDisplay("WPR Weight", "Weight of Williams %R", "Oscillator")
.SetRange(0m, 5m)
;
_wprPeriod = Param(nameof(WprPeriod), 14)
.SetDisplay("WPR Period", "Number of bars used for Williams %R", "Oscillator")
.SetRange(2, 200)
;
_deMarkerWeight = Param(nameof(DeMarkerWeight), 1m)
.SetDisplay("DeMarker Weight", "Weight of DeMarker oscillator", "Oscillator")
.SetRange(0m, 5m)
;
_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 14)
.SetDisplay("DeMarker Period", "Number of bars used for DeMarker", "Oscillator")
.SetRange(2, 200)
;
_smoothingMethod = Param(nameof(SmoothingMethod), WeightOscillatorSmoothingMethods.Jurik)
.SetDisplay("Smoothing Method", "Moving average applied to the blended oscillator", "Oscillator");
_smoothingLength = Param(nameof(SmoothingLength), 10)
.SetDisplay("Smoothing Length", "Length of the smoothing moving average", "Oscillator")
.SetRange(1, 200)
;
_stopLossPoints = Param(nameof(StopLossPoints), 1000)
.SetDisplay("Stop Loss Points", "Protective stop in price steps (0 disables)", "Risk Management")
.SetRange(0, 10000)
;
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
.SetDisplay("Take Profit Points", "Profit target in price steps (0 disables)", "Risk Management")
.SetRange(0, 20000)
;
_buyOpenEnabled = Param(nameof(BuyOpenEnabled), true)
.SetDisplay("Allow Long Entries", "Enable opening long positions", "Trading");
_sellOpenEnabled = Param(nameof(SellOpenEnabled), true)
.SetDisplay("Allow Short Entries", "Enable opening short positions", "Trading");
_buyCloseEnabled = Param(nameof(BuyCloseEnabled), true)
.SetDisplay("Close Shorts on Long Signal", "Allow closing shorts when a long signal appears", "Trading");
_sellCloseEnabled = Param(nameof(SellCloseEnabled), true)
.SetDisplay("Close Longs on Short Signal", "Allow closing longs when a short signal appears", "Trading");
}
/// <summary>
/// Candle type used for the calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Defines whether the strategy trades with or against the oscillator direction.
/// </summary>
public WeightOscillatorTrendModes TrendMode
{
get => _trendMode.Value;
set => _trendMode.Value = value;
}
/// <summary>
/// Number of closed bars to skip when evaluating the composite oscillator.
/// </summary>
public int SignalBar
{
get => _signalBar.Value;
set => _signalBar.Value = value;
}
/// <summary>
/// Weight assigned to RSI.
/// </summary>
public decimal RsiWeight
{
get => _rsiWeight.Value;
set => _rsiWeight.Value = value;
}
/// <summary>
/// RSI lookback period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Weight assigned to MFI.
/// </summary>
public decimal MfiWeight
{
get => _mfiWeight.Value;
set => _mfiWeight.Value = value;
}
/// <summary>
/// MFI lookback period.
/// </summary>
public int MfiPeriod
{
get => _mfiPeriod.Value;
set => _mfiPeriod.Value = value;
}
/// <summary>
/// Weight assigned to Williams %R.
/// </summary>
public decimal WprWeight
{
get => _wprWeight.Value;
set => _wprWeight.Value = value;
}
/// <summary>
/// Williams %R lookback period.
/// </summary>
public int WprPeriod
{
get => _wprPeriod.Value;
set => _wprPeriod.Value = value;
}
/// <summary>
/// Weight assigned to DeMarker oscillator.
/// </summary>
public decimal DeMarkerWeight
{
get => _deMarkerWeight.Value;
set => _deMarkerWeight.Value = value;
}
/// <summary>
/// DeMarker lookback period.
/// </summary>
public int DeMarkerPeriod
{
get => _deMarkerPeriod.Value;
set => _deMarkerPeriod.Value = value;
}
/// <summary>
/// Smoothing method applied to the blended oscillator.
/// </summary>
public WeightOscillatorSmoothingMethods SmoothingMethod
{
get => _smoothingMethod.Value;
set => _smoothingMethod.Value = value;
}
/// <summary>
/// Length of the smoothing moving average.
/// </summary>
public int SmoothingLength
{
get => _smoothingLength.Value;
set => _smoothingLength.Value = value;
}
/// <summary>
/// Stop loss distance expressed in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take profit distance expressed in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Enables opening long positions.
/// </summary>
public bool BuyOpenEnabled
{
get => _buyOpenEnabled.Value;
set => _buyOpenEnabled.Value = value;
}
/// <summary>
/// Enables opening short positions.
/// </summary>
public bool SellOpenEnabled
{
get => _sellOpenEnabled.Value;
set => _sellOpenEnabled.Value = value;
}
/// <summary>
/// Enables closing short positions on a long signal.
/// </summary>
public bool BuyCloseEnabled
{
get => _buyCloseEnabled.Value;
set => _buyCloseEnabled.Value = value;
}
/// <summary>
/// Enables closing long positions on a short signal.
/// </summary>
public bool SellCloseEnabled
{
get => _sellCloseEnabled.Value;
set => _sellCloseEnabled.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_oscillatorHistory.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_mfi = new MoneyFlowIndex { Length = MfiPeriod };
_wpr = new WilliamsR { Length = WprPeriod };
_deMarker = new DeMarker { Length = DeMarkerPeriod };
_smoothing = CreateSmoothingIndicator();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, _mfi, _wpr, _deMarker, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
var step = Security?.PriceStep ?? 1m;
var takeProfit = TakeProfitPoints > 0 ? new Unit(TakeProfitPoints * step, UnitTypes.Absolute) : null;
var stopLoss = StopLossPoints > 0 ? new Unit(StopLossPoints * step, UnitTypes.Absolute) : null;
StartProtection(stopLoss, takeProfit);
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue, decimal mfiValue, decimal wprValue, decimal deMarkerValue)
{
if (candle.State != CandleStates.Finished)
return;
var totalWeight = RsiWeight + MfiWeight + WprWeight + DeMarkerWeight;
if (totalWeight <= 0)
{
this.LogInfo("Total oscillator weight must be positive to generate signals.");
return;
}
// Williams %R is negative in StockSharp, so shift it into the 0..100 range.
var normalizedWpr = wprValue + 100m;
// DeMarker returns 0..1; scale to match other oscillators.
var normalizedDeMarker = deMarkerValue * 100m;
var blended = (RsiWeight * rsiValue + MfiWeight * mfiValue + WprWeight * normalizedWpr + DeMarkerWeight * normalizedDeMarker) / totalWeight;
var smoothedValue = _smoothing.Process(new DecimalIndicatorValue(_smoothing, blended, candle.OpenTime) { IsFinal = true });
if (!smoothedValue.IsFinal)
return;
var oscillator = smoothedValue.ToDecimal();
_oscillatorHistory.Add(oscillator);
if (_oscillatorHistory.Count > 512)
_oscillatorHistory.RemoveAt(0);
var requiredCount = SignalBar + 2;
if (_oscillatorHistory.Count < requiredCount)
return;
var current = GetHistoryValue(SignalBar);
var previous = GetHistoryValue(SignalBar + 1);
var prior = GetHistoryValue(SignalBar + 2);
// Rising when slope turns up over the last two steps.
var rising = previous < prior && current > previous;
// Falling when slope turns down over the last two steps.
var falling = previous > prior && current < previous;
bool longSignal;
bool shortSignal;
if (TrendMode == WeightOscillatorTrendModes.Direct)
{
longSignal = rising;
shortSignal = falling;
}
else
{
longSignal = falling;
shortSignal = rising;
}
if (longSignal)
{
if (BuyCloseEnabled && Position < 0)
{
BuyMarket(Math.Abs(Position));
}
if (BuyOpenEnabled && Position <= 0)
{
BuyMarket(Volume > 0m ? Volume : 1m);
}
}
if (shortSignal)
{
if (SellCloseEnabled && Position > 0)
{
SellMarket(Math.Abs(Position));
}
if (SellOpenEnabled && Position >= 0)
{
SellMarket(Volume > 0m ? Volume : 1m);
}
}
}
private IIndicator CreateSmoothingIndicator()
{
return SmoothingMethod switch
{
WeightOscillatorSmoothingMethods.Simple => new SMA { Length = SmoothingLength },
WeightOscillatorSmoothingMethods.Exponential => new EMA { Length = SmoothingLength },
WeightOscillatorSmoothingMethods.Smoothed => new SmoothedMovingAverage { Length = SmoothingLength },
WeightOscillatorSmoothingMethods.Weighted => new WeightedMovingAverage { Length = SmoothingLength },
WeightOscillatorSmoothingMethods.Kaufman => new KaufmanAdaptiveMovingAverage { Length = SmoothingLength },
_ => new JurikMovingAverage { Length = SmoothingLength },
};
}
private decimal GetHistoryValue(int shift)
{
return _oscillatorHistory[_oscillatorHistory.Count - shift];
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import (RelativeStrengthIndex, MoneyFlowIndex, WilliamsR, DeMarker,
SimpleMovingAverage, ExponentialMovingAverage, SmoothedMovingAverage,
WeightedMovingAverage, JurikMovingAverage, KaufmanAdaptiveMovingAverage)
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
TREND_DIRECT = 0
TREND_AGAINST = 1
SMOOTH_SIMPLE = 0
SMOOTH_EXPONENTIAL = 1
SMOOTH_SMOOTHED = 2
SMOOTH_WEIGHTED = 3
SMOOTH_JURIK = 4
SMOOTH_KAUFMAN = 5
class weight_oscillator_direct_strategy(Strategy):
def __init__(self):
super(weight_oscillator_direct_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._trend_mode = self.Param("TrendMode", TREND_DIRECT)
self._signal_bar = self.Param("SignalBar", 2)
self._rsi_weight = self.Param("RsiWeight", 1.0)
self._rsi_period = self.Param("RsiPeriod", 14)
self._mfi_weight = self.Param("MfiWeight", 1.0)
self._mfi_period = self.Param("MfiPeriod", 14)
self._wpr_weight = self.Param("WprWeight", 1.0)
self._wpr_period = self.Param("WprPeriod", 14)
self._demarker_weight = self.Param("DeMarkerWeight", 1.0)
self._demarker_period = self.Param("DeMarkerPeriod", 14)
self._smoothing_method = self.Param("SmoothingMethod", SMOOTH_JURIK)
self._smoothing_length = self.Param("SmoothingLength", 10)
self._stop_loss_points = self.Param("StopLossPoints", 1000)
self._take_profit_points = self.Param("TakeProfitPoints", 2000)
self._buy_open_enabled = self.Param("BuyOpenEnabled", True)
self._sell_open_enabled = self.Param("SellOpenEnabled", True)
self._buy_close_enabled = self.Param("BuyCloseEnabled", True)
self._sell_close_enabled = self.Param("SellCloseEnabled", True)
self._oscillator_history = []
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def TrendMode(self):
return self._trend_mode.Value
@TrendMode.setter
def TrendMode(self, value):
self._trend_mode.Value = value
@property
def SignalBar(self):
return self._signal_bar.Value
@SignalBar.setter
def SignalBar(self, value):
self._signal_bar.Value = value
@property
def RsiWeight(self):
return self._rsi_weight.Value
@RsiWeight.setter
def RsiWeight(self, value):
self._rsi_weight.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def MfiWeight(self):
return self._mfi_weight.Value
@MfiWeight.setter
def MfiWeight(self, value):
self._mfi_weight.Value = value
@property
def MfiPeriod(self):
return self._mfi_period.Value
@MfiPeriod.setter
def MfiPeriod(self, value):
self._mfi_period.Value = value
@property
def WprWeight(self):
return self._wpr_weight.Value
@WprWeight.setter
def WprWeight(self, value):
self._wpr_weight.Value = value
@property
def WprPeriod(self):
return self._wpr_period.Value
@WprPeriod.setter
def WprPeriod(self, value):
self._wpr_period.Value = value
@property
def DeMarkerWeight(self):
return self._demarker_weight.Value
@DeMarkerWeight.setter
def DeMarkerWeight(self, value):
self._demarker_weight.Value = value
@property
def DeMarkerPeriod(self):
return self._demarker_period.Value
@DeMarkerPeriod.setter
def DeMarkerPeriod(self, value):
self._demarker_period.Value = value
@property
def SmoothingMethod(self):
return self._smoothing_method.Value
@SmoothingMethod.setter
def SmoothingMethod(self, value):
self._smoothing_method.Value = value
@property
def SmoothingLength(self):
return self._smoothing_length.Value
@SmoothingLength.setter
def SmoothingLength(self, value):
self._smoothing_length.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def BuyOpenEnabled(self):
return self._buy_open_enabled.Value
@BuyOpenEnabled.setter
def BuyOpenEnabled(self, value):
self._buy_open_enabled.Value = value
@property
def SellOpenEnabled(self):
return self._sell_open_enabled.Value
@SellOpenEnabled.setter
def SellOpenEnabled(self, value):
self._sell_open_enabled.Value = value
@property
def BuyCloseEnabled(self):
return self._buy_close_enabled.Value
@BuyCloseEnabled.setter
def BuyCloseEnabled(self, value):
self._buy_close_enabled.Value = value
@property
def SellCloseEnabled(self):
return self._sell_close_enabled.Value
@SellCloseEnabled.setter
def SellCloseEnabled(self, value):
self._sell_close_enabled.Value = value
def _create_smoothing_indicator(self):
method = int(self.SmoothingMethod)
length = int(self.SmoothingLength)
if method == SMOOTH_SIMPLE:
ind = SimpleMovingAverage()
ind.Length = length
return ind
elif method == SMOOTH_EXPONENTIAL:
ind = ExponentialMovingAverage()
ind.Length = length
return ind
elif method == SMOOTH_SMOOTHED:
ind = SmoothedMovingAverage()
ind.Length = length
return ind
elif method == SMOOTH_WEIGHTED:
ind = WeightedMovingAverage()
ind.Length = length
return ind
elif method == SMOOTH_KAUFMAN:
ind = KaufmanAdaptiveMovingAverage()
ind.Length = length
return ind
else:
ind = JurikMovingAverage()
ind.Length = length
return ind
def OnStarted2(self, time):
super(weight_oscillator_direct_strategy, self).OnStarted2(time)
self._oscillator_history = []
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
self._mfi = MoneyFlowIndex()
self._mfi.Length = self.MfiPeriod
self._wpr = WilliamsR()
self._wpr.Length = self.WprPeriod
self._demarker = DeMarker()
self._demarker.Length = self.DeMarkerPeriod
self._smoothing = self._create_smoothing_indicator()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._mfi, self._wpr, self._demarker, self.ProcessCandle).Start()
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
if step <= Decimal(0):
step = Decimal(1)
tp = int(self.TakeProfitPoints)
sl = int(self.StopLossPoints)
tp_unit = Unit(Decimal(tp) * step, UnitTypes.Absolute) if tp > 0 else None
sl_unit = Unit(Decimal(sl) * step, UnitTypes.Absolute) if sl > 0 else None
self.StartProtection(sl_unit, tp_unit)
def ProcessCandle(self, candle, rsi_value, mfi_value, wpr_value, demarker_value):
if candle.State != CandleStates.Finished:
return
rsi_w = float(self.RsiWeight)
mfi_w = float(self.MfiWeight)
wpr_w = float(self.WprWeight)
dm_w = float(self.DeMarkerWeight)
total_weight = rsi_w + mfi_w + wpr_w + dm_w
if total_weight <= 0.0:
return
rsi_val = float(rsi_value)
mfi_val = float(mfi_value)
wpr_val = float(wpr_value)
dm_val = float(demarker_value)
normalized_wpr = wpr_val + 100.0
normalized_dm = dm_val * 100.0
blended = (rsi_w * rsi_val + mfi_w * mfi_val + wpr_w * normalized_wpr + dm_w * normalized_dm) / total_weight
smoothed_result = process_float(self._smoothing, Decimal(blended), candle.OpenTime, True)
if not smoothed_result.IsFinal:
return
oscillator = float(smoothed_result)
self._oscillator_history.append(oscillator)
if len(self._oscillator_history) > 512:
self._oscillator_history.pop(0)
signal_bar = int(self.SignalBar)
required_count = signal_bar + 2
if len(self._oscillator_history) < required_count:
return
current = self._oscillator_history[len(self._oscillator_history) - signal_bar]
previous = self._oscillator_history[len(self._oscillator_history) - (signal_bar + 1)]
prior = self._oscillator_history[len(self._oscillator_history) - (signal_bar + 2)]
rising = previous < prior and current > previous
falling = previous > prior and current < previous
trend_mode = int(self.TrendMode)
if trend_mode == TREND_DIRECT:
long_signal = rising
short_signal = falling
else:
long_signal = falling
short_signal = rising
if long_signal:
if self.BuyCloseEnabled and self.Position < 0:
self.BuyMarket()
if self.BuyOpenEnabled and self.Position <= 0:
self.BuyMarket()
if short_signal:
if self.SellCloseEnabled and self.Position > 0:
self.SellMarket()
if self.SellOpenEnabled and self.Position >= 0:
self.SellMarket()
def OnReseted(self):
super(weight_oscillator_direct_strategy, self).OnReseted()
self._oscillator_history = []
def CreateClone(self):
return weight_oscillator_direct_strategy()