Skip to content

Commit 210a662

Browse files
committed
fix: FlashAlphaSource catches sparse-data exceptions, skips bars (v0.1.4)
1 parent d47df35 commit 210a662

6 files changed

Lines changed: 61 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
88

99
_No changes yet._
1010

11+
## [0.1.4] — 2026-06-02
12+
13+
### Fixed
14+
15+
- **`FlashAlphaSource.For` now catches sparse-data exceptions and returns null bars instead of erroring out.** Sparse data is the expected case for LEAN custom-data subscriptions — weekends, holidays, pre-RTH midnight ticks on Daily resolution, dates without coverage, etc. v0.1.3 propagated `NoDataError` / `NoCoverageError` / `InvalidAtError` straight from the SDK, which caused LEAN's data feed worker to abort the entire subscription (manifesting as `Sequence contains no elements` on backtest completion). The source now catches those three exception types and caches an empty payload, so `Parse` returns null and LEAN skips the bar cleanly. The algorithm waits for the next session, no error surfaces. Caught by V8 Tier 2 smoke after v0.1.3 — LEAN's daily-resolution subscription calls `For` with `date=midnight UTC`, but the FlashAlpha API only has data at market-hours timestamps. Same fix shipped to C# (`FlashAlphaSource.cs`) and Python (`data.source.source_for`).
16+
1117
## [0.1.3] — 2026-06-02
1218

