dhan
¶
Dhan historical candle download.
Dhan is the one broker here that splits its candles across two endpoints: daily bars come from
/v2/charts/historical and everything intraday from /v2/charts/intraday, with differently
formatted dates. Both are POSTs carrying a JSON body, and both answer columnar - one parallel
array per field rather than a list of rows - so the response is transposed into bars here.
Three things were settled against the live API on 2026-09-12 rather than taken from the documentation.
Dhan serves exactly five intraday resolutions: 1, 5, 15, 25 and 60 minutes. Asking for any other
number is not refused, it returns an empty array, so an unsupported interval looks exactly like a
market holiday. INTERVALS therefore lists only what Dhan actually serves.
An empty window and a window before the instrument's history begins are reported differently. A
valid instrument with nothing to show - a Saturday - answers 200 with empty arrays. A window that
predates the instrument's first bar answers 400 with DH-907, which reads like an error and is
not one; it is the same "there is nothing here" in different clothes, so it is parsed as an empty
window. Retiring a series on DH-907 would kill RELIANCE the moment its backward walk stepped
past 2002.
Daily bars are capped by nothing: a single request for 1990 to today returns the instrument's entire history. Intraday requests are refused beyond ninety days, with a message that says so.
Classes:
| Name | Description |
|---|---|
DhanCandles |
Downloads Dhan's historical candles. |
DhanCandles
¶
Bases: BrokerCandles
Downloads Dhan's historical candles.
Dhan candle downloader.
apiis an authenticatedDhanAPI. One is constructed when not supplied, which establishes a session if the stored one has expired.
Example programs
"""Fetches one intraday window and one daily window of RELIANCE bars from Dhan, and turns Dhan's columnar answer into bars.
Dhan serves intraday and daily candles from two endpoints that take the same body with differently formatted dates: the intraday one wants a time of day on each date and an interval code, the daily one wants bare dates. It answers with one array per field rather than one object per bar, and `parse_response` transposes them.
A `DhanCandles` constructor opens a PostgreSQL connection and logs in to Dhan when the stored session has expired, so the program builds the object without running its constructor, as `test_runs/candle_parse.py` does, and gives it a stand-in for `DhanAPI`. The stand-in records every POST and answers with arrays shaped like Dhan's recorded responses, wrapped in the `status_code` and `data` envelope the real API class returns. Notice that intraday bars keep the UTC offset their epoch decodes to, so 03:45 UTC is the 09:15 India time bar, while the daily bar, stamped with an epoch that decodes to 18:30 UTC, lands on midnight India time on 2002-01-01, and that a cash instrument's answer has no `open_interest` array, so open interest is None.
Run it from the project root:
python examples/stock_brokers/instruments/historical/dhan/DhanCandles/example_1_intraday_and_daily_windows.py
"""
import datetime
from stock_brokers.instruments.historical.dhan import (
DhanCandles,
)
class RecordedDhanAPI:
"""A stand-in for `DhanAPI` that answers each endpoint with one recorded columnar response.
Attributes:
requests (list): The (url, body) pairs it was sent.
"""
def __init__(self):
"""Builds the stand-in with no requests recorded.
Returns:
None: This method returns nothing.
"""
self.requests = []
def post(self, url, json=None):
"""Records a POST request and answers it with recorded arrays.
Args:
url (str): The endpoint the downloader called.
json (dict): The request body it sent.
Returns:
dict: The response envelope, with the arrays under `data`.
"""
self.requests.append((url, json))
if url.endswith('/intraday'):
data = {
'timestamp': [
1788839100.0,
1788839400.0,
],
'open': [
1306.8,
1303.1,
],
'high': [
1306.8,
1304.0,
],
'low': [
1300.0,
1301.0,
],
'close': [
1303.1,
1302.0,
],
'volume': [
174555.0,
98434.0,
],
}
else:
data = {
'timestamp': [
1009823400.0,
],
'open': [
55.73,
],
'high': [
56.46,
],
'low': [
55.31,
],
'close': [
55.75,
],
'volume': [
688738.0,
],
}
return {
'status_code': 200,
'data': data,
}
class IntradayAndDailyWindowsExample:
"""Fetches and parses one intraday and one daily window.
Attributes:
api (RecordedDhanAPI): The stand-in Dhan API.
candles (DhanCandles): The downloader being shown.
"""
def __init__(self):
"""Builds the downloader around the stand-in API without opening a database connection.
Returns:
None: This method returns nothing.
"""
self.api = RecordedDhanAPI()
self.candles = object.__new__(DhanCandles)
self.candles._api = self.api
def fetch_and_print(self, interval, start_date, end_date):
"""Fetches one window of RELIANCE bars and prints the request and the bars.
Args:
interval (str): The stored interval name.
start_date (datetime.date): The first day of the window.
end_date (datetime.date): The last day of the window.
Returns:
None: This method returns nothing.
"""
payload = self.candles.fetch_candles('2885|NSE_EQ|EQUITY', interval, start_date, end_date)
url, body = self.api.requests[-1]
print(f'{interval}: {url}')
print(f' body: {body}')
for bar in self.candles.parse_response(payload, interval):
bar_time, open_price, high, low, close, volume, open_interest = bar
print(f' {bar_time.isoformat()} open {open_price} high {high} low {low} close {close} volume {volume} oi {open_interest}')
def run(self):
"""Fetches the intraday window and then the daily one.
Returns:
None: This method returns nothing.
"""
self.fetch_and_print('5minute', datetime.date(2026, 9, 8), datetime.date(2026, 9, 8))
self.fetch_and_print('day', datetime.date(2002, 1, 1), datetime.date(2002, 1, 31))
if __name__ == '__main__':
IntradayAndDailyWindowsExample().run()
It prints:
5minute: https://api.dhan.co/v2/charts/intraday
body: {'securityId': '2885', 'exchangeSegment': 'NSE_EQ', 'instrument': 'EQUITY', 'oi': True, 'interval': '5', 'fromDate': '2026-09-08 00:00:00', 'toDate': '2026-09-08 23:59:59'}
2026-09-08T03:45:00+00:00 open 1306.8 high 1306.8 low 1300.0 close 1303.1 volume 174555.0 oi None
2026-09-08T03:50:00+00:00 open 1303.1 high 1304.0 low 1301.0 close 1302.0 volume 98434.0 oi None
day: https://api.dhan.co/v2/charts/historical
body: {'securityId': '2885', 'exchangeSegment': 'NSE_EQ', 'instrument': 'EQUITY', 'oi': True, 'fromDate': '2002-01-01', 'toDate': '2002-01-31'}
2002-01-01T00:00:00+05:30 open 55.73 high 56.46 low 55.31 close 55.75 volume 688738.0 oi None
"""Lists Dhan's instruments as download identifiers, reads what an identifier says about its instrument, and classifies Dhan's refusals.
Dhan's security id is not unique on its own: 2885 is RELIANCE in the NSE cash segment and an old currency option in another. So `instruments` joins the security id, Dhan's exchange segment name and the instrument type into one identifier, leaving out any segment Dhan does not chart. The program gives the downloader a stand-in database connection whose cursor returns four rows as the instrument master query would, one of them in NSE's commodity segment, which Dhan lists but cannot chart and which is therefore dropped.
`identifier_parts` takes an identifier apart again and `series_context` reads its exchange and segments; the latter is a classmethod, so it needs no downloader at all.
Last, the program fetches through a stand-in for `DhanAPI` that raises the `DhanAPIException` each of three refusals produces. `Data_Error` is Dhan's answer for an empty window, so it comes back as no bars rather than an error; `Input_Exception` naming missing fields is what an expired contract gets, so the series is retired; and `Invalid_Authentication` means the session is dead. The downloader is built without its constructor, as in `test_runs/candle_parse.py`, so nothing connects to PostgreSQL or logs in.
Run it from the project root:
python examples/stock_brokers/instruments/historical/dhan/DhanCandles/example_2_identifiers_and_refusals.py
"""
import datetime
from stock_brokers.api.dhan import (
DhanAPIException,
)
from stock_brokers.instruments.historical.base import (
CandleError,
)
from stock_brokers.instruments.historical.dhan import (
DhanCandles,
)
class InstrumentMasterCursor:
"""A stand-in database cursor that answers the instrument master query with fixed rows.
Attributes:
rows (list): The rows `fetchall` returns.
"""
def __init__(self, rows):
"""Holds the rows to answer with.
Args:
rows (list): Tuples of (exchange, segment, security id, instrument, expiry).
Returns:
None: This method returns nothing.
"""
self.rows = rows
def __enter__(self):
"""Enters the `with` block.
Returns:
InstrumentMasterCursor: This cursor.
"""
return self
def __exit__(self, exception_type, exception, traceback):
"""Leaves the `with` block without suppressing an exception.
Args:
exception_type (type | None): The exception's class, if one was raised.
exception (BaseException | None): The exception, if one was raised.
traceback (types.TracebackType | None): Its traceback, if one was raised.
Returns:
bool: Always False.
"""
return False
def execute(self, query):
"""Accepts the query without running it.
Args:
query (str): The SQL the downloader sent.
Returns:
None: This method returns nothing.
"""
def fetchall(self):
"""Returns the fixed rows.
Returns:
list: The rows.
"""
return self.rows
class InstrumentMasterConnection:
"""A stand-in PostgreSQL connection over a few rows of Dhan's instrument master.
Attributes:
rows (list): The rows every cursor answers with.
"""
def __init__(self):
"""Builds the connection with four instrument master rows.
Returns:
None: This method returns nothing.
"""
self.rows = [
(
'NSE',
'E',
'2885',
'EQUITY',
None,
),
(
'NSE',
'I',
'13',
'INDEX',
None,
),
(
'MCX',
'M',
'565899',
'FUTCOM',
'2026-10-19',
),
(
'NSE',
'M',
'801234',
'OPTFUT',
'2026-10-27',
),
]
def cursor(self):
"""Opens a cursor.
Returns:
InstrumentMasterCursor: A cursor over the rows.
"""
return InstrumentMasterCursor(self.rows)
def commit(self):
"""Accepts a commit.
Returns:
None: This method returns nothing.
"""
class RefusingDhanAPI:
"""A stand-in for `DhanAPI` that refuses with a chosen Dhan error.
Attributes:
error_type (str): Dhan's error type to refuse with.
error_message (str): Dhan's error message to refuse with.
"""
def __init__(self, error_type, error_message):
"""Holds the refusal to raise.
Args:
error_type (str): Dhan's error type, such as `Data_Error`.
error_message (str): Dhan's error message.
Returns:
None: This method returns nothing.
"""
self.error_type = error_type
self.error_message = error_message
def post(self, url, json=None):
"""Refuses a POST request.
Args:
url (str): The endpoint the downloader called.
json (dict): The request body it sent.
Returns:
dict: Never returns.
Raises:
DhanAPIException: Always, carrying the chosen type and message.
"""
raise DhanAPIException(code=self.error_type, message=self.error_message)
class IdentifiersAndRefusalsExample:
"""Lists identifiers, reads them, and classifies three refusals.
Attributes:
candles (DhanCandles): The downloader being shown.
"""
def __init__(self):
"""Builds the downloader around the stand-in connection without running its constructor.
Returns:
None: This method returns nothing.
"""
self.candles = object.__new__(DhanCandles)
self.candles._connection = InstrumentMasterConnection()
def show_identifiers(self):
"""Lists the instruments and reads each identifier.
Returns:
None: This method returns nothing.
"""
instruments = self.candles.instruments()
print(f'Instruments to download: {len(instruments)} of 4 rows')
for identifier, instrument_type, expiry in instruments:
security_id, exchange_segment, _ = self.candles.identifier_parts(identifier)
context = DhanCandles.series_context(identifier)
print(f'{identifier}: security id {security_id} in {exchange_segment}, exchange {context.exchange}, {len(context.segments)} segments to search, expiry {expiry}')
def show_refusals(self):
"""Fetches through three refusing stand-ins and prints what each became.
Returns:
None: This method returns nothing.
"""
refusals = [
(
'Data_Error',
'System is unable to fetch data due to incorrect parameters or no data present',
),
(
'Input_Exception',
'Missing required fields, bad values for parameters etc.',
),
(
'Invalid_Authentication',
'Client ID or user generated access token is invalid or expired.',
),
]
for error_type, error_message in refusals:
self.candles._api = RefusingDhanAPI(error_type, error_message)
try:
payload = self.candles.fetch_candles(
'52175|NSE_FNO|FUTSTK',
'5minute',
datetime.date(2024, 1, 1),
datetime.date(2024, 3, 30),
)
outcome = f'empty window, {len(self.candles.parse_response(payload, "5minute"))} bars'
except CandleError as error:
outcome = type(error).__name__
print(f'{error_type}: {outcome}')
def run(self):
"""Runs both parts in turn.
Returns:
None: This method returns nothing.
"""
self.show_identifiers()
self.show_refusals()
if __name__ == '__main__':
IdentifiersAndRefusalsExample().run()
It prints:
Instruments to download: 3 of 4 rows
2885|NSE_EQ|EQUITY: security id 2885 in NSE_EQ, exchange nse, 6 segments to search, expiry None
13|IDX_I|INDEX: security id 13 in IDX_I, exchange None, 8 segments to search, expiry None
565899|MCX_COMM|FUTCOM: security id 565899 in MCX_COMM, exchange mcx, 16 segments to search, expiry 2026-10-19
Data_Error: empty window, 0 bars
Input_Exception: CandleInstrumentUnknown
Invalid_Authentication: CandleAuthenticationError
Methods:
| Name | Description |
|---|---|
instruments |
Every instrument Dhan has ever published, identified the way its charting API needs. |
identifier_parts |
Split a stored Dhan identifier into the fields a request needs. |
series_context |
Read the exchange and segment out of a Dhan identifier. |
fetch_candles |
Fetch one window of Dhan candles. |
parse_response |
Turn Dhan's columnar response into bars. |
priority_for |
Where a series sits in the queue. Lower is worked first. |
seed |
Make sure every instrument and interval this broker serves has a progress row. |
claim |
The next series due for a window, or None when there is nothing to do. |
next_window |
The date range to ask for next, as a |
relogin |
Establish a fresh session and rebuild everything built on the old one. |
after_relogin |
Re-establish anything a downloader keeps on top of the broker session. Nothing by default. |
visit |
Fetch one window for one series and store it, advancing the watermark. |
run |
Work the queue until stopped, out of work, or out of time. |
summary |
How far the download has got, for a status display. |
close |
Close the database connection. |
Source code in stock_brokers/instruments/historical/dhan.py
instruments
¶
Every instrument Dhan has ever published, identified the way its charting API needs.
Dhan's security id is not unique on its own: id 2885 is Reliance in the NSE cash segment
and a 2025 EURINR option in the currency one, and the instrument master repeats ids this
way some ten thousand times. So the identifier stored in the progress table is the
security id, the exchange segment and the instrument name joined by bars, which is
exactly the three fields the request body carries, and fetch_candles splits it again.
Instruments in a segment Dhan does not chart are left out rather than registered and retired one request at a time.
Example usage
Source code in stock_brokers/instruments/historical/dhan.py
identifier_parts
staticmethod
¶
Split a stored Dhan identifier into the fields a request needs.
The identifier is built in instruments and taken apart here and nowhere else, so the two
cannot drift apart.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
For example "2885|NSE_EQ|EQUITY". |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
The security id, the exchange segment and the instrument type. |
Example usage
security_id, exchange_segment, _ = self.candles.identifier_parts(identifier)
Source code in stock_brokers/instruments/historical/dhan.py
series_context
classmethod
¶
Read the exchange and segment out of a Dhan identifier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
For example "2885|NSE_EQ|EQUITY". |
required |
Returns:
| Type | Description |
|---|---|
SeriesContext
|
The security id, with the exchange and segments its segment names. |
Example usage
Source code in stock_brokers/instruments/historical/dhan.py
fetch_candles
¶
Fetch one window of Dhan candles.
The two endpoints take the same body but differently formatted dates, the intraday one wanting a time of day as well, which is why they are built separately.
tokenis thesecurityId|exchangeSegment|instrumentidentifier frominstruments.intervalis the stored interval name.start_dateandend_datebound the window, inclusive.
Example usage
payload = self.candles.fetch_candles('2885|NSE_EQ|EQUITY', interval, start_date, end_date)
payload = self.candles.fetch_candles(
'52175|NSE_FNO|FUTSTK',
'5minute',
datetime.date(2024, 1, 1),
datetime.date(2024, 3, 30),
)
Source code in stock_brokers/instruments/historical/dhan.py
_classify
staticmethod
¶
Decide what a Dhan refusal means for the series being downloaded.
Dhan names its conditions in the body, and the API class turns that into an exception whose code is the error type and whose message is the error message. Returning None says the refusal is not a failure at all but an empty window.
DH-905, Dhan's catch-all input error, is the one that retires a series: it is what an
expired contract answers. It is also what a window wider than Dhan allows answers, and
that would be a bug in this module rather than a dead instrument, so the message is read
before the series is given up on.
exceptionis what the API call raised.
Source code in stock_brokers/instruments/historical/dhan.py
parse_response
¶
Turn Dhan's columnar response into bars.
Dhan answers with one array per field rather than one object per bar, so the arrays are transposed here. Open interest is absent for cash instruments rather than present and zero, so it is read defensively.
Dhan's timestamps are epoch seconds, and its daily bars already land on midnight India time; they are normalised anyway, so every broker's day bar in this project is produced the same way.
payloadis thedataobject from the response.intervalis the stored interval name.
Example usage
outcome = f'empty window, {len(self.candles.parse_response(payload, "5minute"))} bars'
Source code in stock_brokers/instruments/historical/dhan.py
priority_for
¶
Where a series sits in the queue. Lower is worked first.
The ordering is the difference between useful data in days and useful data in months. Cash instruments come first because they have the deepest history and are wanted most; expired options come last because there are tens of thousands of them, each holding a few weeks of data, and most will be refused outright. Coarse intervals beat fine ones within each tier, since a day bar covers a year in one request and a minute bar covers two months.
instrument_typeis the broker's own type string.expiryis the contract expiry, or None for cash.intervalis the stored interval name.
Example usage
priority = self.downloader.priority_for(instrument_type, expiry, interval)
Source code in stock_brokers/instruments/historical/base.py
_interval_minutes
staticmethod
¶
Minutes in an interval name like 15minute, or 0 when it is not a minute interval.
intervalis the stored interval name.
Source code in stock_brokers/instruments/historical/base.py
seed
¶
Make sure every instrument and interval this broker serves has a progress row.
Safe to run again: existing rows are left exactly as they are, so re-seeding after a new instrument master picks up what is new without disturbing what is already downloaded. Returns how many rows were added.
Example usage
print(f'Series added by the first seed: {self.downloader.seed()}')
print(f'Series added by a second seed: {self.downloader.seed()}')
Source code in stock_brokers/instruments/historical/base.py
_insert_series
¶
Register one batch of series, leaving any that already exist exactly as they are.
cursoris an open cursor on this broker's connection.batchis a list of(token, interval, priority, seeded_date)tuples.
Source code in stock_brokers/instruments/historical/base.py
claim
¶
The next series due for a window, or None when there is nothing to do.
Ordered by priority and then by how long a series has been waiting. Series that have reached the broker's limit, or that are backing off after failures, are skipped.
Example usage
Source code in stock_brokers/instruments/historical/base.py
next_window
¶
The date range to ask for next, as a (start, end, direction) triple.
The backward walk comes first and runs to completion. A series starts at today and steps back a window at a time until it reaches the broker's earliest date or the broker stops returning anything, and only then does it start filling forward with whatever has happened since.
Doing it the other way round - preferring forward whenever the last stored bar is older than today - looks reasonable and does not work: outside market hours the last bar is always older than today, so the series asks for the same empty recent window forever and never walks back at all.
seriesis a row fromclaim.
Example usage
start_date, end_date, direction = self.downloader.next_window(series)
Source code in stock_brokers/instruments/historical/base.py
_fetch_bars
¶
Fetch and parse one window, logging in once first if the broker refuses the session.
A token expiring does not mean the download is over, and it does not mean this series is at
fault. The rule is to log in first and then carry on: one login, then the same window
again. Only if that login fails, or the fresh session is refused as well, does the error go
back up - where visit and run stop the broker rather than carry on with a dead token.
Both alternatives are worse. Stopping outright leaves the download waiting for systemd to restart it into a login, and recording the refusal against whichever series it was on and moving to the next sends thousands of refused requests an hour - enough for Cloudflare to ban the machine from Fyers.
The login is re-armed by any window that comes back without a session error, so a backfill that runs for weeks gets its one login at each daily expiry.
token,interval,start_dateandend_dateare as forfetch_candles.
Source code in stock_brokers/instruments/historical/base.py
relogin
¶
Establish a fresh session and rebuild everything built on the old one.
Through ensure_session, so it takes the same Redis lock and obeys the same floor on login
attempts as any other process using it - which means it may wait, and may find another
process has already logged in, in which case that session is simply used.
Raises:
| Type | Description |
|---|---|
CandleAuthenticationError
|
If no working session could be established. |
Example usage
Source code in stock_brokers/instruments/historical/base.py
_build_api
¶
Construct this broker's authenticated API class, around whatever session is now stored.
The broker's own class from api_class_for, which is what every downloader builds. A
downloader that builds its API differently overrides this.
Source code in stock_brokers/instruments/historical/base.py
after_relogin
¶
Re-establish anything a downloader keeps on top of the broker session. Nothing by default.
For a broker whose historical endpoint takes a second credential minted from the session, such as a separate market data token, this is where that credential is renewed.
Example usage
Source code in stock_brokers/instruments/historical/base.py
visit
¶
Fetch one window for one series and store it, advancing the watermark.
Returns the number of bars stored. The bars and the watermark are written in the same transaction, so an interruption costs at most the window in flight - and repeating that window is harmless, because bars upsert on their key.
seriesis a row fromclaim.
Example usage
stored = downloader.visit(self.series('738561'))
Source code in stock_brokers/instruments/historical/base.py
_store
¶
Write a window's bars and move the watermark, in one transaction.
tokenis the broker's instrument identifier.intervalis the stored interval name.barsare the parsed(time, open, high, low, close, volume, oi)tuples.requested_fromis the start of the window that was asked for.directionis whether the walk was forward or backward.previous_latestis the newest bar already stored, used to tell a forward window that gained ground from one that returned only what was already known.
Source code in stock_brokers/instruments/historical/base.py
_value_within_bigint
¶
Returns a bar's count, or None when the count cannot fit in a BIGINT column.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token
|
str
|
The broker's instrument identifier. |
required |
interval
|
str
|
The stored interval name. |
required |
bar_time
|
datetime
|
The time of the bar the value belongs to. |
required |
column
|
str
|
The name of the column the value is written to, either "volume" or "oi". |
required |
value
|
int | float | None
|
The count exactly as the broker sent it. |
required |
Returns:
| Type | Description |
|---|---|
int | float | None
|
The value unchanged when it fits in a BIGINT or is not a number, and None when it does not fit. |
Source code in stock_brokers/instruments/historical/base.py
_record_empty
¶
Note that a window came back with nothing, and step past it.
token,intervalidentify the series.streakis how many empty windows in a row this makes.requested_fromis the start of the window that was asked for.directionis whether the walk was forward or backward.
Source code in stock_brokers/instruments/historical/base.py
_finish_backfill
¶
Mark the backward walk complete, leaving the series collecting forward.
Distinct from _retire: the broker has no more past for this series, but it will keep
producing new bars. Retiring it here would quietly stop a live instrument updating.
token,intervalidentify the series.reasonis why the walk stopped, kept for review.
Source code in stock_brokers/instruments/historical/base.py
_defer_until_tomorrow
¶
Put a series aside until the next day.
Reached when a forward window brings nothing new, which outside market hours is every forward window. Without it the highest priority series would be re-fetched continuously and spend the whole rate limit re-reading bars already stored.
token,intervalidentify the series.reasonis recorded as the outcome.
Source code in stock_brokers/instruments/historical/base.py
_record_throttle
¶
Note that the broker asked us to slow down, without holding it against the series.
The window is left exactly as it was, so the same series is claimed again once the limiter's back off has passed and the work is simply redone.
token,intervalidentify the series.reasonis what the broker said.
Source code in stock_brokers/instruments/historical/base.py
_record_failure
¶
Note a failure and push the next attempt out exponentially.
An hour, then two, four, eight and so on, so a series that is permanently broken stops crowding out the ones that are not.
token,intervalidentify the series.reasonis what went wrong, stored for a human to read.
Source code in stock_brokers/instruments/historical/base.py
_retire
¶
Stop asking for a series the broker will not serve.
token,intervalidentify the series.reasonis why, stored so the decision can be reviewed.
Source code in stock_brokers/instruments/historical/base.py
run
¶
Work the queue until stopped, out of work, or out of time.
Returns a (visits, bars) pair.
stop_eventis athreading.Eventthat ends the loop when set.deadline_secondsends the loop after this long, for a job with a nightly window.
Example usage
print(f'A run asked to stop before it starts: {self.downloader.run(stop_event=stop_event)}')
Source code in stock_brokers/instruments/historical/base.py
summary
¶
How far the download has got, for a status display.
Example usage
Source code in stock_brokers/instruments/historical/base.py
close
¶
Close the database connection.
Example usage