Ver no GitHub

Cryptocurrency Fibonacci MAs (StockSharp)

Overview

This strategy ports the MetaTrader expert advisor "Cryptocurrency Fibonacci MAs" to StockSharp's high-level API. The system tracks a stack of Fibonacci-based exponential moving averages (8/13/21/55), validates momentum on a higher timeframe, and confirms the macro trend with a monthly MACD filter before sending market orders. Only completed candles are processed and all indicator updates are performed via the Bind/BindEx pipeline.

Compared to the MetaTrader version the following intentional adjustments were made:

  • Money-based take profit, equity stop-out, candle-by-candle trailing and break-even automation were omitted. The StockSharp port uses classic pip-based stop-loss and take-profit via StartProtection.
  • Order pyramiding is limited to a net position per direction. Reversals close the opposite exposure first, mirroring StockSharp's netted position model.
  • Multi-timeframe data is provided through additional candle subscriptions rather than ad-hoc indicator requests on demand.

Trading logic

Long entry

  1. EMA alignment: 8 > 13 > 21 > 55 on the main timeframe.
  2. Higher timeframe momentum: the absolute deviation of the 14-period Momentum from the neutral 100 level is above the configured buy threshold for at least one of the last three higher timeframe candles.
  3. Monthly MACD filter: MACD main line is above the signal line.
  4. Position filter: current net position must be flat or short and remain below the configured maximum volume.

Short entry

  1. EMA alignment: 8 < 13 < 21 < 55.
  2. Momentum deviation above the sell threshold for at least one of the last three higher timeframe candles.
  3. MACD main line below its signal line.
  4. Net exposure must be flat or long and within the MaxPositions limit.

Exit logic

  • StartProtection places protective stop-loss and take-profit orders expressed in pip distances. No additional trailing or break-even logic is applied in this port.
  • Reversal signals send the opposite market order size, which first offsets the existing position before establishing the new exposure.

Multi-timeframe mapping

The higher timeframe used for the momentum indicator mirrors the original coefficient table:

Main timeframe Momentum timeframe
1 minute 15 minutes
5 minutes 30 minutes
15 minutes 1 hour
30 minutes 4 hours
1 hour 1 day
4 hours 1 week
1 day 1 month
1 week 1 month
1 month 1 month

The MACD confirmation always runs on a 30-day monthly approximation.

Parameters

Name Description Default
TradeVolume Order size in lots. 0.1
StopLossPips Stop-loss distance in pips. 20
TakeProfitPips Take-profit distance in pips. 50
MomentumBuyThreshold Minimum absolute momentum deviation from 100 required for long trades. 0.3
MomentumSellThreshold Minimum absolute momentum deviation from 100 required for short trades. 0.3
MaxPositions Maximum net volume per direction expressed as multiples of TradeVolume. 1
CandleType Primary timeframe for EMA calculations. 1-hour candles

Usage notes

  1. Attach the strategy to a symbol and select an appropriate timeframe through CandleType.
  2. Ensure that the data source can provide both the main timeframe and the derived higher timeframes (momentum and monthly).
  3. Adjust pip-based risk parameters to match the instrument's tick size. The helper converts pips to instrument steps using Security.PriceStep.
  4. Backtesting and optimization can fine-tune the momentum thresholds and stop distances using the provided parameter ranges.
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 CryptocurrencyFibonacciMAsStrategy : 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 CryptocurrencyFibonacciMAsStrategy()
	{
		_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;
	}
}