Стратегия переносит классический советник CCFp из MetaTrader на высокоуровневый API StockSharp. Для каждой из восьми основных валют (USD, EUR, GBP, CHF, JPY, AUD, CAD, NZD) вычисляется показатель относительной силы на основе отношения быстрого и медленного простого скользящего среднего по семи основным парам с долларом США (EURUSD, GBPUSD, AUDUSD, NZDUSD, USDCAD, USDCHF, USDJPY). Когда разница между силой двух валют превышает заданный порог, стратегия открывает рыночные позиции, выражающие силу одной валюты против слабости другой.
Реализация построена на высокоуровневом подходе: для каждого инструмента создаётся собственная подписка на свечи, индикаторы подключаются через Bind, заявки отправляются через RegisterOrder как рыночные. Комментарии к заявкам сохраняют формат (TOPDOWN), используемый в MQL-версии, что облегчает сопоставление сделок.
Необходимые инструменты
Перед запуском нужно привязать следующие инструменты к параметрам стратегии:
EURUSD
GBPUSD
AUDUSD
NZDUSD
USDCAD
USDCHF
USDJPY
Все пары должны обрабатываться на одном и том же таймфрейме, который задаётся параметром Candle Type.
Параметры
Параметр
Описание
Fast MA
Период быстрого скользящего среднего, участвующего в расчёте силы.
Slow MA
Период медленного скользящего среднего.
Strength Step
Минимальная разница между силами валют, необходимая для появления сигнала.
Close Opposite
При включении сначала закрываются встречные позиции, затем открывается новая.
Candle Type
Тип свечей, который используется индикаторами.
Базовый Volume
Используется значение Strategy.Volume и применяется ко всем заявкам.
Логика торговли
Для каждой из семи USD-пар создаются по два простых скользящих средних (быстрое и медленное).
После прихода завершённой свечи отношение медленного и быстрого средних преобразуется в синтетический показатель силы, аналогичный значениям индикатора CCFp.
Когда обновлены все семь пар, пересчитываются восемь значений силы валют.
Если разница между «сильной» и «слабой» валютой пересекает уровень Strength Step снизу вверх, при этом сила первой растёт, а второй падает, фиксируется возможность для входа.
Стратегия открывает рыночные позиции, отражающие покупку сильной валюты и продажу слабой:
Если сильной оказывается USD, выставляется одна заявка по соответствующей паре (например, продажа EURUSD).
Если слабой становится USD, стратегия покупает пару, где сильная валюта стоит первой (например, покупка EURUSD).
Если обе валюты не содержат USD, отправляются две заявки: покупка сильной валюты против USD и продажа слабой валюты против USD.
При активном флаге Close Opposite, если по инструменту есть встречная позиция, сначала выставляется рыночная заявка на её закрытие.
Управление рисками
Автоматических стоп-лоссов и тейк-профитов не устанавливается; контроль рисков осуществляется за счёт флага Close Opposite и внешних инструментов управления портфелем.
Размер входа задаётся свойством Volume. Его следует настроить в соответствии с размером счёта и желаемой нагрузкой на позицию.
Отличия от оригинального советника MQL
Расчёт силы валют выполняется через индикаторы SimpleMovingAverage на одном таймфрейме. Каскадирование таймфреймов из оригинального индикатора можно приблизить подбором периодов Fast MA и Slow MA.
Автоматическое сопровождение стопов не реализовано: стратегия сосредоточена на воспроизведении логики входа/выхода и передаёт расширенное управление рисками на уровень портфеля StockSharp.
Заявки отправляются через RegisterOrder с использованием объектов Security, вместо прямого взаимодействия с торговыми объектами MetaTrader.
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;
/// <summary>
/// CCFp Currency Strength strategy (simplified). Uses fast/slow EMA crossover
/// to detect momentum shifts, inspired by currency strength comparison.
/// </summary>
public class CcfpCurrencyStrengthStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
public CcfpCurrencyStrengthStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_fastLength = Param(nameof(FastLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast EMA period", "Indicators");
_slowLength = Param(nameof(SlowLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow EMA period", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFast = new ExponentialMovingAverage { Length = FastLength };
var emaSlow = new ExponentialMovingAverage { Length = SlowLength };
decimal prevFast = 0, prevSlow = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaFast, emaSlow, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastVal;
prevSlow = slowVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastVal;
prevSlow = slowVal;
return;
}
// Fast EMA crosses above slow - bullish
if (prevFast <= prevSlow && fastVal > slowVal && Position <= 0)
BuyMarket();
// Fast EMA crosses below slow - bearish
else if (prevFast >= prevSlow && fastVal < slowVal && Position >= 0)
SellMarket();
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaFast);
DrawIndicator(area, emaSlow);
DrawOwnTrades(area);
}
}
}
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 ccfp_currency_strength_strategy(Strategy):
def __init__(self):
super(ccfp_currency_strength_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._fast_length = self.Param("FastLength", 5) \
.SetDisplay("Fast Length", "Fast EMA period", "Indicators")
self._slow_length = self.Param("SlowLength", 20) \
.SetDisplay("Slow Length", "Slow EMA period", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastLength(self):
return self._fast_length.Value
@property
def SlowLength(self):
return self._slow_length.Value
def OnReseted(self):
super(ccfp_currency_strength_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ccfp_currency_strength_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
fast_ema = ExponentialMovingAverage()
fast_ema.Length = self.FastLength
slow_ema = ExponentialMovingAverage()
slow_ema.Length = self.SlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast_ema, slow_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast_ema)
self.DrawIndicator(area, slow_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return ccfp_currency_strength_strategy()