Heiken Ashi stratejisinin, LONG/SHORT yapılabilen tüm enstrümanlarda kullanılabilen versiyonudur.
Heiken Ashi barlarını hesaplayarak;
Pozitif barlarda (kapanış > açılış) alım,
Negatif barlarda (kapanış < açılış) açığa satış yapılır.
Barların strateji çalıştıktan sonra doğru şekilde hesaplanabilmesi için, bufferLength ile belirlenen sayıda bar geriye gidilerek hesaplama başlatılır.

using System;
using System.Collections.Generic;
using System.Linq;
using Matriks.Data.Symbol;
using Matriks.Engines;
using Matriks.Indicators;
using Matriks.Symbols;
using Matriks.AlgoTrader;
using Matriks.Trader.Core;
using Matriks.Trader.Core.Fields;
using Matriks.Trader.Core.TraderModels;
using Matriks.Lean.Algotrader.AlgoBase;
using Matriks.Lean.Algotrader.Models;
using Matriks.Lean.Algotrader.Trading;
using Matriks.Data.Tick;
using Matriks.Enumeration;
using Matriks.IntermediaryInstitutionAnalysis.Enums;
using Newtonsoft.Json;


namespace Matriks.Lean.Algotrader
{
	public class HeikenAshi_Futures : MatriksAlgo
	{
		// Strateji çalıştırılırken kullanacağımız parametreler. Eğer sembolle ilgili bir parametre ise,
		// "SymbolParameter" ile, değilse "Parameter" ile tanımlama yaparız. Parantez içindeki değerler default değerleridir.

		[SymbolParameter("BTC_USDT_FBIN")]
		public string Symbol;
		[Parameter(0.01)]
		public decimal Quantity;

		[Parameter(SymbolPeriod.Min)]
		public SymbolPeriod SymbolPeriod;

		public bool FirstBar = true;
		public int Position = 0;
		decimal HA_Open = 0;    //(open of previous bar + close of previous bar)/2
		decimal HA_High = 0;    //the maximum value from the high, open, or close of the current period
		decimal HA_Low = 0;     // minimum value from the low, open, or close of the current period
		decimal HA_Close = 0;
		int bufferlength = 30;
		decimal _open_ = 0, _high_ = 0, _low_ = 0, _close_ = 0;

		public override void OnInit()
		{
			AddSymbol(Symbol, SymbolPeriod);

			//Eger backtestte emri bir al bir sat seklinde gonderilmesi isteniyor bu true set edilir. 
			//Alttaki satırı silerek veya false geçerek emirlerin sirayla gönderilmesini engelleyebilirsiniz. 
			SendOrderSequential(false);
			WorkWithPermanentSignal(true);
		}

		/// <summary>
		/// Init islemleri tamamlaninca, bardatalar kullanmaya hazir hale gelince bu fonksiyon tetiklenir. Data uzerinde bir defa yapilacak islemler icin kullanilir
		/// </summary>
		public override void OnInitCompleted()
		{

		}