1319
### Fixed
@@ -44,7 +50,8 @@ Initial public release. The bridge ships with full coverage of the FlashAlpha hi
4450
- **Documentation corpus.** Repo-root `README.md` with side-by-side C# + Python examples, `docs/getting-started.md`, `docs/data-types.md` (per-bar field reference for all 17 endpoints), `docs/auth.md`, `docs/troubleshooting.md`, and five `docs/recipes/*.md` cookbooks.
4551
- **`llms.txt`** site map per [llmstxt.org](https://llmstxt.org/).
4652

47-
[Unreleased]: https://github.com/FlashAlpha-lab/flashalpha-quantconnect/compare/v0.1.3...HEAD
53+
[Unreleased]: https://github.com/FlashAlpha-lab/flashalpha-quantconnect/compare/v0.1.4...HEAD
54+
[0.1.4]: https://github.com/FlashAlpha-lab/flashalpha-quantconnect/compare/v0.1.3...v0.1.4
4855
[0.1.3]: https://github.com/FlashAlpha-lab/flashalpha-quantconnect/compare/v0.1.2...v0.1.3
4956
[0.1.2]: https://github.com/FlashAlpha-lab/flashalpha-quantconnect/compare/v0.1.1...v0.1.2
5057
[0.1.1]: https://github.com/FlashAlpha-lab/flashalpha-quantconnect/compare/v0.1.0...v0.1.1

src/csharp/FlashAlpha.QuantConnect/Data/FlashAlphaSource.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,32 @@ public static SubscriptionDataSource For(string endpoint, Symbol symbol, DateTim
4242
// Eager fetch — store in cache for Reader to consume.
4343
// Synchronous block via GetAwaiter().GetResult() because LEAN's
4444
// GetSource is sync; bar classes can't be async.
45-
var json = _http.Value
46-
.FetchJsonAsync(endpoint, symbol.Value, date)
47-
.GetAwaiter().GetResult();
48-
_cache[key] = json;
45+
//
46+
// Sparse data is the expected case for LEAN custom-data subscriptions —
47+
// weekends, holidays, pre-RTH midnight ticks on Daily resolution, etc.
48+
// When the FlashAlpha API reports no data at the requested timestamp
49+
// (NoDataException / NoCoverageException / InvalidAtException), cache
50+
// an empty payload so Parse returns null and LEAN skips the bar cleanly.
51+
// The algorithm waits for the next session, no error surfaces.
52+
try
53+
{
54+
var json = _http.Value
55+
.FetchJsonAsync(endpoint, symbol.Value, date)
56+
.GetAwaiter().GetResult();
57+
_cache[key] = json;
58+
}
59+
catch (FlashAlpha.Historical.NoDataException)
60+
{
61+
_cache[key] = string.Empty;
62+
}
63+
catch (FlashAlpha.Historical.NoCoverageException)
64+
{
65+
_cache[key] = string.Empty;
66+
}
67+
catch (FlashAlpha.Historical.InvalidAtException)
68+
{
69+
_cache[key] = string.Empty;
70+
}
4971
return new SubscriptionDataSource(
5072
SentinelPrefix + key,
5173
SubscriptionTransportMedium.Rest,

src/csharp/FlashAlpha.QuantConnect/FlashAlpha.QuantConnect.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<PackageId>FlashAlpha.QuantConnect</PackageId>
44
<RootNamespace>FlashAlpha.QuantConnect</RootNamespace>
5-
<Version>0.1.3</Version>
5+
<Version>0.1.4</Version>
66
<Authors>FlashAlpha</Authors>
77
<Company>FlashAlpha</Company>
88
<Copyright>Copyright (c) 2026 FlashAlpha</Copyright>

src/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "flashalpha-quantconnect"
7-
version = "0.1.3"
7+
version = "0.1.4"
88
description = "FlashAlpha options-flow and dealer-positioning data as QuantConnect LEAN custom-data bars. GEX, DEX, VEX, vol surface, 0DTE, VRP, max-pain."
99
readme = "README.md"
1010
license = "MIT"

src/python/src/flashalpha_quantconnect/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""FlashAlpha options-flow data as QuantConnect LEAN custom-data bars."""
22

3-
__version__ = "0.1.3"
3+
__version__ = "0.1.4"
44

55
from . import config
66
from .data.exposure import (

src/python/src/flashalpha_quantconnect/data/source.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,36 @@ def _to_pascal_case(snake: str) -> str:
4949
def source_for(endpoint: str, symbol: Any, date: datetime) -> Any:
5050
"""Eagerly fetch the JSON for (endpoint, ticker, date), cache it, return
5151
a sentinel ``SubscriptionDataSource`` that ``parse`` will resolve via
52-
the cache key."""
52+
the cache key.
53+
54+
Sparse data is the expected case for LEAN custom-data subscriptions —
55+
weekends, holidays, pre-RTH midnight ticks on a Daily resolution etc.
56+
When the FlashAlpha API reports no data at the requested timestamp
57+
(``NoDataError`` / ``NoCoverageError`` / ``InvalidAtError``), we cache
58+
an empty payload so ``parse`` returns ``None`` and LEAN skips the bar
59+
cleanly. The algorithm waits for the next session, no error surfaces.
60+
"""
5361
from QuantConnect import SubscriptionTransportMedium
5462
from QuantConnect.Data import SubscriptionDataSource, FileFormat
5563

64+
# Import sparsely — the SDK exception classes only need to exist at
65+
# call time, and importing them at module load would couple this file
66+
# to the SDK's namespace shape.
67+
from flashalpha_historical.exceptions import (
68+
NoDataError, NoCoverageError, InvalidAtError,
69+
)
70+
5671
ticker = symbol.Value
5772
key = _make_key(endpoint, ticker, date)
58-
payload = _get_client().fetch_json(endpoint=endpoint, ticker=ticker, at=date)
5973

60-
with _cache_lock:
61-
_cache[key] = json.dumps(payload)
74+
try:
75+
payload = _get_client().fetch_json(endpoint=endpoint, ticker=ticker, at=date)
76+
with _cache_lock:
77+
_cache[key] = json.dumps(payload)
78+
except (NoDataError, NoCoverageError, InvalidAtError):
79+
# Sparse data — cache empty so parse returns None; LEAN skips this bar.
80+
with _cache_lock:
81+
_cache[key] = ""
6282

6383
return SubscriptionDataSource(
6484
f"{_SENTINEL_PREFIX}{key}",

0 commit comments

Comments
 (0)