Skip to content

Commit e33430d

Browse files
Added history provider and show demonstration of it in example.
1 parent 5fbb1f3 commit e33430d

4 files changed

Lines changed: 386 additions & 23 deletions

File tree

Demonstration.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,12 @@ public override void Initialize()
3434
Log("Algorithm Initialize");
3535

3636
SetStartDate(2025, 10, 1);
37-
SetStartDate(2025, 10, 16);
37+
SetEndDate(2025, 10, 16);
3838
SetCash(100000);
3939

4040
var exp = new DateTime(2025, 12, 19);
4141
var symbol = QuantConnect.Symbol.CreateFuture("ES", Market.CME, exp);
42-
_es = AddFutureContract(symbol, Resolution.Minute, true, 1, true);
43-
Log($"_es: {_es}");
42+
_es = AddFutureContract(symbol, Resolution.Second, true, 1, true);
4443
}
4544

4645
public override void OnData(Slice slice)

QuantConnect.DataBento/DataBentoDataDownloader.cs

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using System.IO;
1919
using System.Text;
2020
using System.Net.Http;
21+
using System.Net.Http.Headers;
2122
using System.Globalization;
2223
using System.Collections.Generic;
2324
using CsvHelper;
@@ -50,6 +51,10 @@ public DataBentoDataDownloader(string apiKey)
5051
{
5152
_apiKey = apiKey;
5253
_httpClient = new HttpClient();
54+
55+
// Set up HTTP Basic Authentication
56+
var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_apiKey}:"));
57+
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", credentials);
5358
}
5459

5560
public DataBentoDataDownloader()
@@ -73,30 +78,35 @@ public IEnumerable<BaseData> Get(DataDownloaderGetParameters parameters)
7378

7479
var dataset = "GLBX.MDP3"; // hard coded for now. Later on can add equities and options with different mapping
7580
var schema = GetSchema(resolution, tickType);
76-
var dbSymbol = MapSymbol(symbol);
81+
var dbSymbol = MapSymbolToDataBento(symbol);
7782

7883
// prepare body for Raw HTTP request
7984
var body = new StringBuilder();
8085
body.Append($"dataset={dataset}");
8186
body.Append($"&symbols={dbSymbol}");
8287
body.Append($"&schema={schema}");
83-
body.Append($"&start={startUtc:yyyy-MM-ddTHH:mm:ssZ}");
84-
body.Append($"&end={endUtc:yyyy-MM-ddTHH:mm:ssZ}");
85-
body.Append("&stype_in=continuous");
88+
body.Append($"&start={startUtc:yyyy-MM-ddTHH:mm}");
89+
body.Append($"&end={endUtc:yyyy-MM-ddTHH:mm}");
90+
body.Append("&stype_in=parent");
8691
body.Append("&encoding=csv");
8792

8893
var request = new HttpRequestMessage(
89-
HttpMethod.Post,
90-
"https://hist.databento.com/v0/timeseries.get_range")
94+
HttpMethod.Post,
95+
"https://hist.databento.com/v0/timeseries.get_range")
9196
{
9297
Content = new StringContent(body.ToString(), Encoding.UTF8, "application/x-www-form-urlencoded")
9398
};
9499

95-
// Add API key authentication
96-
request.Headers.Add("Authorization", $"Bearer {_apiKey}");
97-
98100
// send the request with the get range url
99101
var response = _httpClient.Send(request);
102+
103+
// Add error handling to see the actual error message
104+
if (!response.IsSuccessStatusCode)
105+
{
106+
var errorContent = response.Content.ReadAsStringAsync().Result;
107+
throw new HttpRequestException($"DataBento API error ({response.StatusCode}): {errorContent}");
108+
}
109+
100110
response.EnsureSuccessStatusCode();
101111

102112
using var stream = response.Content.ReadAsStream();
@@ -105,9 +115,10 @@ public IEnumerable<BaseData> Get(DataDownloaderGetParameters parameters)
105115

