Стратегия CH2010 Structure с многотаймфреймовым пробоем
Стратегия переносит логику оригинального ch2010structure.mq5 в StockSharp. Пять валютных пар одновременно отслеживаются на дневном и 30-минутном таймфреймах. Дневная свеча задаёт направление, а внутридневные свечи ищут пробой границ предыдущего дня и открывают позиции по тренду. Управление рисками выполнено через процентные стоп-лоссы и тейк-профиты, а также через ограничение объёмов, аналогичное MQL-функциям DebugOrderSend/DebugOrderCheck.
Алгоритм
Формирование дневной структуры
- Для USDCHF, GBPUSD, AUDUSD, USDJPY и EURGBP подписываемся на дневные свечи.
- После закрытия свечи вычисляется направление (бычье, медвежье или нейтральное) и сохраняются High/Low/Close, а также дата сессии.
- Флаги сбрасываются, чтобы на следующем дне снова разрешить сигналы.
Внутридневные пробои
- Работают только завершённые 30-минутные свечи текущего дня.
- Если закрытие выше дневного High + буфер и дневной тренд не медвежий, отправляется рыночная покупка.
- Если закрытие ниже дневного Low - буфер и дневной тренд не бычий, отправляется рыночная продажа.
- Для каждого инструмента в день допускается максимум один пробой в каждую сторону.
Контроль риска
- Объём заявки ограничивается параметрами
MinTradeVolume,MaxTradeVolumeи общим пределомMaxAggregateVolume. - Сразу после появления позиции рассчитываются цены стоп-лосса и тейк-профита как проценты от цены входа.
- При достижении стопа/цели позиция закрывается рыночной заявкой; флаг
ExitInProgressзащищает от повторных заявок, пока закрытие не завершено.
- Объём заявки ограничивается параметрами
Ведение состояния
- Для каждого инструмента создан класс
InstrumentContext, в котором хранится дневная структура, направление, последняя позиция, параметры выхода и служебные флаги. - Это упрощает многосимвольную работу и повторяет идею отдельных объектов
CExpв MQL.
- Для каждого инструмента создан класс
Параметры
| Параметр | Описание |
|---|---|
TradeVolume |
Базовый объём входа, на который распространяются лимиты. |
MinTradeVolume / MaxTradeVolume |
Минимальный и максимальный объём одной сделки. |
MaxAggregateVolume |
Суммарный лимит по модулю позиций всех инструментов. |
StopLossPercent |
Процентное расстояние от цены входа до стоп-лосса. |
TakeProfitPercent |
Процентное расстояние до тейк-профита. |
BreakoutBufferPercent |
Дополнительный процент от дневного диапазона для формирования уровня пробоя. |
DailyCandleType / IntradayCandleType |
Типы данных свечей для дневного и внутридневного расчёта. |
UsdChfSecurity … EurGbpSecurity |
Инструменты, участвующие в стратегии (по умолчанию основные пары из EA). |
Требования к данным
- Дневные и 30-минутные свечи по каждому выбранному инструменту.
- Подключение к брокеру/бирже, поддерживающее рыночные заявки по всем инструментам.
- Портфель и коннектор StockSharp должны быть настроены до запуска.
Порядок работы
- Укажите значения параметров
Securityдля всех пяти инструментов либо замените их на собственные. - Настройте портфель, коннектор и остальные свойства стратегии.
- При необходимости скорректируйте параметры риска и величину буфера.
- Запустите стратегию. Она автоматически оформит подписки на свечи и начнёт отслеживать пробои.
- Используйте лог (
LogInfo) для контроля состояния — сообщенияDaily candle captured,Enter Buy/Sell,Exitотражают все ключевые шаги.
Отличия от оригинала
- Вместо отложенных ордеров используются рыночные заявки в момент пробоя, что соответствует высокоуровневому API StockSharp.
- Ограничения по объёмам и количеству заявок перенесены в настраиваемые параметры.
- Весь код сопровождается англоязычными комментариями, что облегчает командную работу и аудит.
Отказ от ответственности
Данный пример предназначен для обучения. Перед применением на реальном счёте обязательно протестируйте стратегию и адаптируйте параметры под свои условия торговли.
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>
/// Multi-currency breakout strategy converted from the original CH2010 structure expert.
/// Watches daily candles to define trend bias and 30-minute candles for entries and exits.
/// </summary>
public class Ch2010StructureStrategy : Strategy
{
private readonly StrategyParam<Security> _usdChf;
private readonly StrategyParam<Security> _gbpUsd;
private readonly StrategyParam<Security> _audUsd;
private readonly StrategyParam<Security> _usdJpy;
private readonly StrategyParam<Security> _eurGbp;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly StrategyParam<decimal> _minTradeVolume;
private readonly StrategyParam<decimal> _maxTradeVolume;
private readonly StrategyParam<decimal> _maxAggregateVolume;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _breakoutBufferPercent;
private readonly StrategyParam<DataType> _dailyCandleType;
private readonly StrategyParam<DataType> _intradayCandleType;
private readonly List<InstrumentContext> _contexts = new();
private static readonly (string Alias, Func<Ch2010StructureStrategy, Security> Getter)[] _instrumentSlots =
[
("USDCHF", s => s.UsdChfSecurity),
("GBPUSD", s => s.GbpUsdSecurity),
("AUDUSD", s => s.AudUsdSecurity),
("USDJPY", s => s.UsdJpySecurity),
("EURGBP", s => s.EurGbpSecurity),
];
/// <summary>
/// Initializes a new instance of the <see cref="Ch2010StructureStrategy"/> class.
/// </summary>
public Ch2010StructureStrategy()
{
_usdChf = Param<Security>(nameof(UsdChfSecurity), null);
_usdChf.SetDisplay("USD/CHF", "USDCHF symbol to trade", "Instruments");
_gbpUsd = Param<Security>(nameof(GbpUsdSecurity), null);
_gbpUsd.SetDisplay("GBP/USD", "GBPUSD symbol to trade", "Instruments");
_audUsd = Param<Security>(nameof(AudUsdSecurity), null);
_audUsd.SetDisplay("AUD/USD", "AUDUSD symbol to trade", "Instruments");
_usdJpy = Param<Security>(nameof(UsdJpySecurity), null);
_usdJpy.SetDisplay("USD/JPY", "USDJPY symbol to trade", "Instruments");
_eurGbp = Param<Security>(nameof(EurGbpSecurity), null);
_eurGbp.SetDisplay("EUR/GBP", "EURGBP symbol to trade", "Instruments");
_tradeVolume = Param(nameof(TradeVolume), 1m);
_tradeVolume.SetGreaterThanZero();
_tradeVolume.SetDisplay("Trade Volume", "Nominal volume used for entries", "Risk");
_minTradeVolume = Param(nameof(MinTradeVolume), 0.1m);
_minTradeVolume.SetGreaterThanZero();
_minTradeVolume.SetDisplay("Minimum Volume", "Lower bound that mirrors the MQL expert", "Risk");
_maxTradeVolume = Param(nameof(MaxTradeVolume), 5m);
_maxTradeVolume.SetGreaterThanZero();
_maxTradeVolume.SetDisplay("Maximum Volume", "Upper bound for a single position", "Risk");
_maxAggregateVolume = Param(nameof(MaxAggregateVolume), 15m);
_maxAggregateVolume.SetGreaterThanZero();
_maxAggregateVolume.SetDisplay("Aggregate Volume", "Cap across all instruments", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 1.5m);
_stopLossPercent.SetGreaterThanZero();
_stopLossPercent.SetDisplay("Stop Loss %", "Protective stop percentage", "Risk");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 3m);
_takeProfitPercent.SetGreaterThanZero();
_takeProfitPercent.SetDisplay("Take Profit %", "Profit target percentage", "Risk");
_breakoutBufferPercent = Param(nameof(BreakoutBufferPercent), 10m);
_breakoutBufferPercent.SetGreaterThanZero();
_breakoutBufferPercent.SetDisplay("Buffer %", "Percentage of daily range added above/below breakout", "Logic");
_dailyCandleType = Param(nameof(DailyCandleType), TimeSpan.FromMinutes(5).TimeFrame());
_dailyCandleType.SetDisplay("Daily Candle", "Time frame used for the daily bias", "Data");
_intradayCandleType = Param(nameof(IntradayCandleType), TimeSpan.FromMinutes(30).TimeFrame());
_intradayCandleType.SetDisplay("Intraday Candle", "Time frame used for intraday execution", "Data");
}
/// <summary>
/// USDCHF security parameter.
/// </summary>
public Security UsdChfSecurity
{
get => _usdChf.Value;
set => _usdChf.Value = value;
}
/// <summary>
/// GBPUSD security parameter.
/// </summary>
public Security GbpUsdSecurity
{
get => _gbpUsd.Value;
set => _gbpUsd.Value = value;
}
/// <summary>
/// AUDUSD security parameter.
/// </summary>
public Security AudUsdSecurity
{
get => _audUsd.Value;
set => _audUsd.Value = value;
}
/// <summary>
/// USDJPY security parameter.
/// </summary>
public Security UsdJpySecurity
{
get => _usdJpy.Value;
set => _usdJpy.Value = value;
}
/// <summary>
/// EURGBP security parameter.
/// </summary>
public Security EurGbpSecurity
{
get => _eurGbp.Value;
set => _eurGbp.Value = value;
}
/// <summary>
/// Nominal trade volume.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Minimum allowed volume.
/// </summary>
public decimal MinTradeVolume
{
get => _minTradeVolume.Value;
set => _minTradeVolume.Value = value;
}
/// <summary>
/// Maximum allowed volume for a single position.
/// </summary>
public decimal MaxTradeVolume
{
get => _maxTradeVolume.Value;
set => _maxTradeVolume.Value = value;
}
/// <summary>
/// Maximum combined exposure across all instruments.
/// </summary>
public decimal MaxAggregateVolume
{
get => _maxAggregateVolume.Value;
set => _maxAggregateVolume.Value = value;
}
/// <summary>
/// Stop-loss percentage applied to entries.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Take-profit percentage applied to entries.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Buffer in percent of the daily range used to trigger breakouts.
/// </summary>
public decimal BreakoutBufferPercent
{
get => _breakoutBufferPercent.Value;
set => _breakoutBufferPercent.Value = value;
}
/// <summary>
/// Daily candle type.
/// </summary>
public DataType DailyCandleType
{
get => _dailyCandleType.Value;
set => _dailyCandleType.Value = value;
}
/// <summary>
/// Intraday candle type.
/// </summary>
public DataType IntradayCandleType
{
get => _intradayCandleType.Value;
set => _intradayCandleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
foreach (var slot in _instrumentSlots)
{
var security = slot.Getter(this);
if (security == null)
continue;
yield return (security, DailyCandleType);
yield return (security, IntradayCandleType);
}
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_contexts.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_contexts.Clear();
foreach (var slot in _instrumentSlots)
{
var security = slot.Getter(this);
if (security == null)
continue;
var context = new InstrumentContext(slot.Alias, security);
_contexts.Add(context);
var dailySubscription = SubscribeCandles(DailyCandleType, true, security);
dailySubscription.Bind(candle => ProcessDailyCandle(context, candle));
dailySubscription.Start();
var intradaySubscription = SubscribeCandles(IntradayCandleType, true, security);
intradaySubscription.Bind(candle => ProcessIntradayCandle(context, candle));
intradaySubscription.Start();
}
if (_contexts.Count == 0)
{
throw new InvalidOperationException("At least one security must be configured.");
}
}
private void ProcessDailyCandle(InstrumentContext context, ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
{
return;
}
context.DailyDate = candle.OpenTime.Date;
context.DailyHigh = candle.HighPrice;
context.DailyLow = candle.LowPrice;
context.DailyClose = candle.ClosePrice;
context.HasLevels = true;
context.LongTriggered = false;
context.ShortTriggered = false;
if (candle.ClosePrice > candle.OpenPrice)
{
context.Bias = BiasDirections.Long;
}
else if (candle.ClosePrice < candle.OpenPrice)
{
context.Bias = BiasDirections.Short;
}
else
{
context.Bias = BiasDirections.Neutral;
}
LogInfo($"[{context.Alias}] Daily candle captured. High={candle.HighPrice} Low={candle.LowPrice} Close={candle.ClosePrice}");
}
private void ProcessIntradayCandle(InstrumentContext context, ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
{
return;
}
if (!context.HasLevels)
{
return;
}
if (context.DailyDate != candle.OpenTime.Date)
{
return;
}
var security = context.Security;
if (security == null)
{
return;
}
var position = GetPositionValue(security, Portfolio) ?? 0m;
UpdatePositionSnapshot(context, candle.ClosePrice, position);
if (position != 0)
{
ManageOpenPosition(context, position, candle.ClosePrice);
return;
}
var range = context.DailyHigh - context.DailyLow;
if (range <= 0)
{
return;
}
var buffer = range * (BreakoutBufferPercent / 100m);
var longTrigger = context.DailyHigh + buffer;
var shortTrigger = context.DailyLow - buffer;
if (!context.LongTriggered && context.Bias != BiasDirections.Short)
{
if (candle.ClosePrice > longTrigger)
{
TryEnterPosition(context, Sides.Buy, candle.ClosePrice, "Daily breakout long");
context.LongTriggered = true;
}
}
if (!context.ShortTriggered && context.Bias != BiasDirections.Long)
{
if (candle.ClosePrice < shortTrigger)
{
TryEnterPosition(context, Sides.Sell, candle.ClosePrice, "Daily breakout short");
context.ShortTriggered = true;
}
}
}
private void TryEnterPosition(InstrumentContext context, Sides side, decimal price, string reason)
{
if (context.ExitInProgress)
{
return;
}
var security = context.Security;
if (security == null)
{
return;
}
var volume = AdjustVolumeForLimits(TradeVolume);
if (volume <= 0)
{
return;
}
RegisterOrder(new Order
{
Security = security,
Portfolio = Portfolio,
Side = side,
Volume = volume,
Type = OrderTypes.Market,
Comment = $"{context.Alias}:{reason}"
});
context.EntrySide = side;
context.EntryPrice = price;
context.StopPrice = null;
context.TakeProfitPrice = null;
context.ExitInProgress = false;
LogInfo($"[{context.Alias}] Enter {side} at {price} vol={volume}. Reason={reason}");
}
private void ManageOpenPosition(InstrumentContext context, decimal position, decimal closePrice)
{
if (context.EntrySide == null)
{
return;
}
var isLong = position > 0;
if (context.StopPrice == null || context.TakeProfitPrice == null)
{
var entryPrice = context.EntryPrice ?? closePrice;
var stopOffset = entryPrice * (StopLossPercent / 100m);
var takeOffset = entryPrice * (TakeProfitPercent / 100m);
if (isLong)
{
context.StopPrice = entryPrice - stopOffset;
context.TakeProfitPrice = entryPrice + takeOffset;
}
else
{
context.StopPrice = entryPrice + stopOffset;
context.TakeProfitPrice = entryPrice - takeOffset;
}
}
if (context.ExitInProgress)
{
return;
}
if (isLong)
{
if (context.StopPrice != null && closePrice <= context.StopPrice.Value)
{
ExitPosition(context, position, Sides.Sell, $"StopLoss at {context.StopPrice.Value}");
return;
}
if (context.TakeProfitPrice != null && closePrice >= context.TakeProfitPrice.Value)
{
ExitPosition(context, position, Sides.Sell, $"TakeProfit at {context.TakeProfitPrice.Value}");
}
}
else
{
var volume = Math.Abs(position);
if (context.StopPrice != null && closePrice >= context.StopPrice.Value)
{
ExitPosition(context, volume, Sides.Buy, $"StopLoss at {context.StopPrice.Value}");
return;
}
if (context.TakeProfitPrice != null && closePrice <= context.TakeProfitPrice.Value)
{
ExitPosition(context, volume, Sides.Buy, $"TakeProfit at {context.TakeProfitPrice.Value}");
}
}
}
private void ExitPosition(InstrumentContext context, decimal volume, Sides side, string reason)
{
if (volume <= 0)
{
return;
}
var security = context.Security;
if (security == null)
{
return;
}
context.ExitInProgress = true;
RegisterOrder(new Order
{
Security = security,
Portfolio = Portfolio,
Side = side,
Volume = volume,
Type = OrderTypes.Market,
Comment = $"{context.Alias}:{reason}"
});
LogInfo($"[{context.Alias}] Exit {side} vol={volume}. Reason={reason}");
}
private decimal AdjustVolumeForLimits(decimal desired)
{
if (desired <= 0)
{
return 0m;
}
var volume = Math.Min(desired, MaxTradeVolume);
if (volume < MinTradeVolume)
{
return 0m;
}
var totalExposure = 0m;
foreach (var context in _contexts)
{
var security = context.Security;
if (security == null)
{
continue;
}
var pos = GetPositionValue(security, Portfolio) ?? 0m;
totalExposure += Math.Abs(pos);
}
var remaining = MaxAggregateVolume - totalExposure;
if (remaining <= 0)
{
return 0m;
}
return Math.Min(volume, remaining);
}
private void UpdatePositionSnapshot(InstrumentContext context, decimal price, decimal position)
{
if (position == context.LastKnownPosition)
{
return;
}
if (position == 0)
{
context.ResetPosition();
return;
}
context.LastKnownPosition = position;
context.EntrySide = position > 0 ? Sides.Buy : Sides.Sell;
context.EntryPrice = price;
context.ExitInProgress = false;
var stopOffset = price * (StopLossPercent / 100m);
var takeOffset = price * (TakeProfitPercent / 100m);
if (position > 0)
{
context.StopPrice = price - stopOffset;
context.TakeProfitPrice = price + takeOffset;
}
else
{
context.StopPrice = price + stopOffset;
context.TakeProfitPrice = price - takeOffset;
}
}
private enum BiasDirections
{
Neutral,
Long,
Short
}
private sealed class InstrumentContext
{
public InstrumentContext(string alias, Security security)
{
Alias = alias;
Security = security;
}
public string Alias { get; }
public Security Security { get; }
public DateTime? DailyDate { get; set; }
public decimal DailyHigh { get; set; }
public decimal DailyLow { get; set; }
public decimal DailyClose { get; set; }
public BiasDirections Bias { get; set; }
public bool HasLevels { get; set; }
public bool LongTriggered { get; set; }
public bool ShortTriggered { get; set; }
public decimal LastKnownPosition { get; set; }
public Sides? EntrySide { get; set; }
public decimal? EntryPrice { get; set; }
public decimal? StopPrice { get; set; }
public decimal? TakeProfitPrice { get; set; }
public bool ExitInProgress { get; set; }
public void Reset()
{
DailyDate = null;
DailyHigh = 0m;
DailyLow = 0m;
DailyClose = 0m;
Bias = BiasDirections.Neutral;
HasLevels = false;
LongTriggered = false;
ShortTriggered = false;
ResetPosition();
}
public void ResetPosition()
{
LastKnownPosition = 0m;
EntrySide = null;
EntryPrice = null;
StopPrice = null;
TakeProfitPrice = null;
ExitInProgress = false;
}
}
}
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, InvalidOperationException
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class ch2010_structure_strategy(Strategy):
BIAS_NEUTRAL = 0
BIAS_LONG = 1
BIAS_SHORT = 2
def __init__(self):
super(ch2010_structure_strategy, self).__init__()
self._trade_volume = self.Param("TradeVolume", 1.0)
self._stop_loss_percent = self.Param("StopLossPercent", 1.5)
self._take_profit_percent = self.Param("TakeProfitPercent", 3.0)
self._breakout_buffer_percent = self.Param("BreakoutBufferPercent", 10.0)
self._daily_candle_type = self.Param("DailyCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._intraday_candle_type = self.Param("IntradayCandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._daily_high = 0.0
self._daily_low = 0.0
self._daily_close = 0.0
self._bias = self.BIAS_NEUTRAL
self._has_levels = False
self._long_triggered = False
self._short_triggered = False
self._daily_date = None
self._entry_price = None
self._stop_price = None
self._take_profit_price = None
self._entry_side = None
@property
def TradeVolume(self):
return self._trade_volume.Value
@property
def StopLossPercent(self):
return self._stop_loss_percent.Value
@property
def TakeProfitPercent(self):
return self._take_profit_percent.Value
@property
def BreakoutBufferPercent(self):
return self._breakout_buffer_percent.Value
@property
def DailyCandleType(self):
return self._daily_candle_type.Value
@property
def IntradayCandleType(self):
return self._intraday_candle_type.Value
def OnStarted2(self, time):
super(ch2010_structure_strategy, self).OnStarted2(time)
daily_sub = self.SubscribeCandles(self.DailyCandleType)
daily_sub.Bind(self._process_daily_candle).Start()
intraday_sub = self.SubscribeCandles(self.IntradayCandleType)
intraday_sub.Bind(self._process_intraday_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, intraday_sub)
self.DrawOwnTrades(area)
def _process_daily_candle(self, candle):
if candle.State != CandleStates.Finished:
return
self._daily_date = candle.OpenTime.Date
self._daily_high = float(candle.HighPrice)
self._daily_low = float(candle.LowPrice)
self._daily_close = float(candle.ClosePrice)
self._has_levels = True
self._long_triggered = False
self._short_triggered = False
if float(candle.ClosePrice) > float(candle.OpenPrice):
self._bias = self.BIAS_LONG
elif float(candle.ClosePrice) < float(candle.OpenPrice):
self._bias = self.BIAS_SHORT
else:
self._bias = self.BIAS_NEUTRAL
def _process_intraday_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if not self._has_levels:
return
close = float(candle.ClosePrice)
pos = float(self.Position)
# Manage open position
if pos != 0:
self._manage_open_position(pos, close)
return
rng = self._daily_high - self._daily_low
if rng <= 0:
return
buffer = rng * (float(self.BreakoutBufferPercent) / 100.0)
long_trigger = self._daily_high + buffer
short_trigger = self._daily_low - buffer
if not self._long_triggered and self._bias != self.BIAS_SHORT:
if close > long_trigger:
self.BuyMarket(float(self.TradeVolume))
self._set_entry(True, close)
self._long_triggered = True
if not self._short_triggered and self._bias != self.BIAS_LONG:
if close < short_trigger:
self.SellMarket(float(self.TradeVolume))
self._set_entry(False, close)
self._short_triggered = True
def _manage_open_position(self, pos, close):
if self._entry_price is None:
return
is_long = pos > 0
if self._stop_price is None or self._take_profit_price is None:
entry = self._entry_price
stop_off = entry * (float(self.StopLossPercent) / 100.0)
take_off = entry * (float(self.TakeProfitPercent) / 100.0)
if is_long:
self._stop_price = entry - stop_off
self._take_profit_price = entry + take_off
else:
self._stop_price = entry + stop_off
self._take_profit_price = entry - take_off
if is_long:
if self._stop_price is not None and close <= self._stop_price:
self.SellMarket(pos)
self._reset_position()
return
if self._take_profit_price is not None and close >= self._take_profit_price:
self.SellMarket(pos)
self._reset_position()
else:
vol = abs(pos)
if self._stop_price is not None and close >= self._stop_price:
self.BuyMarket(vol)
self._reset_position()
return
if self._take_profit_price is not None and close <= self._take_profit_price:
self.BuyMarket(vol)
self._reset_position()
def _set_entry(self, is_long, price):
self._entry_price = price
stop_off = price * (float(self.StopLossPercent) / 100.0)
take_off = price * (float(self.TakeProfitPercent) / 100.0)
if is_long:
self._stop_price = price - stop_off
self._take_profit_price = price + take_off
else:
self._stop_price = price + stop_off
self._take_profit_price = price - take_off
def _reset_position(self):
self._entry_price = None
self._stop_price = None
self._take_profit_price = None
self._entry_side = None
def OnReseted(self):
super(ch2010_structure_strategy, self).OnReseted()
self._daily_high = 0.0
self._daily_low = 0.0
self._daily_close = 0.0
self._bias = self.BIAS_NEUTRAL
self._has_levels = False
self._long_triggered = False
self._short_triggered = False
self._daily_date = None
self._reset_position()
def CreateClone(self):
return ch2010_structure_strategy()