Official Python client for Equal Solution realtime and historical market data
Project description
eqldata
Official Python client for Equal Solution realtime and historical market data.
eqldata is designed for traders, developers, analysts, charting tools, and backtesting workflows that need simple access to Equal Solution APIs from Python.
What this package supports
- Login and API token generation
- Instrument list download
- Realtime websocket subscription
- Single-day EOD download
- Range-based EOD download
- After-market / current-day 1-minute data download
- Reading EOD range ZIP output in plain Python
- Reading EOD range ZIP output with pandas
Installation
pip install eqldata
Upgrade to the latest version:
pip install --upgrade eqldata
Check installed version:
import eqldata
print(eqldata.__version__)
Important API session rule
Only the latest successful login token remains valid.
If the same user logs in again from another PC, server, or script, the previous API token becomes invalid automatically.
This protects the business rule:
A client may login from any PC, but only one active API session is allowed at a time.
Use one fresh token per running client/script.
Instrument naming
Always use exchange/segment-prefixed instruments.
Examples:
"NSEEQ:ABB"
"NSEEQ:RELIANCE"
"NSEIDX:NIFTY_50"
"NSEIDX:INDIA_VIX"
"MCXFUT:CRUDEOILM_I"
Do not pass raw names like "ABB" unless a specific helper script adds the prefix for you.
Function index
from eqldata import (
DataClient,
EqualDataError,
generate_auth_token,
get_instrument_list,
get_EOD,
get_EOD_range,
get_1MARKET_DATA,
read_EOD_range_metadata,
read_EOD_range_csv,
read_EOD_range_csv_rows,
)
| Function | Purpose |
|---|---|
generate_auth_token() |
Login and get API token |
get_instrument_list() |
Get instruments available for the account |
DataClient |
Realtime websocket client |
get_EOD() |
Single-day EOD download |
get_EOD_range() |
Date-range EOD ZIP download |
get_1MARKET_DATA() |
1-minute data for one date |
read_EOD_range_metadata() |
Read metadata from EOD range ZIP |
read_EOD_range_csv() |
Read CSV rows from EOD range ZIP |
read_EOD_range_csv_rows() |
Same as read_EOD_range_csv() |
1. Login and generate token
from getpass import getpass
from eqldata import generate_auth_token
email = input("Email: ").strip()
password = getpass("Password: ")
auth_token = generate_auth_token(email, password)
if not auth_token:
raise SystemExit("Login failed")
print("Login successful")
print("Token length:", len(auth_token))
Production style with error raising:
from getpass import getpass
from eqldata import generate_auth_token, EqualDataError
try:
auth_token = generate_auth_token(
input("Email: ").strip(),
getpass("Password: "),
raise_on_error=True,
)
print("Login successful")
except EqualDataError as e:
print("API login failed:", e)
2. Get instrument list
from getpass import getpass
from eqldata import generate_auth_token, get_instrument_list
email = input("Email: ").strip()
password = getpass("Password: ")
auth_token = generate_auth_token(email, password)
instruments = get_instrument_list(auth_token)
print(type(instruments))
print(instruments)
Print only NSE equity instruments:
from getpass import getpass
from eqldata import generate_auth_token, get_instrument_list
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
data = get_instrument_list(auth_token)
nseeq = data.get("NSEEQ", []) if isinstance(data, dict) else []
print("NSEEQ count:", len(nseeq))
print("First 20 instruments:")
for item in nseeq[:20]:
print(item)
3. Realtime websocket data
import json
from getpass import getpass
from eqldata import generate_auth_token, DataClient
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
topics = [
"NSEIDX:NIFTY_50",
"NSEIDX:INDIA_VIX",
]
client = DataClient(auth_token, topics)
try:
while True:
message = client.listen()
print("RAW:", message)
try:
data = json.loads(message)
print("JSON:", data)
except Exception:
pass
except KeyboardInterrupt:
print("Stopping...")
client.stop_listening()
client.disconnect()
4. Single-day EOD download
Use get_EOD() for one specific date and one or more instruments.
from getpass import getpass
from eqldata import generate_auth_token, get_EOD
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
instruments = [
"NSEEQ:ABB",
"NSEIDX:NIFTY_50",
"NSEIDX:INDIA_VIX",
]
for_date = "2026-06-22"
result = get_EOD(auth_token, instruments, for_date)
print(type(result))
print(result)
Save single-day EOD rows to CSV:
import csv
from getpass import getpass
from eqldata import generate_auth_token, get_EOD
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
instruments = ["NSEEQ:ABB", "NSEIDX:NIFTY_50"]
for_date = "2026-06-22"
result = get_EOD(auth_token, instruments, for_date)
with open("single_day_eod.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["INSTRUMENT", "DTYYYYMMDD", "OPEN", "HIGH", "LOW", "CLOSE", "VOL", "OPENINT"])
for group in result:
for row in group:
writer.writerow(row)
print("Saved: single_day_eod.csv")
5. EOD range download
Use get_EOD_range() for multiple dates and multiple instruments.
The API returns a ZIP file containing:
EOD_RANGE.csv
EOD_RANGE_META.json
Basic example:
from getpass import getpass
from eqldata import generate_auth_token, get_EOD_range
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
zip_path = get_EOD_range(
auth_token=auth_token,
instruments=[
"NSEEQ:ABB",
"NSEIDX:NIFTY_50",
"NSEIDX:INDIA_VIX",
],
from_date="2026-06-10",
to_date="2026-06-22",
output_path="EOD_RANGE.zip",
)
print("Downloaded:", zip_path)
EOD range public limits
| Limit | Value |
|---|---|
| Maximum instruments per request | 500 |
| Maximum calendar days per request | 366 |
| Output format | ZIP |
| CSV file inside ZIP | EOD_RANGE.csv |
| Metadata file inside ZIP | EOD_RANGE_META.json |
If your account has 5, 10, or 20 years of history access, download in yearly chunks.
6. Read EOD range metadata
from getpass import getpass
from eqldata import generate_auth_token, get_EOD_range, read_EOD_range_metadata
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
zip_path = get_EOD_range(
auth_token,
["NSEEQ:ABB", "NSEIDX:NIFTY_50"],
"2026-06-10",
"2026-06-22",
output_path="EOD_RANGE.zip",
)
meta = read_EOD_range_metadata(zip_path)
print(meta)
Example metadata keys may include:
from_date
to_date
symbols_requested
files_scanned
rows_written
format
compression
7. Read EOD range CSV rows
from getpass import getpass
from eqldata import (
generate_auth_token,
get_EOD_range,
read_EOD_range_csv,
)
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
zip_path = get_EOD_range(
auth_token,
["NSEEQ:ABB", "NSEIDX:NIFTY_50"],
"2026-06-10",
"2026-06-22",
output_path="EOD_RANGE.zip",
)
rows = read_EOD_range_csv(zip_path)
print("Rows:", len(rows))
print("First row:", rows[0] if rows else None)
Expected CSV columns:
INSTRUMENT,DTYYYYMMDD,OPEN,HIGH,LOW,CLOSE,VOL,OPENINT
8. Read EOD range ZIP with pandas
from getpass import getpass
import zipfile
import pandas as pd
from eqldata import generate_auth_token, get_EOD_range
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
zip_path = get_EOD_range(
auth_token=auth_token,
instruments=["NSEEQ:ABB", "NSEIDX:NIFTY_50"],
from_date="2026-06-10",
to_date="2026-06-22",
output_path="EOD_RANGE.zip",
)
with zipfile.ZipFile(zip_path, "r") as z:
with z.open("EOD_RANGE.csv") as f:
df = pd.read_csv(f)
df["DTYYYYMMDD"] = pd.to_datetime(df["DTYYYYMMDD"].astype(str), format="%Y%m%d")
print(df.head())
print(df.tail())
print(df.shape)
Install pandas if needed:
pip install pandas
9. Download multiple years in yearly chunks
Because each EOD range request is capped to 366 calendar days, large history should be downloaded year by year.
from getpass import getpass
from pathlib import Path
from eqldata import generate_auth_token, get_EOD_range
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
instruments = [
"NSEEQ:ABB",
"NSEEQ:RELIANCE",
"NSEIDX:NIFTY_50",
]
ranges = [
("2022-01-01", "2022-12-31"),
("2023-01-01", "2023-12-31"),
("2024-01-01", "2024-12-31"),
("2025-01-01", "2025-12-31"),
]
out_dir = Path("eod_yearly_downloads")
out_dir.mkdir(exist_ok=True)
for from_date, to_date in ranges:
out_file = out_dir / f"EOD_{from_date}_to_{to_date}.zip"
zip_path = get_EOD_range(
auth_token=auth_token,
instruments=instruments,
from_date=from_date,
to_date=to_date,
output_path=out_file,
)
print("Downloaded:", zip_path)
10. 1-minute market data
Use get_1MARKET_DATA() for 1-minute data for one date.
from getpass import getpass
from eqldata import generate_auth_token, get_1MARKET_DATA
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
instruments = [
"NSEIDX:NIFTY_50",
"NSEIDX:INDIA_VIX",
]
for_date = "2026-06-22"
result = get_1MARKET_DATA(auth_token, instruments, for_date)
print(type(result))
print(result)
Save 1-minute data to CSV:
import csv
from getpass import getpass
from eqldata import generate_auth_token, get_1MARKET_DATA
auth_token = generate_auth_token(input("Email: ").strip(), getpass("Password: "))
result = get_1MARKET_DATA(auth_token, ["NSEIDX:NIFTY_50"], "2026-06-22")
with open("nifty_1min.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerow(["INSTRUMENT", "TIMESTAMP", "OPEN", "HIGH", "LOW", "CLOSE", "VOL", "OPENINT"])
for group in result:
for row in group:
writer.writerow(row)
print("Saved: nifty_1min.csv")
11. Error handling
Use raise_on_error=True when you want exceptions instead of returned error text.
from getpass import getpass
from eqldata import generate_auth_token, get_EOD_range, EqualDataError
try:
auth_token = generate_auth_token(
input("Email: ").strip(),
getpass("Password: "),
raise_on_error=True,
)
zip_path = get_EOD_range(
auth_token=auth_token,
instruments=["NSEEQ:ABB"],
from_date="2026-06-10",
to_date="2026-06-22",
output_path="EOD_RANGE.zip",
raise_on_error=True,
)
print("Downloaded:", zip_path)
except EqualDataError as e:
print("Equal Solution API error:", e)
except Exception as e:
print("Unexpected error:", e)
Common API errors:
| Error | Meaning |
|---|---|
INVALID_TOKEN |
Login again and use the latest token |
MAX_DAYS_EXCEEDED |
Keep EOD range within 366 calendar days |
MAX_SYMBOLS_EXCEEDED |
Keep instruments within 500 per call |
HISTORY_RANGE_NOT_ALLOWED |
Your account is not entitled to that old history |
RATE_LIMIT_EXCEEDED |
Wait and retry later |
EOD_RANGE_BUSY |
Another range job is already running for the user or server |
Examples folder
This package includes runnable sample scripts in the examples/ folder:
examples/
01_login.py
02_get_instrument_list.py
03_get_eod_single_day.py
04_get_eod_range_download_zip.py
05_get_eod_range_pandas.py
06_get_1market_data_single_day.py
07_check_1min_range_save_csv.py
08_realtime_websocket_subscribe.py
09_error_handling.py
10_eod_range_yearly_chunks.py
Run any example like this:
python examples/04_get_eod_range_download_zip.py
Support
For subscription, entitlement, or API access issues, contact Equal Solution support.
Website: https://equalsolution.com
Email: info@equalsolution.com
License
MIT License.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file eqldata-1.7.tar.gz.
File metadata
- Download URL: eqldata-1.7.tar.gz
- Upload date:
- Size: 16.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e6809ecdd64e4a746ceb914c2b9070955402e0d7d77aa9e453ca3cf50f74e73c
|
|
| MD5 |
2aa4359de0bc396bfa3743713be5767d
|
|
| BLAKE2b-256 |
6163d49b9d8b83eb8782d18feb9e08037e9a2531f6fef7849e5443c48ea6698b
|
File details
Details for the file eqldata-1.7-py3-none-any.whl.
File metadata
- Download URL: eqldata-1.7-py3-none-any.whl
- Upload date:
- Size: 9.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d9f4ab37e86a361585b77b1b12b8cdf16bd63dd93b8c23f5e55db7df487174e6
|
|
| MD5 |
a07f53ddcc705323a0941d41aaddd53e
|
|
| BLAKE2b-256 |
2a406e0a659f8967c85a56410f30488671542afcd7af5d7b1b7896f8dac89024
|