indmoney
¶
IND Money historical candle download.
The endpoint is api.indstocks.com/market/historical/<interval> - the interval is a path segment,
so each interval is a different URL - and it takes the window as epoch milliseconds while
answering in epoch seconds. That asymmetry reads like a documentation error and is genuine.
An instrument is named by a scrip code, an exchange prefix joined to IND Money's security id by an
underscore, and the prefix is not the exchange the instrument master names. Measured on
2026-09-12: NSE cash is NSE_2885, BSE cash is BSE_500325, an index is NSE_40000001, and an
NSE derivative is NFO_68777. Asking for NSE_68777 - the exchange the master actually records
for that contract - is accepted and answers zero bars, silently, which is the worst possible
failure mode and the reason the prefix is derived from the segment here rather than copied from
the exchange column.
BSE derivatives are not served at all: both BFO_ and BSE_ are refused with "Invalid scrip
codes", so they are left out of the queue rather than registered and retired one request each.
The window cap is a truncation rather than a refusal, and it is measured in trading days: minute
intervals up to thirty return the newest five trading days of whatever window is asked for, the
hourly ones eleven, and daily, weekly and monthly one year. Asking wider does not fail, it
quietly returns less - so MAXIMUM_WINDOW_DAYS here is not a documented limit but the point past
which data would be lost without any sign of it. The truncation is relative to the window rather
than to today, which is what makes the backward walk possible at all.
IND Money publishes no open interest on any interval, so that column is left null.
The endpoint will answer about five instruments in one call. That is not used: the downloader visits one series at a time, and taking five would need a second path through the base class for one broker. The cost is real and worth recording - IND Money caps data requests at one hundred thousand a day, so asking one at a time makes this backfill five times longer than it needs to be. If that becomes the binding constraint, batching is the first thing to do.
Classes:
| Name | Description |
|---|---|
IndMoneyCandles |
Downloads IND Money's historical candles. |
IndMoneyCandles
¶
Bases: BrokerCandles
Downloads IND Money's historical candles.
IND Money candle downloader.
apiis an authenticatedINDMoneyAPI. One is constructed when not supplied, which establishes a session if the stored one has expired.
Example programs
"""Fetches one window of RELIANCE five minute bars from IND Money, where the answer nests each instrument's bars under its scrip code.
IND Money's chart endpoint takes the interval in its path and the window as epoch milliseconds at midnight India time, the end being midnight after the last day so the whole day is inside. Its answer maps each scrip code asked about to that instrument's bars, and `fetch_candles` hands `parse_response` only the part for the code it asked about. A scrip with nothing in the window may be present holding null or missing altogether, and both are read as an empty window. IND Money serves no open interest, so that column is always None. Intraday bar times are stored as the UTC instants the epochs decode to, and the program prints them in India time so they can be read against the 09:15 open.
An `IndMoneyCandles` constructor opens a PostgreSQL connection and logs in to IND Money 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 `INDMoneyAPI`. The stand-in records each request and answers with bars shaped like IND Money's recorded answer, wrapped in the `status_code` and `data` envelope the real API class returns; for any scrip other than RELIANCE it answers with the code holding null.
Run it from the project root:
python examples/stock_brokers/instruments/historical/indmoney/IndMoneyCandles/example_1_bars_nested_under_scrip_code.py
"""
import datetime
from stock_brokers.instruments.historical.indmoney import (
IndMoneyCandles,
)
class RecordedIndMoneyAPI:
"""A stand-in for `INDMoneyAPI` that answers with two recorded bars for RELIANCE and null for any other scrip.
Attributes:
requests (list): The (url, parameters) pairs it was asked for.
"""
def __init__(self):
"""Builds the stand-in with no requests recorded.
Returns:
None: This method returns nothing.
"""
self.requests = []
def get(self, url, params=None):
"""Records a GET request and answers it.
Args:
url (str): The URL the downloader asked for.
params (dict): The query parameters it sent.
Returns:
dict: The response envelope, with the map of scrip code to bars under `data`.
"""
self.requests.append((url, params))
scrip_code = params['scrip-codes']
if scrip_code != 'NSE_2885':
return {
'status_code': 200,
'data': {
scrip_code: None,
},
}
return {
'status_code': 200,
'data': {
'NSE_2885': {
'candles': [
{
'ts': 1788839100,
'o': 1306.8,
'h': 1306.8,
'l': 1300.0,
'c': 1303.1,
'v': 174555,
},
{
'ts': 1788839400,
'o': 1303.1,
'h': 1304.0,
'l': 1301.0,
'c': 1302.0,
'v': 98434,
},
],
},
},
}
class BarsNestedUnderScripCodeExample:
"""Fetches and parses a window for a scrip with bars and one without.
Attributes:
api (RecordedIndMoneyAPI): The stand-in IND Money API.
candles (IndMoneyCandles): 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 = RecordedIndMoneyAPI()
self.candles = object.__new__(IndMoneyCandles)
self.candles._api = self.api
def fetch_and_print(self, scrip_code):
"""Fetches one day of five minute bars for a scrip and prints the request and the bars.
Args:
scrip_code (str): The scrip code, such as `NSE_2885`.
Returns:
None: This method returns nothing.
"""
day = datetime.date(2026, 9, 8)
payload = self.candles.fetch_candles(scrip_code, '5minute', day, day)
url, parameters = self.api.requests[-1]
print(f'Requested {url}')
print(f' parameters: {parameters}')
bars = self.candles.parse_response(payload, '5minute')
print(f' bars: {len(bars)}')
for bar in bars:
bar_time, open_price, high, low, close, volume, open_interest = bar
india_time = bar_time.astimezone(datetime.timezone(datetime.timedelta(hours=5, minutes=30)))
print(f' {india_time.isoformat()} open {open_price} high {high} low {low} close {close} volume {volume} oi {open_interest}')
def run(self):
"""Fetches RELIANCE, then a scrip with nothing in the window.
Returns:
None: This method returns nothing.
"""
self.fetch_and_print('NSE_2885')
self.fetch_and_print('NFO_52175')
if __name__ == '__main__':
BarsNestedUnderScripCodeExample().run()
It prints:
Requested https://api.indstocks.com/market/historical/5minute
parameters: {'scrip-codes': 'NSE_2885', 'start_time': '1788805800000', 'end_time': '1788892200000'}
bars: 2
2026-09-08T09:15:00+05:30 open 1306.8 high 1306.8 low 1300.0 close 1303.1 volume 174555 oi None
2026-09-08T09:20:00+05:30 open 1303.1 high 1304.0 low 1301.0 close 1302.0 volume 98434 oi None
Requested https://api.indstocks.com/market/historical/5minute
parameters: {'scrip-codes': 'NFO_52175', 'start_time': '1788805800000', 'end_time': '1788892200000'}
bars: 0
"""Builds IND Money scrip codes from its instrument master, reads what a scrip code says about its instrument, and classifies IND Money's refusals.
The chart endpoint wants a scrip code whose prefix comes from the segment rather than the exchange: a derivative is recorded under NSE in the instrument master but must be asked about as `NFO`, and asking with `NSE` quietly answers nothing. BSE derivatives are left out, because the endpoint refuses every prefix tried for them. IND Money's expiry, written like `09/29/2026 14:00`, is turned into a date for the queue's priority. The program gives the downloader a stand-in database connection whose cursor answers with four rows, one of them a BSE derivative that is dropped.
`identifier_parts` splits a scrip code into its prefix and security id, and `series_context`, a classmethod that needs no downloader, reads the exchange and segments the prefix names.
Last, the program fetches through a stand-in for `INDMoneyAPI` that raises the `INDMoneyAPIException` each of three refusals produces: an unknown scrip code retires the series, a rate limit is a throttle and a 401 is a dead session. 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/indmoney/IndMoneyCandles/example_2_scrip_codes_and_refusals.py
"""
import datetime
from stock_brokers.api.indmoney import (
INDMoneyAPIException,
)
from stock_brokers.instruments.historical.base import (
CandleError,
)
from stock_brokers.instruments.historical.indmoney import (
IndMoneyCandles,
)
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 type, 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 four rows of IND Money'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,
),
(
'BSE',
'E',
'500325',
'EQUITY',
None,
),
(
'NSE',
'D',
'52175',
'FUTSTK',
'09/29/2026 14:00',
),
(
'BSE',
'D',
'1144412',
'FUTSTK',
'09/24/2026 14:00',
),
]
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 RefusingIndMoneyAPI:
"""A stand-in for `INDMoneyAPI` that refuses with a chosen error.
Attributes:
code (int): The HTTP status the refusal carries.
message (str): The message the refusal carries.
"""
def __init__(self, code, message):
"""Holds the refusal to raise.
Args:
code (int): The HTTP status.
message (str): The message.
Returns:
None: This method returns nothing.
"""
self.code = code
self.message = message
def get(self, url, params=None):
"""Refuses a GET request.
Args:
url (str): The URL the downloader asked for.
params (dict): The query parameters it sent.
Returns:
dict: Never returns.
Raises:
INDMoneyAPIException: Always, carrying the chosen status and message.
"""
raise INDMoneyAPIException(code=self.code, message=self.message)
class ScripCodesAndRefusalsExample:
"""Lists scrip codes, reads them, and classifies three refusals.
Attributes:
candles (IndMoneyCandles): 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__(IndMoneyCandles)
self.candles._connection = InstrumentMasterConnection()
def show_scrip_codes(self):
"""Lists the instruments and reads each scrip code.
Returns:
None: This method returns nothing.
"""
instruments = self.candles.instruments()
print(f'Instruments to download: {len(instruments)} of 4 rows')
for scrip_code, instrument_type, expiry in instruments:
prefix, security_id = self.candles.identifier_parts(scrip_code)
context = IndMoneyCandles.series_context(scrip_code)
print(f'{scrip_code}: prefix {prefix}, security id {security_id}, {instrument_type}, expiry {expiry}, exchange {context.exchange}, {len(context.segments)} segments to search')
def show_refusals(self):
"""Fetches through three refusing stand-ins and prints what each became.
Returns:
None: This method returns nothing.
"""
refusals = [
(
400,
'Invalid scrip codes: NSE_99999999',
),
(
429,
'Rate limit exceeded',
),
(
401,
'Unauthorized',
),
]
for code, message in refusals:
self.candles._api = RefusingIndMoneyAPI(code, message)
try:
self.candles.fetch_candles(
'NSE_99999999',
'day',
datetime.date(2025, 9, 1),
datetime.date(2026, 9, 1),
)
outcome = 'answered'
except CandleError as error:
outcome = type(error).__name__
print(f'{code} "{message}": {outcome}')
def run(self):
"""Runs both parts in turn.
Returns:
None: This method returns nothing.
"""
self.show_scrip_codes()
self.show_refusals()
if __name__ == '__main__':
ScripCodesAndRefusalsExample().run()
It prints:
Instruments to download: 3 of 4 rows
NSE_2885: prefix NSE, security id 2885, EQUITY, expiry None, exchange nse, 10 segments to search
BSE_500325: prefix BSE, security id 500325, EQUITY, expiry None, exchange bse, 10 segments to search
NFO_52175: prefix NFO, security id 52175, FUTSTK, expiry 2026-09-29, exchange nse, 16 segments to search
400 "Invalid scrip codes: NSE_99999999": CandleInstrumentUnknown
429 "Rate limit exceeded": CandleThrottled
401 "Unauthorized": CandleAuthenticationError
Methods:
| Name | Description |
|---|---|
instruments |
Every instrument IND Money has published, named the way the chart endpoint needs. |
identifier_parts |
Split a stored IND Money scrip code into its prefix and security id. |
series_context |
Read the exchange and segment out of an IND Money scrip code. |
fetch_candles |
Fetch one window of IND Money candles. |
parse_response |
Turn IND Money's candle list 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/indmoney.py
instruments
¶
Every instrument IND Money has published, named the way the chart endpoint needs.
The scrip code's prefix comes from the segment rather than from the exchange column, because a derivative's exchange is recorded as NSE while the endpoint wants NFO - and asking with NSE is accepted and answers nothing at all, so getting this wrong would show up as an instrument with no history rather than as an error.
BSE derivatives are left out: the endpoint refuses every prefix tried for them.
The expiry is converted from IND Money's 09/29/2026 14:00 to a date here, because
priority_for reads dates and would otherwise treat every expired contract as live and
queue it ahead of the cash bars that are actually wanted.
Example usage
Source code in stock_brokers/instruments/historical/indmoney.py
_scrip_prefix
staticmethod
¶
The scrip code prefix for one instrument, or None when IND Money will not chart it.
exchangeis theexchcolumn, NSE or BSE.segmentis thesegmentcolumn:Dfor derivatives,Efor cash, and the index's own name for an index.
Source code in stock_brokers/instruments/historical/indmoney.py
_expiry_date
staticmethod
¶
Turn IND Money's 09/29/2026 14:00 expiry into a date, or None when there is none.
expiryis theexpiry_datecolumn.
Source code in stock_brokers/instruments/historical/indmoney.py
identifier_parts
staticmethod
¶
Split a stored IND Money scrip code into its prefix and security id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
For example "NSE_2885". |
required |
Returns:
| Type | Description |
|---|---|
tuple
|
The prefix and the security id. |
Example usage
prefix, security_id = self.candles.identifier_parts(scrip_code)
Source code in stock_brokers/instruments/historical/indmoney.py
series_context
classmethod
¶
Read the exchange and segment out of an IND Money scrip code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identifier
|
str
|
For example "NSE_2885". |
required |
Returns:
| Type | Description |
|---|---|
SeriesContext
|
The security id, with the exchange and segments its prefix names. |
Example usage
Source code in stock_brokers/instruments/historical/indmoney.py
fetch_candles
¶
Fetch one window of IND Money candles.
The window goes out as epoch milliseconds. The response nests its bars under the scrip
code they were asked about, so the code is looked up here and parse_response receives
just that instrument's part.
tokenis the scrip code, such asNSE_2885.intervalis the stored interval name.start_dateandend_datebound the window, inclusive.
Example usage
payload = self.candles.fetch_candles(scrip_code, '5minute', day, day)
self.candles.fetch_candles(
'NSE_99999999',
'day',
datetime.date(2025, 9, 1),
datetime.date(2026, 9, 1),
)
Source code in stock_brokers/instruments/historical/indmoney.py
_epoch_milliseconds
staticmethod
¶
Midnight India time on a date, as epoch milliseconds.
dayis the date to convert.
Source code in stock_brokers/instruments/historical/indmoney.py
_classify
staticmethod
¶
Decide what an IND Money refusal means for the series being downloaded.
An unknown scrip code is an HTTP 400 naming "Invalid scrip codes", which retires the series. A throttle is a 429 carrying "Rate limit exceeded".
exceptionis what the API call raised.
Source code in stock_brokers/instruments/historical/indmoney.py
parse_response
¶
Turn IND Money's candle list into bars.
Each bar is an object keyed by initials, ts holding epoch seconds. There is no open
interest on any interval, so that column is left null rather than invented.
payloadis this instrument's part of the response.intervalis the stored interval name.
Example usage
bars = self.candles.parse_response(payload, '5minute')
Source code in stock_brokers/instruments/historical/indmoney.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