Python True Data Integration (2025)
import os
import logging
import time
from dotenv import load_dotenv
from truedata import TD_live # official TrueData client
load_dotenv()
# ---- helpers ----
def _to_kv_string(obj):
try:
items = {k: v for k, v in vars(obj).items() if not k.startswith("_")}
except Exception:
return repr(obj)
# stringify safely
def s(v):
try:
return str(v)
except Exception:
return repr(v)
return " ".join(f"{k}={s(items[k])}" for k in sorted(items))
def _first_pair(values):
# Accept shapes like [(price, qty)] or [price, qty]
try:
if isinstance(values, list) and values:
first = values[0]
if isinstance(first, (list, tuple)) and len(first) >= 2:
return first[0], first[1]
if isinstance(values, list) and len(values) >= 2:
return values[0], values[1]
except Exception:
pass
return None, None
USER = os.getenv("TD_USER", "tdwsp774")
PASSWORD = os.getenv("TD_PASSWORD", "adithya@774")
SYMBOLS = [s.strip() for s in os.getenv("TD_SYMBOLS", "NIFTY-I,BANKNIFTY-I,SENSEX-I").split(",") if s.strip()]
PORT = int(os.getenv("TD_PORT", "8084"))
# RUN_SECS = int(os.getenv("TD_RUN_SECS", "300")) # 5 minutes
USE_REPLAY = os.getenv("TD_USE_REPLAY", "0") == "1"
HOST = os.getenv("TD_URL", "push.truedata.in")
if USE_REPLAY:
HOST = "replay.truedata.in" # optional replay feed
if not USER or not PASSWORD:
raise SystemExit("Missing TD_USER/TD_PASSWORD in .env")
def main():
# DEBUG shows heartbeats, market status, symbol add/remove, etc.
log_level = logging.DEBUG
print(f"[TrueData] host={HOST} port={PORT} user={USER}")
print(f"[TrueData] symbols={SYMBOLS}")
# Build official client
td = TD_live(
USER,
PASSWORD,
live_port=PORT,
url=HOST,
log_level=log_level,
log_format="%(message)s",
)
# ---- Callbacks (official decorators) ----
@td.trade_callback
def on_trade(tick):
# Safe access across versions
def pick(obj, *names):
for name in names:
if hasattr(obj, name):
return getattr(obj, name)
return None
symbol = pick(tick, "symbol", "sym")
ltp = pick(tick, "ltp", "last_price", "price")
ltq = pick(tick, "ltq", "last_qty", "qty", "quantity") # per-tick volume
ts = pick(tick, "timestamp", "ts", "time")
# Print only symbol, LTP, and LTQ for every tick
print(f"[TICK] {symbol} | LTP: {ltp} | LTQ: {ltq} | Time: {ts}")
@td.bidask_callback
def on_bidask(ba):
# Be tolerant to field name differences across library versions
def pick(obj, *names):
for name in names:
if hasattr(obj, name):
return getattr(obj, name)
return None
symbol = pick(ba, "symbol", "sym")
bid_px = pick(ba, "best_bid_price", "bid_price", "best_bid", "bid")
bid_qty = pick(ba, "best_bid_qty", "bid_qty", "bid_quantity", "bidqty")
ask_px = pick(ba, "best_ask_price", "ask_price", "best_ask", "ask")
ask_qty = pick(ba, "best_ask_qty", "ask_qty", "ask_quantity", "askqty")
ts = pick(ba, "timestamp", "ts", "time")
# If library provides lists of (price, qty), normalize
if isinstance(bid_px, list) and (bid_qty is None):
px, qty = _first_pair(bid_px)
if px is not None:
bid_px, bid_qty = px, qty
if isinstance(ask_px, list) and (ask_qty is None):
px, qty = _first_pair(ask_px)
if px is not None:
ask_px, ask_qty = px, qty
print(f"[BIDASK] {symbol} bid={bid_px}@{bid_qty} ask={ask_px}@{ask_qty} ts={ts}")
# Print all available fields for debugging/inspection
print("[BIDASK ALL]", _to_kv_string(ba))
# Optional: minute bars if enabled on your account
# @td.one_min_bar_callback
# def on_1m(bar):
# print(f"[1M] {bar.symbol} O={bar.open} H={bar.high} L={bar.low} C={bar.close} V={bar.volume} ts={bar.timestamp}")
# Start stream
td.start_live_data(SYMBOLS)
# Run indefinitely; callbacks + library logs will print continuously
try:
while True:
time.sleep(0.25)
# Example: access latest snapshot on demand
# for s in SYMBOLS:
# snap = td.live_data.get(s)
# if snap:
# _ = (snap.ltp, snap.best_bid_price, snap.best_ask_price)
except KeyboardInterrupt:
print("\nStopping on user request (Ctrl+C)...")
finally:
print("[TrueData] stopping & disconnecting…")
with contextually_silent():
td.stop_live_data(SYMBOLS) # remove if your TD_live version doesn’t support this
td.disconnect()
# small utility to silence library exceptions during shutdown
from contextlib import contextmanager
@contextmanager
def contextually_silent():
try:
yield
except Exception:
pass
if __name__ == "__main__":
main()
Comments
Post a Comment