QuantConnect / QuantConnect/Lean
Add Daily & Hour with extended market hours
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 21.7k
- Forks
- 5.3k
- Avg merge
- 2d 22h
- Merged PRs (30d)
- 34
Description
Expected Behavior
self.history[TradeBar](self._symbol, 30, Resolution.DAILY, extended_market_hours=False) should return data only within market hours
Actual Behavior
It instead returns extended_market_hours=True data
While the following test is configured for IB data, it can also be run in the cloud, where you can compare the Actual values. They are the same for both extended_market_hours True and False, where I would expect them to be different. ie.
extended_market_hours = False
********** TEST FAILED: 2024-08-06 (MH) ********** Expected: NTES: O: 86.44 H: 87.44 L: 86.01 C: 86.65 V: 1126405 Actual : NTES: O: 83.96141 H: 85.04083 L: 83.6405 C: 84.28232 V: 1733373
extended_market_hours = True
********** TEST FAILED: 2024-08-06 (EMH) ********** Expected: NTES: O: 86.5 H: 87.44 L: 86 C: 86.67 V: 1342268 Actual : NTES: O: 83.96141 H: 85.04083 L: 83.6405 C: 84.28232 V: 1733373
Potential Solution
Reproducing the Problem
from datetime import date
from AlgorithmImports import *
"""
Test History extended_market_hours.
Result: `extended_market_hours = False` fails and instead returns the data for `extended_market_hours = True`.
Test date for data was 2024-08-05 and 2024-08-06 for NTES from IB.
"""
class BUG_HISTORY_EMH(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 10, 1)
self.set_cash(100000)
# Run this test with extended_market_hours set to True or False
# Change the value of extended_market_hours to test both scenarios
# self.extended_market_hours = True
self.extended_market_hours = False
# below can be run in HOUR or DAILY resolution, the result is the same
self._symbol = self.add_equity(
"NTES", Resolution.HOUR, extended_market_hours=self.extended_market_hours
).symbol
# Test data for NTES run locally with IB data. Replace with QuantConnect data if needed.
# Contains data for EMH and non-EMH (MH)
self.test_data = {
"2024-08-05": {
"MH": TradeBar(date(2024, 8, 5), self._symbol, 86.5, 88.5, 86.5, 87.91, 1483903),
"EMH": TradeBar(date(2024, 8, 5), self._symbol, 89.0, 89.0, 86.5, 88.8, 1539853),
},
"2024-08-06": {
"MH": TradeBar(date(2024, 8, 6), self._symbol, 86.44, 87.44, 86.01, 86.65, 1126405),
"EMH": TradeBar(date(2024, 8, 6), self._symbol, 86.5, 87.44, 86.0, 86.67, 1342268),
},
}
self.tests_executed = []
# Manual Warm up
self.count = 1
history = list(
self.history[TradeBar](
self._symbol,
30,
Resolution.DAILY,
extended_market_hours=self.extended_market_hours,
)
)
for bar in history:
self.log(f"{bar.end_time}: count:{self.count} History Bar: {bar}")
self.count += 1
self.assert_data(bar)
def on_data(self, data: Slice):
pass
def assert_data(self, bar: TradeBar) -> None:
"""
Assert that the bar data matches the expected values stored in self.test_data
"""
# Get the expected data for the current date
date_str = bar.end_time.strftime("%Y-%m-%d")
if date_str not in self.test_data:
return # No test data for this date
expected_data = self.test_data[date_str]
# Check if the bar is for extended market hours or not
if self.extended_market_hours:
key = "EMH"
else:
key = "MH"
if key not in expected_data:
raise ValueError(f"No expected data for {key} on date {date_str}")
expected_bar: TradeBar = expected_data[key]
# Do a soft assert of the expected values
if not all(
[
bar.open == expected_bar.open,
bar.high == expected_bar.high,
bar.low == expected_bar.low,
bar.close == expected_bar.close,
bar.volume == expected_bar.volume,
]
):
self.log(
f"\n{'*' * 10} TEST FAILED: {date_str} ({key}) {'*' * 10}"
f"\nExpected: {expected_bar}"
f"\n Actual : {bar}"
)
self.tests_executed += [False]
return
self.log(
f"\n{'*' * 10} TEST PASSED: {date_str} ({key}) {'*' * 10}"
f"\nData matches: {bar.open}, {bar.high}, {bar.low}, {bar.close}, {bar.volume}"
)
self.tests_executed += [True]
def on_end_of_algorithm(self):
self.log(f"{'=' * 25}")
self.log("Test Configuration:")
self.log(f"Extended Market Hours: {self.extended_market_hours}")
self.log(f"{'=' * 25}")
self.log("Test Results:")
if len(self.tests_executed) == 2:
self.log("✅ 2 Tests Executed")
else:
self.log(f"❌ Expected 2 tests but only executed: {len(self.tests_executed)}")
if all(self.tests_executed):
self.log("✅ All tests passed!")
else:
self.log(f"❌ Tests Failed: {len(self.tests_executed) - sum(self.tests_executed)}")
self.log(f"{'=' * 25}")
System Information
windows 10, lean 1.0.218 for local
also ran in cloud
Checklist
- I have completely filled out this template
- I have confirmed that this issue exists on the current
masterbranch - I have confirmed that this is not a duplicate issue by searching issues
- I have provided detailed steps to reproduce the issue
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the reproducer's history[TradeBar] call and compare daily results for extended_market_hours=True and False using the NTES data described. Trace how add_equity and History apply the extended-hours setting; done means the two configurations return the expected distinct market-hours and extended-hours bars.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, python
- Domain
- backend, data
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100