106116
if (tickType == TickType.Trade)
107117
{
108-
foreach (var record in csv.GetRecords<DatabentoBar>())
118+
if (resolution == Resolution.Tick)
109119
{
110-
if (resolution == Resolution.Tick)
120+
// For tick data, use the trades schema which returns individual trades
121+
foreach (var record in csv.GetRecords<DatabentoTrade>())
111122
{
112123
yield return new Tick
113124
{
@@ -117,7 +128,11 @@ public IEnumerable<BaseData> Get(DataDownloaderGetParameters parameters)
117128
Quantity = record.Size
118129
};
119130
}
120-
else
131+
}
132+
else
133+
{
134+
// For aggregated data, use the ohlcv schema which returns bars
135+
foreach (var record in csv.GetRecords<DatabentoBar>())
121136
{
122137
yield return new TradeBar
123138
{
@@ -206,13 +221,20 @@ private string GetSchema(Resolution resolution, TickType tickType)
206221
}
207222

208223
/// <summary>
209-
/// Map Lean Symbol to Databento symbol string for continous
224+
/// Maps a LEAN symbol to DataBento symbol format
210225
/// </summary>
211-
private static string MapSymbol(Symbol symbol)
226+
private string MapSymbolToDataBento(Symbol symbol)
212227
{
213228
if (symbol.SecurityType == SecurityType.Future)
214229
{
215-
return $"{symbol.ID.Symbol}.v.0";
230+
// For DataBento, use the root symbol with .FUT suffix for parent subscription
231+
// ES19Z25 -> ES.FUT
232+
var value = symbol.Value;
233+
234+
// Extract root by removing digits and month codes
235+
var root = new string(value.TakeWhile(c => !char.IsDigit(c)).ToArray());
236+
237+
return $"{root}.FUT";
216238
}
217239

218240
return symbol.Value;
@@ -222,26 +244,62 @@ private static string MapSymbol(Symbol symbol)
222244
/// Really used as a map from the http request to then get it in QC data structures
223245
private class DatabentoBar
224246
{
225-
public DateTime Timestamp { get; set; }
226-
public decimal Price { get; set; }
227-
public int Size { get; set; }
247+
[Name("ts_event")]
248+
public long TimestampNanos { get; set; }
249+
250+
public DateTime Timestamp => DateTimeOffset.FromUnixTimeSeconds(TimestampNanos / 1_000_000_000)
251+
.AddTicks((TimestampNanos % 1_000_000_000) / 100).UtcDateTime;
252+
253+
[Name("open")]
228254
public decimal Open { get; set; }
255+
256+
[Name("high")]
229257
public decimal High { get; set; }
258+
259+
[Name("low")]
230260
public decimal Low { get; set; }
261+
262+
[Name("close")]
231263
public decimal Close { get; set; }
264+
265+
[Name("volume")]
232266
public decimal Volume { get; set; }
233267
}
234268

269+
private class DatabentoTrade
270+
{
271+
[Name("ts_event")]
272+
public long TimestampNanos { get; set; }
273+
274+
public DateTime Timestamp => DateTimeOffset.FromUnixTimeSeconds(TimestampNanos / 1_000_000_000)
275+
.AddTicks((TimestampNanos % 1_000_000_000) / 100).UtcDateTime;
276+
277+
[Name("price")]
278+
public long PriceRaw { get; set; }
279+
280+
public decimal Price => PriceRaw * PriceScaleFactor;
281+
282+
[Name("size")]
283+
public int Size { get; set; }
284+
}
285+
235286
private class DatabentoQuote
236287
{
237288
[Name("ts_event")]
238-
public DateTime Timestamp { get; set; }
289+
public long TimestampNanos { get; set; }
290+
291+
public DateTime Timestamp => DateTimeOffset.FromUnixTimeSeconds(TimestampNanos / 1_000_000_000)
292+
.AddTicks((TimestampNanos % 1_000_000_000) / 100).UtcDateTime;
293+
239294
[Name("bid_px_00")]
240295
public long BidPrice { get; set; }
296+
241297
[Name("bid_sz_00")]
242298
public int BidSize { get; set; }
299+
243300
[Name("ask_px_00")]
244301
public long AskPrice { get; set; }
302+
245303
[Name("ask_sz_00")]
246304
public int AskSize { get; set; }
247305
}

QuantConnect.DataBento/DataBentoDataProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ public void Unsubscribe(SubscriptionDataConfig dataConfig)
273273
/// <param name="job">Job we're subscribing for</param>
274274
public void SetJob(LiveNodePacket job)
275275
{
276-
// Not sure what to do in here.
276+
// No action required for DataBento since the job details are not used in the subscription process.
277277
}
278278

279279
/// <summary>

0 commit comments

Comments
 (0)