Converting Intraday Series to Daily Series
Converting Intraday Series to Daily Series
25 Jul 2024, 12:59
Hello fellow traders and developers,
I've written a piece of code using the IndicatorHelper
that transforms an intraday series into a daily series. The objective is to create these daily bars by correcting the timestamps, ensuring that daily bars are considered to start and end at midnight UTC. This approach helps eliminate various discontinuities in 24-hour markets.
However, I'm encountering an issue when trying to calculate a simple moving average (SMA) on the daily series. Here’s the relevant part of my code:
dailySma = Indicators.MovingAverage(
dailyBars.ClosePrices,
SMAPeriod,
MovingAverageType.Simple
);
This line throws the following exception:
System.ArgumentException
HResult=0x80070057
Message=Bars and source series must be from the same TimeFrame and Symbol. (Parameter 'parameterValues')
Source=cTrader.Automate.Adapters
StackTrace:
at cTrader.Automate.Adapters.IndicatorsApi.IndicatorContainerAdapter.GetParameters(Type clrType, Bars bars, Object[] parameterValues)
at cTrader.Automate.Adapters.IndicatorsApi.IndicatorContainerAdapter.GetIndicatorInternal[TIndicator](Bars bars, Object[] parameterValues)
at cTrader.Automate.Adapters.IndicatorsApi.IndicatorContainerAdapter.GetIndicator[TIndicator](Object[] parameterValues)
at cTrader.Automate.Adapters.IndicatorsApi.IndicatorContainerAdapter.SimpleMovingAverage(DataSeries source, Int32 periods)
at cTrader.Automate.Adapters.IndicatorsApi.IndicatorContainerAdapter.MovingAverage(DataSeries source, Int32 periods, MovingAverageType maType)
at cAlgo.Indicators.VCM_DailySMA.Initialize() in C:\Users\Vinicius\Documents\cAlgo\Sources\Indicators\VCM_DailySMA\VCM_DailySMA\VCM_DailySMA.cs:line 34
at cTrader.Automate.Adapters.IndicatorWrapper.OnLoad()
at cTrader.Automate.Host.Runtime.Instances.SmallIndicatorInstance.<.ctor>b__11_0()
at cTrader.Automate.Host.Runtime.AlgoExecutor.SmallAlgoCodeExecutorExtensions.<>c.<.cctor>b__3_0(Action a)
at cTrader.Automate.Domain.Shared.Dispatcher.DispatcherAction.DispatcherActionImpl`1.Invoke()
at cTrader.Automate.Host.Runtime.AlgoExecutor.SmallAlgoCodeExecutor.ExecuteInternal(IDispatcherAction& action, Boolean raiseFrameEvent)
I've tried several approaches to resolve this but haven't had any luck. Has anyone faced a similar issue or have any suggestions on how to properly apply an indicator to a transformed series like this?
Thanks in advance for your help!
using System;
using System.Collections;
using System.Collections.Generic;
using cAlgo.API;
using cAlgo.API.Indicators;
namespace cAlgo.Indicators
{
[Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]
public class VCM_DailySMA : Indicator
{
[Parameter("SMA Period", DefaultValue = 14)]
public int SMAPeriod { get; set; }
[Output("Daily SMA", LineColor = "Red")]
public IndicatorDataSeries DailySMA { get; set; }
private Bars dailyBars;
private MovingAverage dailySma;
protected override void Initialize()
{
#if DEBUG
// Debugging
var result = System.Diagnostics.Debugger.Launch();
if (result is false)
{
Print("Debugger not launched");
}
#endif
// Cria a série diária
// dailyBars = MarketData.GetBars(TimeFrame.Daily);
dailyBars = IndicatorHelper.GetDailyBarsFromHourlyBars(Bars);
// Cria a SMA na série diária
dailySma = Indicators.MovingAverage(
dailyBars.ClosePrices,
SMAPeriod,
MovingAverageType.Simple
);
}
public override void Calculate(int index)
{
// Calcula o índice correspondente na série diária
int dailyIndex = dailyBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
if (dailyIndex < 0)
{
dailyBars.LoadMoreHistory();
dailyIndex = dailyBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);
}
DailySMA[index] = dailySma.Result[dailyIndex - 1];
}
protected override void OnException(Exception exception)
{
Print($@"Erro: {exception.Message}");
}
}
public static class IndicatorHelper
{
public static FakeBars GetDailyBarsFromHourlyBars(Bars hourlyBars)
{
List<double> dailyClosePrices;
List<DateTime> dailyOpenTimes;
GetDailyClosePricesFromHourlyBars(hourlyBars, out dailyClosePrices, out dailyOpenTimes);
// Criar FakeBars com os preços de fechamento e tempos de abertura diários
return new FakeBars(
hourlyBars: hourlyBars,
dailyClosePrices: new FakeDataSeries(dailyClosePrices),
dailyOpenTimes: new FakeTimeSeries(dailyOpenTimes),
timeframe: TimeFrame.Daily,
// timeframe: hourlyBars.TimeFrame,
symbolName: hourlyBars.SymbolName
);
}
public static void GetDailyClosePricesFromHourlyBars(
Bars hourlyBars,
out List<double> dailyClosePrices,
out List<DateTime> dailyOpenTimes
)
{
if (hourlyBars == null || hourlyBars.Count == 0)
throw new ArgumentException("hourlyBars cannot be null or empty");
// Listas para armazenar os preços de fechamento e os tempos de abertura do timeframe D1
dailyClosePrices = new List<double>();
dailyOpenTimes = new List<DateTime>();
// Obter o dia da primeira barra
var currentDay = hourlyBars.OpenTimes[0].Day;
// Armazenar a primeira barra do dia atual
dailyOpenTimes.Add(hourlyBars.OpenTimes[0]);
// Iterar sobre as barras H1
for (int i = 1; i < hourlyBars.Count; i++)
{
// Verificar se mudou o dia
if (hourlyBars.OpenTimes[i].Day != currentDay)
{
// Adicionar o preço de fechamento da última barra do dia anterior
dailyClosePrices.Add(hourlyBars.ClosePrices[i - 1]);
// Atualizar o dia atual
currentDay = hourlyBars.OpenTimes[i].Day;
// Adicionar o tempo de abertura da primeira barra do novo dia
dailyOpenTimes.Add(hourlyBars.OpenTimes[i]);
}
}
// Adicionar o preço de fechamento da última barra disponível (último dia completo)
dailyClosePrices.Add(hourlyBars.ClosePrices[hourlyBars.Count - 1]);
}
}
public class FakeBars : Bars
{
private FakeDataSeries dailyClosePrices;
private FakeTimeSeries dailyOpenTimes;
private Bars hourlyBars;
private TimeFrame timeframe;
private string symbolName;
public FakeBars(
FakeDataSeries dailyClosePrices,
FakeTimeSeries dailyOpenTimes,
Bars hourlyBars,
TimeFrame timeframe,
string symbolName
)
{
this.dailyClosePrices = dailyClosePrices;
this.dailyOpenTimes = dailyOpenTimes;
this.hourlyBars = hourlyBars;
this.timeframe = timeframe;
this.symbolName = symbolName;
}
public Bar this[int index] => throw new NotImplementedException();
public Bar LastBar => throw new NotImplementedException();
public int Count => throw new NotImplementedException();
public TimeFrame TimeFrame
{
get { return timeframe; }
}
public string SymbolName
{
get { return symbolName; }
}
public DataSeries OpenPrices => throw new NotImplementedException();
public DataSeries HighPrices => throw new NotImplementedException();
public DataSeries LowPrices => throw new NotImplementedException();
public DataSeries ClosePrices
{
get { return dailyClosePrices; }
}
public DataSeries TickVolumes => throw new NotImplementedException();
public DataSeries MedianPrices => throw new NotImplementedException();
public DataSeries TypicalPrices => throw new NotImplementedException();
public DataSeries WeightedPrices => throw new NotImplementedException();
public TimeSeries OpenTimes
{
get { return dailyOpenTimes; }
}
public event Action<BarsHistoryLoadedEventArgs> HistoryLoaded;
public event Action<BarsHistoryLoadedEventArgs> Reloaded;
public event Action<BarsTickEventArgs> Tick;
public event Action<BarOpenedEventArgs> BarOpened;
public event Action<BarClosedEventArgs> BarClosed;
public IEnumerator<Bar> GetEnumerator()
{
throw new NotImplementedException();
}
public DataSeries GetPrices(PriceType priceType)
{
throw new NotImplementedException();
}
public Bar Last(int index)
{
throw new NotImplementedException();
}
public int LoadMoreHistory()
{
var newBars = hourlyBars.LoadMoreHistory();
List<double> dailyClosePrices;
List<DateTime> dailyOpenTimes;
IndicatorHelper.GetDailyClosePricesFromHourlyBars(
hourlyBars,
out dailyClosePrices,
out dailyOpenTimes
);
this.dailyClosePrices = new FakeDataSeries(dailyClosePrices);
this.dailyOpenTimes = new FakeTimeSeries(dailyOpenTimes);
return newBars;
}
public void LoadMoreHistoryAsync()
{
throw new NotImplementedException();
}
public void LoadMoreHistoryAsync(Action<BarsHistoryLoadedEventArgs> callback)
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public class FakeTimeSeries : TimeSeries
{
private List<DateTime> dailyOpenTimes;
public FakeTimeSeries(List<DateTime> dailyOpenTimes)
{
this.dailyOpenTimes = dailyOpenTimes;
}
public DateTime this[int index]
{
get { return dailyOpenTimes[index]; }
}
public DateTime LastValue
{
get
{
if (dailyOpenTimes.Count == 0)
return DateTime.MinValue;
return dailyOpenTimes[dailyOpenTimes.Count - 1];
}
}
public int Count
{
get { return dailyOpenTimes.Count; }
}
public IEnumerator<DateTime> GetEnumerator()
{
return dailyOpenTimes.GetEnumerator();
}
public int GetIndexByExactTime(DateTime dateTime)
{
for (int i = 0; i < dailyOpenTimes.Count; i++)
{
if (dailyOpenTimes[i] == dateTime)
return i;
}
return -1;
}
public int GetIndexByTime(DateTime dateTime)
{
for (int i = dailyOpenTimes.Count - 1; i >= 0; i--)
{
if (dailyOpenTimes[i] <= dateTime)
return i;
}
return -1;
}
public DateTime Last(int index)
{
return dailyOpenTimes[dailyOpenTimes.Count - 1 - index];
}
IEnumerator IEnumerable.GetEnumerator()
{
return dailyOpenTimes.GetEnumerator();
}
}
public class FakeDataSeries : DataSeries
{
private List<double> dailyClosePrices;
public FakeDataSeries(List<double> dailyClosePrices)
{
this.dailyClosePrices = dailyClosePrices;
}
public double this[int index]
{
get { return dailyClosePrices[index]; }
}
public double LastValue
{
get
{
if (dailyClosePrices.Count == 0)
return 0;
return dailyClosePrices[dailyClosePrices.Count - 1];
}
}
public int Count
{
get { return dailyClosePrices.Count; }
}
public IEnumerator<double> GetEnumerator()
{
return dailyClosePrices.GetEnumerator();
}
public double Last(int index)
{
return dailyClosePrices[dailyClosePrices.Count - 1 - index];
}
IEnumerator IEnumerable.GetEnumerator()
{
return dailyClosePrices.GetEnumerator();
}
}
}