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() { } } }