Class ReferenceClient
- Namespace
- DatabentoDotNet.Reference
- Assembly
- DatabentoDotNet.Reference.dll
A client for Databento's reference data API: security master, corporate actions, and adjustment factors.
public sealed class ReferenceClient : IAsyncDisposable
- Inheritance
-
ReferenceClient
- Implements
- Inherited Members
Examples
using DatabentoDotNet;
using DatabentoDotNet.Dbn;
using DatabentoDotNet.Reference;
using NodaTime;
await using var client = new ReferenceClient
{
ApiKey = new ApiKey(Environment.GetEnvironmentVariable("DATABENTO_API_KEY")!),
};
// Reference data is a separate Databento subscription from live and historical, so a 403 here is
// an entitlement answer rather than a client error.
await foreach (SecurityMaster listing in client.SecurityMaster.GetRangeAsync(
new SecurityMasterGetRangeParams
{
Symbols = Symbols.From("AAPL"),
StypeIn = SType.RawSymbol,
DateTimeRange = ReferenceDateTimeRange.Between(
Instant.FromUtc(2024, 1, 1, 0, 0), Instant.FromUtc(2024, 2, 1, 0, 0)),
}))
{
Console.WriteLine($"{listing.Symbol} {listing.SecurityType} {listing.Exchange} {listing.Isin}");
}
CorporateActions and AdjustmentFactors take the same shape of parameters and stream their rows the same way.
Remarks
Port of upstream's reference::Client (reference.rs:34-92) — its key,
base_url, gateway and request, which are the whole of that type once its
three subclient accessors are set aside.
This client sends through HistoricalClient, and that is the port rather than a
shortcut around one. Upstream's reference client is a separate type in the same crate that
reuses the historical transport through crate-internal visibility: its request composes
v{API_VERSION}/{slug} against a base URL derived from
HistoricalGateway, attaches the same HTTP Basic credential, and sends with the
same Accept: application/json default header and the same user agent. The reference API
is the historical transport with a different set of slugs. Separate .NET assemblies have no
equivalent of pub(crate) and this repo declares no InternalsVisibleTo anywhere,
so the reuse goes through HistoricalClient's public transport — which is public
for reasons of its own, documented on that type.
All three subclient properties, and all six of the reference API's endpoints.
AdjustmentFactors arrived with its endpoint in
#53,
SecurityMaster with its two in
#54,
and CorporateActions with its two documentation endpoints in
#56
and its get_range in
#55.
A facade with no endpoints on it would be a public empty class, which is why none of the three
was declared in #48 — the same call HistoricalClient records for M3.
Thread-safe for concurrent requests once configured, for the same reason HistoricalClient is: everything below the surface is one HttpClient, and the properties are init-only and therefore frozen before the first request.
No builder. Upstream's ClientBuilder<AK> is generic type-state whose only
purpose is to make "no API key" unrepresentable. C# 11 required init
properties do that natively, checked by the compiler at every construction site. See
PORTING.md §2.
Constructors
ReferenceClient()
Creates a client that owns its transport. Configure it through the init properties.
public ReferenceClient()
Remarks
The transport is built on first use, not here. An init accessor runs after the constructor body, so ApiKey does not exist yet at the point where an eager constructor would want to build an HttpClient from it; deferring is what makes required init properties and a fully configured transport compatible at all. ExecutionAndPublication because this type is documented as safe for concurrent requests: two threads racing into the first request must get one transport, not two.
ReferenceClient(HistoricalClient)
Creates a client that sends through an existing HistoricalClient, sharing its connection pool, and does not dispose it.
[SetsRequiredMembers]
public ReferenceClient(HistoricalClient transport)
Parameters
transportHistoricalClientThe client to send through. Not disposed by this one.
Remarks
For a consumer holding both. The reference and historical APIs share a host, a gateway, an API version and an auth scheme, so two independently configured clients mean two HttpClients and two connection pools to the same origin for no reason. This constructor is how they become one.
Ownership does not transfer. DisposeAsync() leaves
transport open, because whoever created it is still using it. Disposing
it while this client is alive is the caller's mistake to avoid, and it surfaces as
ObjectDisposedException from the next request rather than as anything subtler.
The configuration properties below report transport's own settings, so
they describe what this client actually does either way. Assigning one of them alongside
this constructor throws rather than silently having no effect — see
ApiKey.
Exceptions
- ArgumentNullException
transportis null.
Properties
AdjustmentFactors
The adjustment_factors.* endpoints — the multipliers that make a price series
comparable across splits, dividends and other capital events.
public AdjustmentFactorsClient AdjustmentFactors { get; }
Property Value
Remarks
The first of this client's three endpoint-group facades (#53); #54–#56 add the rest. Built once and cached, because this client is documented thread-safe for concurrent requests and a bare null-coalescing assignment would let two threads each build one — the same arrangement Metadata and its three siblings use. Its one endpoint costs money, and reference data is a separate Databento product from historical market data: see GetRangeAsync(AdjustmentFactorsGetRangeParams, CancellationToken).
ApiKey
The API key to authenticate with. Validated when it is constructed.
public required ApiKey ApiKey { get; init; }
Property Value
Remarks
The type, never a string: ToString() is
redacted, so formatting the object that holds the key cannot leak it. The key reaches the
wire in exactly one place in this library — the Authorization header
HistoricalClient builds — and nowhere else, not as a query parameter and not
as a form field.
Assigning this alongside ReferenceClient(HistoricalClient) throws. That constructor's transport is already built and already carries a credential, so an assignment here could not reach the wire — and a property reporting a key that no request carries is exactly the kind of confidently wrong answer that is worse than an exception. The other four configuration properties refuse an assignment on that path for the same reason.
Exceptions
- InvalidOperationException
The client was constructed over an existing HistoricalClient.
BaseUrl
public Uri? BaseUrl { get; init; }
Property Value
Remarks
The advanced knob, as upstream documents base_url: it exists for a test harness or a
proxy, and it is how this library's own tests reach a mock gateway. A path on it is
preserved — BaseUrl does that work and documents why it takes
explicit effort.
Exceptions
- ArgumentException
The URL is not absolute.
- InvalidOperationException
The client was constructed over an existing HistoricalClient.
CorporateActions
The corporate_actions.* endpoints: what happened to a security, and the documentation
the server keeps about its own events and enumerations.
public CorporateActionsClient CorporateActions { get; }
Property Value
Remarks
The largest of this client's three endpoint-group facades — three endpoints where the others have two and one, and a hundred-and-four-field row where the others have fifty and nineteen. #56 shipped ListEventsAsync(CancellationToken) and ListEnumsAsync(CancellationToken); #55 added GetRangeAsync(CorporateActionsGetRangeParams, CancellationToken), which is what makes this a data endpoint group rather than a documentation one. Built once and cached, for the reason AdjustmentFactors gives. Alone among the three, two of its endpoints are not known to cost anything — they return documentation rather than market data, and #57 prices them rather than assuming.
Gateway
The gateway to send requests to. Defaults to Bo1.
public HistoricalGateway Gateway { get; init; }
Property Value
Remarks
Ignored when BaseUrl is set. HistoricalGateway is reused rather
than re-declared: upstream's reference client derives its base URL from that same enum
(reference.rs:37), and a second gateway type for one API would be a worse public
surface than one shared with a sibling package.
Exceptions
- InvalidOperationException
The client was constructed over an existing HistoricalClient.
LoggerFactory
Where to send this client's log messages, or null for none.
public ILoggerFactory? LoggerFactory { get; init; }
Property Value
Remarks
How the API's X-Warning header surfaces, and the only route it has. See
LoggerFactory, which this is handed to and which documents
why a warnings property on every response was rejected.
Exceptions
- InvalidOperationException
The client was constructed over an existing HistoricalClient.
SecurityMaster
The security_master.* endpoints — what a listing is, where it trades, and every
identifier it is known by.
public SecurityMasterClient SecurityMaster { get; }
Property Value
Remarks
The second of this client's three endpoint-group facades (#54). Built once and cached, for the reason AdjustmentFactors gives. Both its endpoints cost money, and one property common to both can spend an ISIN entitlement rather than only money — see AllocateIsins.
Transport
The HTTP transport this client sends through — built from the properties above on first read, or the one supplied to ReferenceClient(HistoricalClient).
public HistoricalClient Transport { get; }
Property Value
Remarks
Public, and that is a decision rather than an omission — the same one HistoricalClient records for its own transport. The reference API has six endpoints (ROADMAP.md §6 lists them) and a caller who needs a seventh the week it ships should not have to wait for a release: SendAsync(HttpMethod, string, IEnumerable<KeyValuePair<string, string>>?, string?, CancellationToken) and SendZstdJsonLinesAsync<T>(HttpMethod, string, IEnumerable<KeyValuePair<string, string>>?, JsonTypeInfo<T>, CancellationToken) reach any slug the API serves.
It is also what makes the shared-transport constructor legible: a client built over an existing HistoricalClient returns that same instance here.
This is a transport, not a facade. That the historical endpoints are reachable from it is a consequence of them living on the same type upstream puts them on, not an invitation to call them from here.
Exceptions
- ObjectDisposedException
The client has been disposed.
UserAgentExtension
Text to append to this library's User-Agent, identifying the application built on
it, or null to send the library's own user agent alone.
public string? UserAgentExtension { get; init; }
Property Value
Remarks
Port of upstream's user_agent_extension (reference.rs:135-138), which composes
it the same way: the library's user agent, a space, then this. An extension that is not a
well-formed sequence of user-agent products and comments is rejected when the first request
is sent rather than reaching Databento's logs malformed.
Exceptions
- InvalidOperationException
The client was constructed over an existing HistoricalClient.
Methods
DisposeAsync()
Releases the transport, if this client created it.
public ValueTask DisposeAsync()
Returns
- ValueTask
The transport's own disposal, or a completed task when there is nothing to release.
Remarks
Idempotent, and safe on a client that never sent a request: the transport is built on first use, so there is nothing to release until one has been. Using the client after this throws ObjectDisposedException rather than quietly building a second one.
A transport supplied to ReferenceClient(HistoricalClient) is left open. This client did not create it and does not know who else holds it.