Neural Network MACD Strategy
This strategy combines a simple four-weight perceptron filter with a classic MACD crossover. A position is opened only when both the MACD and the neural network agree on the direction.
How it works
- Perceptron filter
Three perceptrons evaluate price momentum using differences between the current close and a set of past open prices. Each perceptron has four integer weights (X11…X34) where0means no influence. The perceptron output is a weighted sum of the price differences.
Depending on thePassparameter, one, two or all three perceptrons participate in decision making. The filter also defines stop-loss and take-profit distances (Sl1,Tp1,Sl2,Tp2). - MACD confirmation
A standard MACD (12, 26, 9) is calculated. A buy signal appears when the MACD line is below zero and crosses above the signal line. A sell signal is when the line is above zero and crosses below the signal line. - Trade execution
- Long position is opened if both the MACD and the perceptron filter are positive.
- Short position is opened if both are negative.
The position is closed when either a stop-loss or take-profit level is reached.
Parameters
| Name | Description |
|---|---|
X11…X34 |
Weights for perceptron inputs. |
Tp1, Sl1 |
Take-profit and stop-loss for the first perceptron. |
Tp2, Sl2 |
Take-profit and stop-loss for the second perceptron. |
P1, P2, P3 |
Shifts in bars used to calculate perceptron inputs. |
Pass |
Number of perceptrons to use (1-3). |
CandleType |
Candle series for calculations. |
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>
/// MACD combined with simple neural network.
/// Perceptron-based filter validates MACD signals.
/// </summary>
public class NeuralNetworkMacdStrategy : Strategy
{
private readonly StrategyParam<int> _x11;
private readonly StrategyParam<int> _x12;
private readonly StrategyParam<int> _x13;
private readonly StrategyParam<int> _x14;
private readonly StrategyParam<decimal> _tp1;
private readonly StrategyParam<decimal> _sl1;
private readonly StrategyParam<int> _p1;
private readonly StrategyParam<int> _x21;
private readonly StrategyParam<int> _x22;
private readonly StrategyParam<int> _x23;
private readonly StrategyParam<int> _x24;
private readonly StrategyParam<decimal> _tp2;
private readonly StrategyParam<decimal> _sl2;
private readonly StrategyParam<int> _p2;
private readonly StrategyParam<int> _x31;
private readonly StrategyParam<int> _x32;
private readonly StrategyParam<int> _x33;
private readonly StrategyParam<int> _x34;
private readonly StrategyParam<int> _p3;
private readonly StrategyParam<int> _pass;
private readonly StrategyParam<DataType> _candleType;
private bool _macdInitialized;
private decimal _prevMacd;
private decimal _prevSignal;
private decimal[] _openHistory = Array.Empty<decimal>();
private int _historyIndex;
private bool _historyFilled;
private decimal _currentClose;
private decimal _currentStopLoss;
private decimal _currentTakeProfit;
private decimal _entryPrice;
private bool _isLong;
/// <summary>
/// Weight 1 for perceptron 1.
/// </summary>
public int X11 { get => _x11.Value; set => _x11.Value = value; }
/// <summary>
/// Weight 2 for perceptron 1.
/// </summary>
public int X12 { get => _x12.Value; set => _x12.Value = value; }
/// <summary>
/// Weight 3 for perceptron 1.
/// </summary>
public int X13 { get => _x13.Value; set => _x13.Value = value; }
/// <summary>
/// Weight 4 for perceptron 1.
/// </summary>
public int X14 { get => _x14.Value; set => _x14.Value = value; }
/// <summary>
/// Take profit for perceptron 1.
/// </summary>
public decimal Tp1 { get => _tp1.Value; set => _tp1.Value = value; }
/// <summary>
/// Stop loss for perceptron 1.
/// </summary>
public decimal Sl1 { get => _sl1.Value; set => _sl1.Value = value; }
/// <summary>
/// Shift parameter for perceptron 1.
/// </summary>
public int P1 { get => _p1.Value; set => _p1.Value = value; }
/// <summary>
/// Weight 1 for perceptron 2.
/// </summary>
public int X21 { get => _x21.Value; set => _x21.Value = value; }
/// <summary>
/// Weight 2 for perceptron 2.
/// </summary>
public int X22 { get => _x22.Value; set => _x22.Value = value; }
/// <summary>
/// Weight 3 for perceptron 2.
/// </summary>
public int X23 { get => _x23.Value; set => _x23.Value = value; }
/// <summary>
/// Weight 4 for perceptron 2.
/// </summary>
public int X24 { get => _x24.Value; set => _x24.Value = value; }
/// <summary>
/// Take profit for perceptron 2.
/// </summary>
public decimal Tp2 { get => _tp2.Value; set => _tp2.Value = value; }
/// <summary>
/// Stop loss for perceptron 2.
/// </summary>
public decimal Sl2 { get => _sl2.Value; set => _sl2.Value = value; }
/// <summary>
/// Shift parameter for perceptron 2.
/// </summary>
public int P2 { get => _p2.Value; set => _p2.Value = value; }
/// <summary>
/// Weight 1 for perceptron 3.
/// </summary>
public int X31 { get => _x31.Value; set => _x31.Value = value; }
/// <summary>
/// Weight 2 for perceptron 3.
/// </summary>
public int X32 { get => _x32.Value; set => _x32.Value = value; }
/// <summary>
/// Weight 3 for perceptron 3.
/// </summary>
public int X33 { get => _x33.Value; set => _x33.Value = value; }
/// <summary>
/// Weight 4 for perceptron 3.
/// </summary>
public int X34 { get => _x34.Value; set => _x34.Value = value; }
/// <summary>
/// Shift parameter for perceptron 3.
/// </summary>
public int P3 { get => _p3.Value; set => _p3.Value = value; }
/// <summary>
/// Number of perceptrons to use.
/// </summary>
public int Pass { get => _pass.Value; set => _pass.Value = value; }
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize <see cref="NeuralNetworkMacdStrategy"/>.
/// </summary>
public NeuralNetworkMacdStrategy()
{
_x11 = Param(nameof(X11), 120).SetDisplay("X11", "Weight 1 for perceptron 1", "Perceptron1");
_x12 = Param(nameof(X12), 80).SetDisplay("X12", "Weight 2 for perceptron 1", "Perceptron1");
_x13 = Param(nameof(X13), 110).SetDisplay("X13", "Weight 3 for perceptron 1", "Perceptron1");
_x14 = Param(nameof(X14), 90).SetDisplay("X14", "Weight 4 for perceptron 1", "Perceptron1");
_tp1 = Param(nameof(Tp1), 100m).SetDisplay("Take Profit 1", "Take profit for perceptron 1", "Perceptron1");
_sl1 = Param(nameof(Sl1), 50m).SetDisplay("Stop Loss 1", "Stop loss for perceptron 1", "Perceptron1");
_p1 = Param(nameof(P1), 10).SetDisplay("P1", "Shift parameter for perceptron 1", "Perceptron1");
_x21 = Param(nameof(X21), 130).SetDisplay("X21", "Weight 1 for perceptron 2", "Perceptron2");
_x22 = Param(nameof(X22), 70).SetDisplay("X22", "Weight 2 for perceptron 2", "Perceptron2");
_x23 = Param(nameof(X23), 115).SetDisplay("X23", "Weight 3 for perceptron 2", "Perceptron2");
_x24 = Param(nameof(X24), 85).SetDisplay("X24", "Weight 4 for perceptron 2", "Perceptron2");
_tp2 = Param(nameof(Tp2), 100m).SetDisplay("Take Profit 2", "Take profit for perceptron 2", "Perceptron2");
_sl2 = Param(nameof(Sl2), 50m).SetDisplay("Stop Loss 2", "Stop loss for perceptron 2", "Perceptron2");
_p2 = Param(nameof(P2), 10).SetDisplay("P2", "Shift parameter for perceptron 2", "Perceptron2");
_x31 = Param(nameof(X31), 125).SetDisplay("X31", "Weight 1 for perceptron 3", "Perceptron3");
_x32 = Param(nameof(X32), 75).SetDisplay("X32", "Weight 2 for perceptron 3", "Perceptron3");
_x33 = Param(nameof(X33), 105).SetDisplay("X33", "Weight 3 for perceptron 3", "Perceptron3");
_x34 = Param(nameof(X34), 95).SetDisplay("X34", "Weight 4 for perceptron 3", "Perceptron3");
_p3 = Param(nameof(P3), 10).SetDisplay("P3", "Shift parameter for perceptron 3", "Perceptron3");
_pass = Param(nameof(Pass), 3).SetDisplay("Pass", "Number of perceptrons to use", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macdInitialized = false;
_prevMacd = 0m;
_prevSignal = 0m;
_openHistory = Array.Empty<decimal>();
_historyIndex = 0;
_historyFilled = false;
_currentClose = 0m;
_currentStopLoss = 0m;
_currentTakeProfit = 0m;
_entryPrice = 0m;
_isLong = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var macd = new MovingAverageConvergenceDivergenceSignal(
new MovingAverageConvergenceDivergence
{
ShortMa = { Length = 12 },
LongMa = { Length = 26 },
},
new ExponentialMovingAverage { Length = 9 }
);
var maxLag = Math.Max(Math.Max(P1, P2), P3) * 4 + 1;
_openHistory = new decimal[maxLag];
_historyIndex = 0;
_historyFilled = false;
_currentClose = 0m;
_currentStopLoss = 0m;
_currentTakeProfit = 0m;
var subscription = SubscribeCandles(CandleType);
subscription.BindEx(macd, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, macd);
DrawOwnTrades(area);
}
// Protection handled manually in ProcessCandle
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdInd)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var macdSig = (IMovingAverageConvergenceDivergenceSignalValue)macdInd;
var macdValue = macdSig.Macd ?? 0m;
var signalValue = macdSig.Signal ?? 0m;
_currentClose = candle.ClosePrice;
var macdDir = EvaluateMacd(macdValue, signalValue);
var percDir = Supervisor();
if (macdDir > 0 && percDir > 0 && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
_isLong = true;
}
else if (macdDir < 0 && percDir < 0 && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_entryPrice = candle.ClosePrice;
_isLong = false;
}
if (Position > 0 && _isLong)
{
if (_currentTakeProfit > 0 && candle.ClosePrice >= _entryPrice + _currentTakeProfit)
SellMarket(Math.Abs(Position));
else if (_currentStopLoss > 0 && candle.ClosePrice <= _entryPrice - _currentStopLoss)
SellMarket(Math.Abs(Position));
}
else if (Position < 0 && !_isLong)
{
if (_currentTakeProfit > 0 && candle.ClosePrice <= _entryPrice - _currentTakeProfit)
BuyMarket(Math.Abs(Position));
else if (_currentStopLoss > 0 && candle.ClosePrice >= _entryPrice + _currentStopLoss)
BuyMarket(Math.Abs(Position));
}
AddOpen(candle.OpenPrice);
}
private int EvaluateMacd(decimal macd, decimal signal)
{
if (!_macdInitialized)
{
_prevMacd = macd;
_prevSignal = signal;
_macdInitialized = true;
return 0;
}
var result = 0;
if (macd < 0 && macd >= signal && _prevMacd <= _prevSignal)
result = 1;
else if (macd > 0 && macd <= signal && _prevMacd >= _prevSignal)
result = -1;
_prevMacd = macd;
_prevSignal = signal;
return result;
}
private void AddOpen(decimal open)
{
if (_openHistory.Length == 0)
return;
_openHistory[_historyIndex] = open;
_historyIndex++;
if (_historyIndex >= _openHistory.Length)
{
_historyIndex = 0;
_historyFilled = true;
}
}
private bool TryGetOpen(int shift, out decimal price)
{
if (_openHistory.Length == 0)
{
price = 0m;
return false;
}
if (!_historyFilled && shift >= _historyIndex)
{
price = 0m;
return false;
}
var index = _historyIndex - 1 - shift;
if (index < 0)
index += _openHistory.Length;
price = _openHistory[index];
return true;
}
private decimal Perceptron1()
{
if (!TryGetOpen(P1, out var o1) ||
!TryGetOpen(P1 * 2, out var o2) ||
!TryGetOpen(P1 * 3, out var o3) ||
!TryGetOpen(P1 * 4, out var o4))
return 0m;
var w1 = X11 - 100;
var w2 = X12 - 100;
var w3 = X13 - 100;
var w4 = X14 - 100;
var a1 = _currentClose - o1;
var a2 = o1 - o2;
var a3 = o2 - o3;
var a4 = o3 - o4;
return w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4;
}
private decimal Perceptron2()
{
if (!TryGetOpen(P2, out var o1) ||
!TryGetOpen(P2 * 2, out var o2) ||
!TryGetOpen(P2 * 3, out var o3) ||
!TryGetOpen(P2 * 4, out var o4))
return 0m;
var w1 = X21 - 100;
var w2 = X22 - 100;
var w3 = X23 - 100;
var w4 = X24 - 100;
var a1 = _currentClose - o1;
var a2 = o1 - o2;
var a3 = o2 - o3;
var a4 = o3 - o4;
return w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4;
}
private decimal Perceptron3()
{
if (!TryGetOpen(P3, out var o1) ||
!TryGetOpen(P3 * 2, out var o2) ||
!TryGetOpen(P3 * 3, out var o3) ||
!TryGetOpen(P3 * 4, out var o4))
return 0m;
var w1 = X31 - 100;
var w2 = X32 - 100;
var w3 = X33 - 100;
var w4 = X34 - 100;
var a1 = _currentClose - o1;
var a2 = o1 - o2;
var a3 = o2 - o3;
var a4 = o3 - o4;
return w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4;
}
private int Supervisor()
{
if (Pass >= 3)
{
if (Perceptron3() > 0m)
{
if (Perceptron2() > 0m)
{
_currentStopLoss = Sl2;
_currentTakeProfit = Tp2;
return 1;
}
}
else
{
if (Perceptron1() < 0m)
{
_currentStopLoss = Sl1;
_currentTakeProfit = Tp1;
return -1;
}
}
return 0;
}
if (Pass == 2)
{
if (Perceptron2() > 0m)
{
_currentStopLoss = Sl2;
_currentTakeProfit = Tp2;
return 1;
}
return 0;
}
if (Pass == 1)
{
if (Perceptron1() < 0m)
{
_currentStopLoss = Sl1;
_currentTakeProfit = Tp1;
return -1;
}
return 0;
}
return 0;
}
}
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, Array
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal, MovingAverageConvergenceDivergence, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class neural_network_macd_strategy(Strategy):
def __init__(self):
super(neural_network_macd_strategy, self).__init__()
self._x11 = self.Param("X11", 120).SetDisplay("X11", "Weight 1 for perceptron 1", "Perceptron1")
self._x12 = self.Param("X12", 80).SetDisplay("X12", "Weight 2 for perceptron 1", "Perceptron1")
self._x13 = self.Param("X13", 110).SetDisplay("X13", "Weight 3 for perceptron 1", "Perceptron1")
self._x14 = self.Param("X14", 90).SetDisplay("X14", "Weight 4 for perceptron 1", "Perceptron1")
self._tp1 = self.Param("Tp1", 100.0).SetDisplay("Take Profit 1", "Take profit for perceptron 1", "Perceptron1")
self._sl1 = self.Param("Sl1", 50.0).SetDisplay("Stop Loss 1", "Stop loss for perceptron 1", "Perceptron1")
self._p1 = self.Param("P1", 10).SetDisplay("P1", "Shift for perceptron 1", "Perceptron1")
self._x21 = self.Param("X21", 130).SetDisplay("X21", "Weight 1 for perceptron 2", "Perceptron2")
self._x22 = self.Param("X22", 70).SetDisplay("X22", "Weight 2 for perceptron 2", "Perceptron2")
self._x23 = self.Param("X23", 115).SetDisplay("X23", "Weight 3 for perceptron 2", "Perceptron2")
self._x24 = self.Param("X24", 85).SetDisplay("X24", "Weight 4 for perceptron 2", "Perceptron2")
self._tp2 = self.Param("Tp2", 100.0).SetDisplay("Take Profit 2", "Take profit for perceptron 2", "Perceptron2")
self._sl2 = self.Param("Sl2", 50.0).SetDisplay("Stop Loss 2", "Stop loss for perceptron 2", "Perceptron2")
self._p2 = self.Param("P2", 10).SetDisplay("P2", "Shift for perceptron 2", "Perceptron2")
self._x31 = self.Param("X31", 125).SetDisplay("X31", "Weight 1 for perceptron 3", "Perceptron3")
self._x32 = self.Param("X32", 75).SetDisplay("X32", "Weight 2 for perceptron 3", "Perceptron3")
self._x33 = self.Param("X33", 105).SetDisplay("X33", "Weight 3 for perceptron 3", "Perceptron3")
self._x34 = self.Param("X34", 95).SetDisplay("X34", "Weight 4 for perceptron 3", "Perceptron3")
self._p3 = self.Param("P3", 10).SetDisplay("P3", "Shift for perceptron 3", "Perceptron3")
self._pass_count = self.Param("Pass", 3).SetDisplay("Pass", "Number of perceptrons to use", "General")
self._candle_type = self.Param("CandleType", tf(15)).SetDisplay("Candle Type", "Type of candles", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(neural_network_macd_strategy, self).OnReseted()
self._macd_initialized = False
self._prev_macd = 0
self._prev_signal = 0
self._open_history = []
self._current_close = 0
self._current_sl = 0
self._current_tp = 0
self._entry_price = 0
self._is_long = False
def OnStarted2(self, time):
super(neural_network_macd_strategy, self).OnStarted2(time)
self._macd_initialized = False
self._prev_macd = 0
self._prev_signal = 0
max_lag = max(self._p1.Value, self._p2.Value, self._p3.Value) * 4 + 1
self._open_history = [0] * max_lag
self._hist_index = 0
self._hist_filled = False
self._current_close = 0
self._current_sl = 0
self._current_tp = 0
self._entry_price = 0
self._is_long = False
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = 12
macd.Macd.LongMa.Length = 26
macd.SignalMa.Length = 9
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(macd, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, macd)
self.DrawOwnTrades(area)
def _add_open(self, open_price):
if len(self._open_history) == 0:
return
self._open_history[self._hist_index] = open_price
self._hist_index += 1
if self._hist_index >= len(self._open_history):
self._hist_index = 0
self._hist_filled = True
def _try_get_open(self, shift):
if len(self._open_history) == 0:
return None
if not self._hist_filled and shift >= self._hist_index:
return None
index = self._hist_index - 1 - shift
if index < 0:
index += len(self._open_history)
return self._open_history[index]
def _perceptron(self, p, x1, x2, x3, x4):
o1 = self._try_get_open(p)
o2 = self._try_get_open(p * 2)
o3 = self._try_get_open(p * 3)
o4 = self._try_get_open(p * 4)
if o1 is None or o2 is None or o3 is None or o4 is None:
return 0
w1 = x1 - 100
w2 = x2 - 100
w3 = x3 - 100
w4 = x4 - 100
return w1 * (self._current_close - o1) + w2 * (o1 - o2) + w3 * (o2 - o3) + w4 * (o3 - o4)
def _supervisor(self):
pass_val = self._pass_count.Value
if pass_val >= 3:
if self._perceptron(self._p3.Value, self._x31.Value, self._x32.Value, self._x33.Value, self._x34.Value) > 0:
if self._perceptron(self._p2.Value, self._x21.Value, self._x22.Value, self._x23.Value, self._x24.Value) > 0:
self._current_sl = self._sl2.Value
self._current_tp = self._tp2.Value
return 1
else:
if self._perceptron(self._p1.Value, self._x11.Value, self._x12.Value, self._x13.Value, self._x14.Value) < 0:
self._current_sl = self._sl1.Value
self._current_tp = self._tp1.Value
return -1
return 0
if pass_val == 2:
if self._perceptron(self._p2.Value, self._x21.Value, self._x22.Value, self._x23.Value, self._x24.Value) > 0:
self._current_sl = self._sl2.Value
self._current_tp = self._tp2.Value
return 1
return 0
if pass_val == 1:
if self._perceptron(self._p1.Value, self._x11.Value, self._x12.Value, self._x13.Value, self._x14.Value) < 0:
self._current_sl = self._sl1.Value
self._current_tp = self._tp1.Value
return -1
return 0
return 0
def _evaluate_macd(self, macd_val, signal_val):
if not self._macd_initialized:
self._prev_macd = macd_val
self._prev_signal = signal_val
self._macd_initialized = True
return 0
result = 0
if macd_val < 0 and macd_val >= signal_val and self._prev_macd <= self._prev_signal:
result = 1
elif macd_val > 0 and macd_val <= signal_val and self._prev_macd >= self._prev_signal:
result = -1
self._prev_macd = macd_val
self._prev_signal = signal_val
return result
def OnProcess(self, candle, macd_ind):
if candle.State != CandleStates.Finished:
return
macd_val = macd_ind.Macd if macd_ind.Macd is not None else 0
signal_val = macd_ind.Signal if macd_ind.Signal is not None else 0
self._current_close = candle.ClosePrice
macd_dir = self._evaluate_macd(float(macd_val), float(signal_val))
perc_dir = self._supervisor()
if macd_dir > 0 and perc_dir > 0 and self.Position <= 0:
self.BuyMarket(self.Volume + Math.Abs(self.Position))
self._entry_price = candle.ClosePrice
self._is_long = True
elif macd_dir < 0 and perc_dir < 0 and self.Position >= 0:
self.SellMarket(self.Volume + Math.Abs(self.Position))
self._entry_price = candle.ClosePrice
self._is_long = False
if self.Position > 0 and self._is_long:
if self._current_tp > 0 and candle.ClosePrice >= self._entry_price + self._current_tp:
self.SellMarket(Math.Abs(self.Position))
elif self._current_sl > 0 and candle.ClosePrice <= self._entry_price - self._current_sl:
self.SellMarket(Math.Abs(self.Position))
elif self.Position < 0 and not self._is_long:
if self._current_tp > 0 and candle.ClosePrice <= self._entry_price - self._current_tp:
self.BuyMarket(Math.Abs(self.Position))
elif self._current_sl > 0 and candle.ClosePrice >= self._entry_price + self._current_sl:
self.BuyMarket(Math.Abs(self.Position))
self._add_open(candle.OpenPrice)
def CreateClone(self):
return neural_network_macd_strategy()