		/// <summary>
		/// Eklenen sembollerin bardata'ları ve indikatorler güncellendikçe bu fonksiyon tetiklenir. 
		/// </summary>
		/// <param name="barData">Bardata ve hesaplanan gerçekleşen işleme ait detaylar</param>
		public override void OnDataUpdate(BarDataCurrentValues barDataCurrentValues)
		{
			var barDataModel = GetBarData(Symbol, SymbolPeriod);
			var open = barDataModel.Open[barDataCurrentValues.LastUpdate.BarDataIndex - 1]; //barDataCurrentValues.LastUpdate.Open[barData.BarDataIndex -1]; //barDataModel.Open[barData.BarDataIndex -1]; 
			var high = barDataModel.High[barDataCurrentValues.LastUpdate.BarDataIndex - 1];
			var low = barDataModel.Low[barDataCurrentValues.LastUpdate.BarDataIndex - 1];
			var close = barDataModel.Close[barDataCurrentValues.LastUpdate.BarDataIndex - 1];
			Debug(open + ", " + high + ", " + low + ", " + close);

			//10 bar BUFFER
			if (FirstBar)
			{
				Debug(bufferlength + " bar buffer hesaplaniyor");
				for (int i = bufferlength; i >= 0; i--)
				{
					_open_ = Ref(barDataModel, OHLCType.Open, i);
					_high_ = Ref(barDataModel, OHLCType.High, i);
					_low_ = Ref(barDataModel, OHLCType.Low, i);
					_close_ = Ref(barDataModel, OHLCType.Close, i);
					HA_Open = i == bufferlength ? (_open_ + _close_) / 2 : HA_Open = (HA_Open + HA_Close) / 2; //at first iteration calculate HA_Open accordingly
					HA_Close = (_open_ + _high_ + _low_ + _close_) / 4;
					HA_High = Math.Max(_high_, Math.Max(HA_Open, HA_Close));
					HA_Low = Minimum(_low_, Math.Min(HA_Open, HA_Close));
					//Debug($"({i+1} : _open_ = {_open_} , _high_= {_high_}, _low_ = {_low_}, _close_ = {_close_})");
					if (HA_Close > HA_Open)
						Debug($"({i + 1} bar onceki fiyatlar: HA_Open = {Math.Round(HA_Open, 1)} , HA_Close= {HA_Close}, HA_High = {Math.Round(HA_High, 1)}, HA_Low = {HA_Low}. Bar POSITIVE)");
					else
						Debug($"({i + 1} bar onceki fiyatlar: HA_Open = {Math.Round(HA_Open, 1)} , HA_Close= {HA_Close}, HA_High = {Math.Round(HA_High, 1)}, HA_Low = {HA_Low}. Bar NEGATIVE)");
				}
				FirstBar = false;
			}
			else
			{
				HA_Open = (HA_Open + HA_Close) / 2;
				HA_Close = (open + high + low + close) / 4;
				HA_High = Math.Max(high, Math.Max(HA_Open, HA_Close));
				HA_Low = Minimum(low, Math.Min(HA_Open, HA_Close));
			}

			if (HA_Close > HA_Open)
			{
				Debug($"HA_Close: {HA_Close} > HA_Open: {Math.Round(HA_Open, 2)}. Bar POSITIVE");
				if (Position == 0)
				{
					SendMarketOrder(Symbol, Quantity, OrderSide.Buy);
					Position++;
					Debug("Alis emri gonderildi. Pozisyon = " + Position);
				}
				else if (Position == -1)
				{
					SendMarketOrder(Symbol, Quantity * 2, OrderSide.Buy);
					Position += 2;
					Debug("Alis emri gonderildi. Pozisyon = " + Position);
				}
			}
			if (HA_Close < HA_Open)
			{
				Debug($"HA_Close: {HA_Close} < HA_Open: {Math.Round(HA_Open, 2)}. Bar NEGATIVE");
				if (Position == 0)
				{
					SendMarketOrder(Symbol, Quantity, OrderSide.Sell);
					Position--;
					Debug("Satis emri gonderildi. Pozisyon = " + Position);
				}
				else if (Position == 1)
				{
					SendMarketOrder(Symbol, Quantity * 2, OrderSide.Sell);
					Position -= 2;
					Debug("Satis emri gonderildi. Pozisyon = " + Position);
				}
			}
		}

		/// <summary>
		/// Gönderilen emirlerin son durumu değiştikçe bu fonksiyon tetiklenir.
		/// </summary>
		/// <param name="barData">Emrin son durumu</param>
		public override void OnOrderUpdate(IOrder order)
		{
			if (order.OrdStatus.Obj == OrdStatus.Filled)
			{

			}
		}

		/// <summary>
		/// Strateji durdurulduğunda bu fonksiyon tetiklenir.
		/// </summary>
		public override void OnStopped()
		{
		}
	}
}