Это порт советника MetaTrader 4 «Crypto Analysis» на StockSharp. Алгоритм отслеживает пробои после касания ценой внешней полосы Боллинджера на рабочем таймфрейме при сохранении нисходящей структуры (быстрая LWMA ниже медленной). Сделки разрешаются только при согласии импульса старшего таймфрейма и месячного MACD. Управление позицией включает те же уровни защиты, что и в исходном советнике: стопы по пунктам, денежный трейлинг, перевод в безубыток и контроль просадки портфеля.
Логика торговли
Рабочий таймфрейм: задаётся параметром (по умолчанию M15). Все правила входа/выхода оцениваются на этих свечах.
Триггер волатильности: минимум предыдущей свечи должен коснуться или пробить нижнюю полосу Боллинджера (20, 2) для подготовки лонга; максимум — верхнюю полосу для шорта.
Фильтр тренда: быстрая LWMA (по умолчанию 6) должна находиться ниже медленной LWMA (85), что повторяет проверку «медвежьего» уклона в MQL-коде.
Подтверждение RSI: RSI(14) выше 50 для покупок и ниже 50 для продаж.
Импульс: максимальное модульное отклонение трёх последних значений Momentum(14) со старшего таймфрейма от уровня 100 должно превышать порог для покупок/продаж.
Месячный MACD: отдельные свечи (по умолчанию 30-дневные) рассчитывают MACD (12, 26, 9); для лонга требуется MACD выше сигнальной линии, для шорта — ниже.
Исполнение: при совпадении всех фильтров открывается рыночная заявка. Противоположная позиция закрывается, чтобы стратегия всегда оставалась в одном нетто-направлении, как и оригинальный советник.
Управление позицией
Первичные уровни: расстояния стоп-лосса и тейк-профита в пунктах пересчитываются из шага цены с учётом 5- и 3-значных котировок (0.00001 и 0.001 умножаются на 10).
Трейлинг-стоп: после обновления максимума/минимума стоп подтягивается на расстояние TrailingStopPips, причём сохраняется лучший уровень.
Безубыток: при достижении BreakEvenTriggerPips стоп переносится на цену входа плюс/минус BreakEvenOffsetPips.
Денежные цели: абсолютные и процентные цели закрывают позицию, как только плавающая прибыль достигает заданных уровней.
Денежный трейлинг: после превышения MoneyTrailTarget фиксируется максимум прибыли; при откате на MoneyTrailStop позиция закрывается.
Эквити-стоп: отслеживается текущее значение портфеля плюс плавающий результат; если просадка от пика превышает EquityRiskPercent, позиция закрывается.
Многотаймфреймовые данные
Стратегия автоматически подписывается на три источника данных:
Основной ряд свечей для правил Боллинджера/LWMA/RSI.
Старший таймфрейм для фильтра Momentum (по умолчанию H1).
Месячные свечи для подтверждения MACD (по умолчанию 30-дневные бары).
Параметры
Параметр
Описание
OrderVolume
Базовый объём сделки. Перед сменой направления противоположная позиция закрывается.
UseMoneyTakeProfit
Включает денежный тейк-профит.
MoneyTakeProfit
Прибыль в валюте портфеля, при достижении которой позиция закрывается.
UsePercentTakeProfit
Включает процентный тейк-профит от начального капитала.
PercentTakeProfit
Процент прибыли, необходимый для закрытия позиции при активном процентном таргете.
EnableMoneyTrailing
Активирует денежный трейлинг.
MoneyTrailTarget
Порог прибыли для запуска трейлинга.
MoneyTrailStop
Допустимый откат прибыли после активации трейлинга.
StopLossPips
Стоп-лосс в пунктах.
TakeProfitPips
Тейк-профит в пунктах.
TrailingStopPips
Дистанция трейлинг-стопа в пунктах.
UseBreakEven
Включает перевод в безубыток.
BreakEvenTriggerPips
Количество пунктов прибыли перед переносом стопа.
BreakEvenOffsetPips
Дополнительные пункты к цене входа при переносе стопа.
FastMaPeriod
Длина быстрой LWMA по типичной цене.
SlowMaPeriod
Длина медленной LWMA по типичной цене.
MomentumPeriod
Период Momentum на старшем таймфрейме.
MomentumBuyThreshold
Минимальное отклонение Momentum для покупок.
MomentumSellThreshold
Минимальное отклонение Momentum для продаж.
MacdFastLength
Быстрая EMA в MACD фильтре.
MacdSlowLength
Медленная EMA в MACD фильтре.
MacdSignalLength
Сигнальная EMA в MACD фильтре.
UseEquityStop
Включает защиту по просадке портфеля.
EquityRiskPercent
Допустимый процент просадки капитала до принудительного закрытия.
CandleType
Основной таймфрейм.
MomentumCandleType
Таймфрейм для Momentum.
MacdCandleType
Таймфрейм для MACD.
Примечания
Порт StockSharp поддерживает только одну нетто-позицию, аналогично оригинальному советнику, который закрывал противоположные ордера перед открытием новых.
Все правила защиты рассчитываются на закрытии свечи, повторяя обработку «нового бара» из MQL.
Для инструментов с нестандартным шагом цены скорректируйте параметры в пунктах под спецификацию биржи.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class CryptoAnalysisStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public CryptoAnalysisStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class crypto_analysis_strategy(Strategy):
def __init__(self):
super(crypto_analysis_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(crypto_analysis_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(crypto_analysis_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return crypto_analysis_strategy()