1818using System . IO ;
1919using System . Text ;
2020using System . Net . Http ;
21+ using System . Net . Http . Headers ;
2122using System . Globalization ;
2223using System . Collections . Generic ;
2324using 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 }
0 commit comments