ブル対メドベドのウィンドウ戦略
概要
ブル対メドベド戦略は、MetaTrader 4 エキスパート Bull_vs_Medved.mq4 を StockSharp に変換したものです。システムは次のことを試みます あらかじめ定義された5分間に6回の保留中の指値注文を発注することで、強い強気または弱気の衝動の中で引き戻しに入る ウィンドウは取引日全体に広がります。 StockSharp バージョンでは、ウィンドウごとに 1 回だけ取引するという考えが維持され、古いものはキャンセルされます 保留中の注文を処理し、シグナルローソクの実体サイズを使用して動的なストップロスとテイクプロフィットの距離を導き出します。
取引ロジック
CandleTypeによって定義されたキャンドル ストリームをサブスクライブし、完成したキャンドルのみを処理します。- 最後の 2 つの完了したローソク足を維持します。つまり、現在のローソク足 (
shift1)、前のローソク足 (shift2)、そしてローソク足 その前に (shift3) は、MetaTrader で使用されるClose[1..3]参照を複製します。 - 各取引ウィンドウ (
StartTime0..5から始まるEntryWindowMinutes分) 中に、次のパターンを確認してください。- 強気:
shift3はshift2の始値を上回って終了します。shift2の本体は少なくとも 10 ブローカー ポイントであり、本体はshift1は少なくともCandleSizePointsポイントです。IsBadBullが false (ロングボディが 3 つ連続) の場合、購入制限を設定します。 - クールブル:
shift2は最低 20 ポイントのプルバックであり、shift1の始値を下回って終了し、その後、shift1を上回って終了します。shift2は、しきい値の少なくとも 40% の本文で開きます。購入制限を設けます。 - 弱気:
shift1の本体は少なくともCandleSizePointsポイントですが、弱気です。売り指値を設定します。
- 強気:
- 買い指値は
ask - BuyIndentPoints * PriceStep、売り指値はbid + SellIndentPoints * PriceStepに設定されます。一つだけ 未決注文またはポジションが一度に存在する可能性があるため、取引がすでにアクティブである場合、戦略は新しいシグナルをスキップします。 窓。 - 戦略の中にストップとターゲットが隠されています。エントリー注文が約定すると、
shift1のローソク足本体に が乗算されます。StopLossMultiplierとTakeProfitMultiplierは、PriceStepに正規化され、出口価格として保存されます。 - 終了したローソクごとに、戦略は高値/安値が保存されているストップまたはターゲットを突破したかどうかを評価します。レベルに達する 成行注文でオープンポジションをクローズし、保護フラグをクリアします。
- 230 分を超えた保留中の注文は、MetaTrader のクリーンアップ ルーチンを模倣するためにキャンセルされ、
_orderPlacedInWindowは 価格が取引ウィンドウを離れるとリセットされます。
パラメーター
| 名前 | 種類 | デフォルト | 説明 |
|---|---|---|---|
OrderVolume |
decimal |
0.1 |
各指値注文に使用されるボリューム。 |
CandleSizePoints |
decimal |
75 |
シグナルローソク足の最小強気/弱気本体サイズ (ブローカーポイント単位)。 |
StopLossMultiplier |
decimal |
0.8 |
停止距離を構築するためにシグナルキャンドル本体に適用される乗数。 |
TakeProfitMultiplier |
decimal |
0.8 |
ターゲット距離を構築するためにシグナルキャンドル本体に適用される乗数。 |
BuyIndentPoints |
decimal |
16 |
買い指値を設定するときにアスクから差し引かれるブローカー ポイントの数。 |
SellIndentPoints |
decimal |
20 |
売り指値を設定するときに入札に追加されるブローカー ポイントの数。 |
EntryWindowMinutes |
int |
5 |
各セッションの所要時間 (分単位)。 |
CandleType |
DataType |
5分キャンドル | 戦略的に加工されたキャンドルシリーズ。 |
StartTime0..5 |
TimeSpan |
00:05, 04:05, 08:05, 12:05, 16:05, 20:05 |
各取引ウィンドウの開始時間。 |
元の専門家との違い
- MetaTrader エキスパートは、未決注文自体にストップロスとテイクプロフィットを割り当てます。 StockSharp ポートはそれをシミュレートします 隠れレベルを保存し、ローソク足がそれをブレイクしたときに成行注文でネットポジションを閉じることによって動作します。
- 価格しきい値には
Security.PriceStepが使用されるため、追加の追加なしで 4 桁と 5 桁の両方の外国為替相場で変換が機能します。 パラメータ。 - 終了したローソク足のみがストップ/ターゲット ルールの評価に使用されますが、MetaTrader ストップはバー内でトリガーできます。 トレードサーバー。
- 元の EA のサウンド アラートとコメント フィールドは省略されています。代わりに、StockSharp ログがフィードバックを提供します。
使い方のヒント
- この戦略は、端数ピップ価格設定を使用する外国為替シンボル向けに設計されています。
PriceStepを確認してポイントベースであることを確認してください フィルターは意図したピップ距離と一致します。 - ストップとテイクプロフィットは隠されているため、専用環境で戦略を実行するか、戦略を保護することを検討してください。 接続が切断された場合のブローカー側のリスク モジュール。
- ブローカー セッションが元の GMT ベースのスケジュールと異なる場合は、
StartTimeの値を調整します。各ウィンドウは次の方法で無効にできます。 取引日以外の開始時刻を設定する。 - 戦略をチャートに添付して指値注文を視覚化し、各ウィンドウで 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>
/// Bull vs Medved strategy converted from MetaTrader 4.
/// Enters market orders during predefined intraday windows when multi-candle patterns appear.
/// Exits on candle-based stop-loss / take-profit levels.
/// </summary>
public class BullVsMedvedWindowStrategy : Strategy
{
private readonly StrategyParam<decimal> _candleSizePoints;
private readonly StrategyParam<decimal> _stopLossMultiplier;
private readonly StrategyParam<decimal> _takeProfitMultiplier;
private readonly StrategyParam<int> _entryWindowMinutes;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<TimeSpan> _startTime0;
private readonly StrategyParam<TimeSpan> _startTime1;
private readonly StrategyParam<TimeSpan> _startTime2;
private readonly StrategyParam<TimeSpan> _startTime3;
private readonly StrategyParam<TimeSpan> _startTime4;
private readonly StrategyParam<TimeSpan> _startTime5;
private decimal _pointValue;
private decimal _candleSizeThreshold;
private decimal _bodyMinSize;
private decimal _pullbackSize;
private ICandleMessage _previousCandle1;
private ICandleMessage _previousCandle2;
private TimeSpan[] _entryTimes = Array.Empty<TimeSpan>();
private TimeSpan _entryWindow = TimeSpan.Zero;
private bool _orderPlacedInWindow;
private decimal _entryPrice;
private decimal? _longStopPrice;
private decimal? _longTakePrice;
private decimal? _shortStopPrice;
private decimal? _shortTakePrice;
private bool _exitRequested;
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public BullVsMedvedWindowStrategy()
{
_candleSizePoints = Param(nameof(CandleSizePoints), 75m)
.SetDisplay("Body Size (points)", "Minimum body size for the latest candle", "Filters")
.SetGreaterThanZero();
_stopLossMultiplier = Param(nameof(StopLossMultiplier), 0.8m)
.SetDisplay("Stop Multiplier", "Coefficient applied to the candle body for stop-loss", "Risk")
.SetGreaterThanZero();
_takeProfitMultiplier = Param(nameof(TakeProfitMultiplier), 0.8m)
.SetDisplay("Take Profit Multiplier", "Coefficient applied to the candle body for take-profit", "Risk")
.SetGreaterThanZero();
_entryWindowMinutes = Param(nameof(EntryWindowMinutes), 10)
.SetDisplay("Entry Window", "Duration of each trading window in minutes", "Timing")
.SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe for pattern detection", "Data");
_startTime0 = Param(nameof(StartTime0), new TimeSpan(0, 5, 0))
.SetDisplay("Start Time #1", "First trading window start", "Timing");
_startTime1 = Param(nameof(StartTime1), new TimeSpan(4, 5, 0))
.SetDisplay("Start Time #2", "Second trading window start", "Timing");
_startTime2 = Param(nameof(StartTime2), new TimeSpan(8, 5, 0))
.SetDisplay("Start Time #3", "Third trading window start", "Timing");
_startTime3 = Param(nameof(StartTime3), new TimeSpan(12, 5, 0))
.SetDisplay("Start Time #4", "Fourth trading window start", "Timing");
_startTime4 = Param(nameof(StartTime4), new TimeSpan(16, 5, 0))
.SetDisplay("Start Time #5", "Fifth trading window start", "Timing");
_startTime5 = Param(nameof(StartTime5), new TimeSpan(20, 5, 0))
.SetDisplay("Start Time #6", "Sixth trading window start", "Timing");
}
/// <summary>
/// Minimum bullish or bearish body size in broker points.
/// </summary>
public decimal CandleSizePoints
{
get => _candleSizePoints.Value;
set => _candleSizePoints.Value = value;
}
/// <summary>
/// Multiplier applied to the signal candle body to calculate the stop-loss distance.
/// </summary>
public decimal StopLossMultiplier
{
get => _stopLossMultiplier.Value;
set => _stopLossMultiplier.Value = value;
}
/// <summary>
/// Multiplier applied to the signal candle body to calculate the take-profit distance.
/// </summary>
public decimal TakeProfitMultiplier
{
get => _takeProfitMultiplier.Value;
set => _takeProfitMultiplier.Value = value;
}
/// <summary>
/// Duration of each trading window in minutes.
/// </summary>
public int EntryWindowMinutes
{
get => _entryWindowMinutes.Value;
set => _entryWindowMinutes.Value = value;
}
/// <summary>
/// Candle type used to evaluate price patterns.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// First trading window start time.
/// </summary>
public TimeSpan StartTime0
{
get => _startTime0.Value;
set => _startTime0.Value = value;
}
/// <summary>
/// Second trading window start time.
/// </summary>
public TimeSpan StartTime1
{
get => _startTime1.Value;
set => _startTime1.Value = value;
}
/// <summary>
/// Third trading window start time.
/// </summary>
public TimeSpan StartTime2
{
get => _startTime2.Value;
set => _startTime2.Value = value;
}
/// <summary>
/// Fourth trading window start time.
/// </summary>
public TimeSpan StartTime3
{
get => _startTime3.Value;
set => _startTime3.Value = value;
}
/// <summary>
/// Fifth trading window start time.
/// </summary>
public TimeSpan StartTime4
{
get => _startTime4.Value;
set => _startTime4.Value = value;
}
/// <summary>
/// Sixth trading window start time.
/// </summary>
public TimeSpan StartTime5
{
get => _startTime5.Value;
set => _startTime5.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pointValue = 0m;
_candleSizeThreshold = 0m;
_bodyMinSize = 0m;
_pullbackSize = 0m;
_entryWindow = TimeSpan.Zero;
_previousCandle1 = null;
_previousCandle2 = null;
_entryTimes = Array.Empty<TimeSpan>();
_orderPlacedInWindow = false;
_entryPrice = 0m;
_longStopPrice = null;
_longTakePrice = null;
_shortStopPrice = null;
_shortTakePrice = null;
_exitRequested = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pointValue = Security?.PriceStep ?? 1m;
_candleSizeThreshold = CandleSizePoints * _pointValue;
_bodyMinSize = 10m * _pointValue;
_pullbackSize = 20m * _pointValue;
_entryWindow = TimeSpan.FromMinutes(EntryWindowMinutes);
_entryTimes = new[]
{
StartTime0,
StartTime1,
StartTime2,
StartTime3,
StartTime4,
StartTime5,
};
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (HandlePositionExits(candle))
{
ShiftHistory(candle);
return;
}
var inWindow = IsWithinEntryWindow(candle.CloseTime);
if (!inWindow)
{
_orderPlacedInWindow = false;
ShiftHistory(candle);
return;
}
if (_orderPlacedInWindow || Position != 0m)
{
ShiftHistory(candle);
return;
}
if (_previousCandle1 is null || _previousCandle2 is null)
{
ShiftHistory(candle);
return;
}
var shift1 = candle;
var shift2 = _previousCandle1;
var shift3 = _previousCandle2;
var placedOrder = false;
var isBull = IsBull(shift3, shift2, shift1);
var isBadBull = IsBadBull(shift3, shift2, shift1);
var isCoolBull = IsCoolBull(shift2, shift1);
var isBear = IsBear(shift1);
if (isBull && !isBadBull)
placedOrder = TryBuyMarket(shift1);
else if (isCoolBull)
placedOrder = TryBuyMarket(shift1);
else if (isBear)
placedOrder = TrySellMarket(shift1);
if (placedOrder)
_orderPlacedInWindow = true;
ShiftHistory(candle);
}
private bool HandlePositionExits(ICandleMessage candle)
{
if (Position > 0m)
{
if (!_exitRequested && _longStopPrice is decimal stop && candle.LowPrice <= stop)
{
_exitRequested = true;
SellMarket();
ResetProtectionLevels();
return true;
}
if (!_exitRequested && _longTakePrice is decimal take && candle.HighPrice >= take)
{
_exitRequested = true;
SellMarket();
ResetProtectionLevels();
return true;
}
}
else if (Position < 0m)
{
if (!_exitRequested && _shortStopPrice is decimal stop && candle.HighPrice >= stop)
{
_exitRequested = true;
BuyMarket();
ResetProtectionLevels();
return true;
}
if (!_exitRequested && _shortTakePrice is decimal take && candle.LowPrice <= take)
{
_exitRequested = true;
BuyMarket();
ResetProtectionLevels();
return true;
}
}
return false;
}
private bool TryBuyMarket(ICandleMessage referenceCandle)
{
var body = (referenceCandle.ClosePrice - referenceCandle.OpenPrice).Abs();
var stopDistance = RoundToPoint(body * StopLossMultiplier);
var takeDistance = RoundToPoint(body * TakeProfitMultiplier);
var price = referenceCandle.ClosePrice;
BuyMarket();
_entryPrice = price;
_longStopPrice = stopDistance > 0m ? NormalizePrice(price - stopDistance) : null;
_longTakePrice = takeDistance > 0m ? NormalizePrice(price + takeDistance) : null;
_shortStopPrice = null;
_shortTakePrice = null;
_exitRequested = false;
return true;
}
private bool TrySellMarket(ICandleMessage referenceCandle)
{
var body = (referenceCandle.ClosePrice - referenceCandle.OpenPrice).Abs();
var stopDistance = RoundToPoint(body * StopLossMultiplier);
var takeDistance = RoundToPoint(body * TakeProfitMultiplier);
var price = referenceCandle.ClosePrice;
SellMarket();
_entryPrice = price;
_shortStopPrice = stopDistance > 0m ? NormalizePrice(price + stopDistance) : null;
_shortTakePrice = takeDistance > 0m ? NormalizePrice(price - takeDistance) : null;
_longStopPrice = null;
_longTakePrice = null;
_exitRequested = false;
return true;
}
private bool IsWithinEntryWindow(DateTimeOffset time)
{
if (_entryWindow <= TimeSpan.Zero)
return false;
var tod = time.TimeOfDay;
for (var i = 0; i < _entryTimes.Length; i++)
{
var start = _entryTimes[i];
var end = start + _entryWindow;
if (tod >= start && tod <= end)
return true;
}
return false;
}
private void ShiftHistory(ICandleMessage candle)
{
_previousCandle2 = _previousCandle1;
_previousCandle1 = candle;
}
private bool IsBull(ICandleMessage shift3, ICandleMessage shift2, ICandleMessage shift1)
{
return shift3.ClosePrice > shift2.OpenPrice &&
(shift2.ClosePrice - shift2.OpenPrice) >= _bodyMinSize &&
(shift1.ClosePrice - shift1.OpenPrice) >= _candleSizeThreshold;
}
private bool IsBadBull(ICandleMessage shift3, ICandleMessage shift2, ICandleMessage shift1)
{
return (shift3.ClosePrice - shift3.OpenPrice) >= _bodyMinSize &&
(shift2.ClosePrice - shift2.OpenPrice) >= _bodyMinSize &&
(shift1.ClosePrice - shift1.OpenPrice) >= _candleSizeThreshold;
}
private bool IsCoolBull(ICandleMessage shift2, ICandleMessage shift1)
{
return (shift2.OpenPrice - shift2.ClosePrice) >= _pullbackSize &&
shift2.ClosePrice <= shift1.OpenPrice &&
shift1.ClosePrice > shift2.OpenPrice &&
(shift1.ClosePrice - shift1.OpenPrice) >= 0.4m * _candleSizeThreshold;
}
private bool IsBear(ICandleMessage shift1)
{
return (shift1.OpenPrice - shift1.ClosePrice) >= _candleSizeThreshold;
}
private decimal NormalizePrice(decimal price)
{
if (_pointValue <= 0m)
return price;
var steps = price / _pointValue;
var roundedSteps = decimal.Round(steps, MidpointRounding.AwayFromZero);
return roundedSteps * _pointValue;
}
private decimal RoundToPoint(decimal value)
{
if (_pointValue <= 0m)
return value;
var steps = value / _pointValue;
var roundedSteps = decimal.Round(steps, MidpointRounding.AwayFromZero);
return roundedSteps * _pointValue;
}
private void ResetProtectionLevels()
{
_longStopPrice = null;
_longTakePrice = null;
_shortStopPrice = null;
_shortTakePrice = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Array, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class bull_vs_medved_window_strategy(Strategy):
def __init__(self):
super(bull_vs_medved_window_strategy, self).__init__()
self._candle_size_points = self.Param("CandleSizePoints", Decimal(75)) \
.SetDisplay("Body Size (points)", "Minimum body size for the latest candle", "Filters")
self._stop_loss_multiplier = self.Param("StopLossMultiplier", Decimal(0.8)) \
.SetDisplay("Stop Multiplier", "Coefficient applied to the candle body for stop-loss", "Risk")
self._take_profit_multiplier = self.Param("TakeProfitMultiplier", Decimal(0.8)) \
.SetDisplay("Take Profit Multiplier", "Coefficient applied to the candle body for take-profit", "Risk")
self._entry_window_minutes = self.Param("EntryWindowMinutes", 10) \
.SetDisplay("Entry Window", "Duration of each trading window in minutes", "Timing")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Primary timeframe for pattern detection", "Data")
self._start_time0 = self.Param("StartTime0", TimeSpan(0, 5, 0)) \
.SetDisplay("Start Time #1", "First trading window start", "Timing")
self._start_time1 = self.Param("StartTime1", TimeSpan(4, 5, 0)) \
.SetDisplay("Start Time #2", "Second trading window start", "Timing")
self._start_time2 = self.Param("StartTime2", TimeSpan(8, 5, 0)) \
.SetDisplay("Start Time #3", "Third trading window start", "Timing")
self._start_time3 = self.Param("StartTime3", TimeSpan(12, 5, 0)) \
.SetDisplay("Start Time #4", "Fourth trading window start", "Timing")
self._start_time4 = self.Param("StartTime4", TimeSpan(16, 5, 0)) \
.SetDisplay("Start Time #5", "Fifth trading window start", "Timing")
self._start_time5 = self.Param("StartTime5", TimeSpan(20, 5, 0)) \
.SetDisplay("Start Time #6", "Sixth trading window start", "Timing")
self._point_value = Decimal(0)
self._candle_size_threshold = Decimal(0)
self._body_min_size = Decimal(0)
self._pullback_size = Decimal(0)
self._entry_window = TimeSpan.Zero
self._prev_candle1 = None
self._prev_candle2 = None
self._entry_times = []
self._order_placed_in_window = False
self._entry_price = Decimal(0)
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._exit_requested = False
@property
def CandleSizePoints(self):
return self._candle_size_points.Value
@property
def StopLossMultiplier(self):
return self._stop_loss_multiplier.Value
@property
def TakeProfitMultiplier(self):
return self._take_profit_multiplier.Value
@property
def EntryWindowMinutes(self):
return self._entry_window_minutes.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(bull_vs_medved_window_strategy, self).OnStarted2(time)
ps = self.Security.PriceStep if self.Security is not None else None
self._point_value = Decimal(ps) if ps is not None else Decimal(1)
if self._point_value <= Decimal(0):
self._point_value = Decimal(1)
self._candle_size_threshold = Decimal(self.CandleSizePoints) * self._point_value
self._body_min_size = Decimal(10) * self._point_value
self._pullback_size = Decimal(20) * self._point_value
self._entry_window = TimeSpan.FromMinutes(float(self.EntryWindowMinutes))
self._entry_times = [
self._start_time0.Value,
self._start_time1.Value,
self._start_time2.Value,
self._start_time3.Value,
self._start_time4.Value,
self._start_time5.Value,
]
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._handle_position_exits(candle):
self._shift_history(candle)
return
in_window = self._is_within_entry_window(candle.CloseTime)
if not in_window:
self._order_placed_in_window = False
self._shift_history(candle)
return
if self._order_placed_in_window or self.Position != Decimal(0):
self._shift_history(candle)
return
if self._prev_candle1 is None or self._prev_candle2 is None:
self._shift_history(candle)
return
shift1 = candle
shift2 = self._prev_candle1
shift3 = self._prev_candle2
placed_order = False
is_bull = self._check_bull(shift3, shift2, shift1)
is_bad_bull = self._check_bad_bull(shift3, shift2, shift1)
is_cool_bull = self._check_cool_bull(shift2, shift1)
is_bear = self._check_bear(shift1)
if is_bull and not is_bad_bull:
placed_order = self._try_buy_market(shift1)
elif is_cool_bull:
placed_order = self._try_buy_market(shift1)
elif is_bear:
placed_order = self._try_sell_market(shift1)
if placed_order:
self._order_placed_in_window = True
self._shift_history(candle)
def _handle_position_exits(self, candle):
if self.Position > Decimal(0):
if not self._exit_requested and self._long_stop is not None and candle.LowPrice <= self._long_stop:
self._exit_requested = True
self.SellMarket()
self._reset_protection()
return True
if not self._exit_requested and self._long_take is not None and candle.HighPrice >= self._long_take:
self._exit_requested = True
self.SellMarket()
self._reset_protection()
return True
elif self.Position < Decimal(0):
if not self._exit_requested and self._short_stop is not None and candle.HighPrice >= self._short_stop:
self._exit_requested = True
self.BuyMarket()
self._reset_protection()
return True
if not self._exit_requested and self._short_take is not None and candle.LowPrice <= self._short_take:
self._exit_requested = True
self.BuyMarket()
self._reset_protection()
return True
return False
def _try_buy_market(self, reference_candle):
body = Math.Abs(reference_candle.ClosePrice - reference_candle.OpenPrice)
stop_distance = self._round_to_point(body * self.StopLossMultiplier)
take_distance = self._round_to_point(body * self.TakeProfitMultiplier)
price = reference_candle.ClosePrice
self.BuyMarket()
self._entry_price = price
self._long_stop = self._normalize_price(price - stop_distance) if stop_distance > Decimal(0) else None
self._long_take = self._normalize_price(price + take_distance) if take_distance > Decimal(0) else None
self._short_stop = None
self._short_take = None
self._exit_requested = False
return True
def _try_sell_market(self, reference_candle):
body = Math.Abs(reference_candle.ClosePrice - reference_candle.OpenPrice)
stop_distance = self._round_to_point(body * self.StopLossMultiplier)
take_distance = self._round_to_point(body * self.TakeProfitMultiplier)
price = reference_candle.ClosePrice
self.SellMarket()
self._entry_price = price
self._short_stop = self._normalize_price(price + stop_distance) if stop_distance > Decimal(0) else None
self._short_take = self._normalize_price(price - take_distance) if take_distance > Decimal(0) else None
self._long_stop = None
self._long_take = None
self._exit_requested = False
return True
def _is_within_entry_window(self, time):
if self._entry_window <= TimeSpan.Zero:
return False
tod = time.TimeOfDay
for start in self._entry_times:
end = start.Add(self._entry_window)
if tod >= start and tod <= end:
return True
return False
def _check_bull(self, s3, s2, s1):
return (s3.ClosePrice > s2.OpenPrice and
(s2.ClosePrice - s2.OpenPrice) >= self._body_min_size and
(s1.ClosePrice - s1.OpenPrice) >= self._candle_size_threshold)
def _check_bad_bull(self, s3, s2, s1):
return ((s3.ClosePrice - s3.OpenPrice) >= self._body_min_size and
(s2.ClosePrice - s2.OpenPrice) >= self._body_min_size and
(s1.ClosePrice - s1.OpenPrice) >= self._candle_size_threshold)
def _check_cool_bull(self, s2, s1):
return ((s2.OpenPrice - s2.ClosePrice) >= self._pullback_size and
s2.ClosePrice <= s1.OpenPrice and
s1.ClosePrice > s2.OpenPrice and
(s1.ClosePrice - s1.OpenPrice) >= Decimal(0.4) * self._candle_size_threshold)
def _check_bear(self, s1):
return (s1.OpenPrice - s1.ClosePrice) >= self._candle_size_threshold
def _normalize_price(self, price):
if self._point_value <= Decimal(0):
return price
steps = price / self._point_value
rounded_steps = Decimal.Round(steps, 0)
return rounded_steps * self._point_value
def _round_to_point(self, value):
if self._point_value <= Decimal(0):
return value
steps = value / self._point_value
rounded_steps = Decimal.Round(steps, 0)
return rounded_steps * self._point_value
def _shift_history(self, candle):
self._prev_candle2 = self._prev_candle1
self._prev_candle1 = candle
def _reset_protection(self):
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
def OnReseted(self):
super(bull_vs_medved_window_strategy, self).OnReseted()
self._point_value = Decimal(0)
self._candle_size_threshold = Decimal(0)
self._body_min_size = Decimal(0)
self._pullback_size = Decimal(0)
self._entry_window = TimeSpan.Zero
self._prev_candle1 = None
self._prev_candle2 = None
self._entry_times = []
self._order_placed_in_window = False
self._entry_price = Decimal(0)
self._long_stop = None
self._long_take = None
self._short_stop = None
self._short_take = None
self._exit_requested = False
def CreateClone(self):
return bull_vs_medved_window_strategy()