mcpField
D
dhan-tradehull

dhan-tradehull

github.com/imran-tradehullDevelopment163 installsUpdated 15w ago
Repository

About dhan-tradehull

TradeHull is a Python wrapper for the Dhan API, and this skill guides users building trading strategies and automated programs with it. It covers market data, options, orders, and portfolio operations, with guidance on authentication, risk controls, and deployment.

Key features

  1. 01Three authentication modes
  2. 02Market and historical data queries
  3. 03Option chains and strike selection
  4. 04Create, modify, and cancel orders
  5. 05Query holdings, balance, and P&L
  6. 06Algo strategy and deployment guidance

Use cases

  1. 01Retrieve market data and calculate trading signals
  2. 02Build intraday strategies with stop-loss and targets
  3. 03Set GTT orders active across sessions
  4. 04Review positions and configure P&L exits

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/imran-tradehull/tradehull-dhan-live-algo-skills --skill dhan-tradehull

After installing, type/dhan-tradehullin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with SKILL.md.

examples/
intraday_options_algo.py
nifty50_scanner_algo.py
positional_spread_algo.py
references/
algo-dev-workflow.md
algo-intraday-options.md
algo-positional-spread.md
algo-scanner.md
answer-patterns.md
auth.md
coding-style.md
common-patterns.md
deployment.md
dhanhq-raw.md
error-log.md
flask-ui.md
instruments.md
market-data.md
metadata.md
options.md
orders.md
portfolio.md
ui-ux.md
utilities.md

Details

From the project README

Dhan-Tradehull Skill (v3.3.2)

TradeHull's Python wrapper around the Dhan API. Always import and initialize first.

📐 Always follow TradeHull coding style when generating code. Read references/coding-style.md before writing any strategy or algo code. Key rules: vertical alignment, bc1/bc2/sc1/sc2 conditions, block comments, flat structure.

Reference Files — Load on Demand

TaskRead
Login, authentication, token setupreferences/auth.md
LTP, OHLC, quote, historical datareferences/market-data.md
Option chain, strike selection, expired datareferences/options.md
Order placement, modify, cancel, super/forever ordersreferences/orders.md
Holdings, positions, orderbook, balance, P&Lreferences/portfolio.md
Lot size, margin, Telegram, P&L exit, kill switchreferences/utilities.md
Errors, SEBI regulations, known issuesreferences/error-log.md
Building an algo end-to-end (auth→data→signal→order→exit)references/algo-dev-workflow.md
Scanner: indicators/crossovers over a watchlist (TA-Lib)references/algo-scanner.md
Browser dashboard / UI on top of an algo (Flask)references/flask-ui.md
Designing the dashboard: layout, colour, progress, live-order safetyreferences/ui-ux.md
Running an algo on a server: detach, restart, systemd, logs, portsreferences/deployment.md

1. Installation

pip install --pre dhanhq
pip install Dhan-Tradehull

2. Authentication (3 modes)

from Dhan_Tradehull import Tradehull

# Mode 1: Access Token
# ⚠️  TOKEN EXPIRES DAILY — must regenerate every morning before market open
# Get new token from: Dhan web → My Profile → API Access
tsl = Tradehull(client_code, token_id, mode="access_token")

# Mode 2: API Key (browser-based login flow)
tsl = Tradehull(client_code, mode="api_key", api_key=api_key, api_secret=api_secret)

# Mode 3: PIN + TOTP
# ✅ PIN IS VALID FOR LIFETIME — no daily regeneration needed
# Best for fully automated algos running without manual intervention
tsl = Tradehull(ClientCode=client_code, mode="pin_totp", pin=pin, totp_secret=totp_secret)

Auth mode comparison:

ModeToken ValidityBest For
access_token⚠️ Daily — regenerate every morningManual/semi-auto trading
api_keyBrowser flow each timeOne-off scripts
pin_totp✅ Lifetime (PIN never expires)Fully automated algos, scheduled jobs

3. Market Data

# LTP (Last Traded Price)
# ✅ Up to 500 symbols in one call, returns in under 1 second
# Returns dict → {symbol (str): ltp (float)}
data = tsl.get_ltp_data(names=['NIFTY', 'BANKNIFTY', 'CRUDEOIL'])
nifty_ltp = data['NIFTY']        # 24523.5
crude_ltp = data['CRUDEOIL']     # 6570.0

# Full Quote
data = tsl.get_quote_data(names=['RELIANCE'])

# OHLC
data = tsl.get_ohlc_data(names=['NIFTY', 'CRUDEOIL'])

# Historical Data (timeframe: '1','5','15','25','60','DAY')
df = tsl.get_historical_data(tradingsymbol='NIFTY', exchange='INDEX', timeframe='5')
df = tsl.get_historical_data(tradingsymbol='ACC', exchange='NSE', timeframe='1')

# Long-Term Historical (custom date range)
df = tsl.get_long_term_historical_data(
    tradingsymbol='RELIANCE', exchange='NSE', timeframe='5',
    from_date='2024-01-01', to_date='2025-01-01'
)

# Sector Data
df = tsl.get_historical_data(tradingsymbol="NIFTY 100", exchange="NSE",
                              timeframe="DAY", sector="YES")

Exchange values: 'NSE', 'BSE', 'NFO', 'BFO', 'MCX', 'INDEX'


4. Option Strike Selection

# ATM
CE_sym, PE_sym, strike = tsl.ATM_Strike_Selection(Underlying='NIFTY', Expiry=0)

# OTM (OTM_count = steps away from ATM)
CE_sym, PE_sym, CE_strike, PE_strike = tsl.OTM_Strike_Selection(
    Underlying='NIFTY', Expiry=0, OTM_count=5)

# ITM
CE_sym, PE_sym, CE_strike, PE_strike = tsl.ITM_Strike_Selection(
    Underlying='NIFTY', Expiry=0, ITM_count=1)

Expiry: 0 = current week/month, 1 = next, 2 = far


5. Option Greeks

⚠️ Deprecated in practice — Do NOT use get_option_greek() in new code. Greeks (Delta, Theta, Gamma, Vega, IV) are available directly from get_option_chain() which returns a full DataFrame with greeks per strike. Always prefer get_option_chain() for greeks data.


6. Option Chain

# ✅ Returns TWO values — atm (int) + option_chain (DataFrame)
atm, chain = tsl.get_option_chain(Underlying="NIFTY", exchange="INDEX", expiry=0, num_strikes=10)

# atm = current ATM strike as int e.g. 24000
# chain = DataFrame, 21 rows × 27 columns (for num_strikes=10)

# Columns: CE OI, CE Chg in OI, CE Volume, CE IV, CE LTP,
#          CE Bid Qty, CE Bid, CE Ask, CE Ask Qty,
#          CE Delta, CE Theta, CE Gamma, CE Vega,
#          Strike Price,
#          PE Bid Qty, PE Bid, PE Ask, PE Ask Qty,
#          PE LTP, PE IV, PE Volume, PE Chg in OI, PE OI,
#          PE Delta, PE Theta, PE Gamma, PE Vega

# ATM row
atm_row      = chain[chain['Strike Price'] == atm]
ce_ltp       = atm_row['CE LTP'].values[0]
pe_ltp       = atm_row['PE LTP'].values[0]

# OI analysis
ce_resistance = chain.loc[chain['CE OI'].idxmax(), 'Strike Price']  # highest CE OI = resistance
pe_support    = chain.loc[chain['PE OI'].idxmax(), 'Strike Price']  # highest PE OI = support
pcr           = chain['PE OI'].sum() / chain['CE OI'].sum()         # Put-Call Ratio

# Exchange values
# INDEX  → NIFTY, BANKNIFTY, FINNIFTY
# MCX    → CRUDEOIL, GOLD, SILVER
# NFO    → stock options

✅ Greeks included — Delta, Theta, Gamma, Vega, IV per strike. No need for get_option_greek() — it's deprecated. Use this instead.


7. Order Placement

🚨 SEBI Regulation (effective 1st April 2026) MARKET orders are no longer allowed for F&O. All orders must be LIMIT orders.

Rules for limit price:

  • BUY → limit price must be greater than LTP (so it gets filled immediately)
  • SELL → limit price must be less than LTP (so it gets filled immediately)

Pattern for instant fill using limit order:

ltp = tsl.get_ltp_data(names=['NIFTY 19 DEC 24400 CALL'])['NIFTY 19 DEC 24400 CALL']
buy_price  = round(ltp * 1.02, 1)   # 2% above LTP for BUY
sell_price = round(ltp * 0.98, 1)   # 2% below LTP for SELL
# BUY limit order (price > LTP for instant fill)
order_id = tsl.order_placement(
    tradingsymbol='NIFTY 19 DEC 23300 CALL',
    exchange='NFO',
    quantity=75,
    price=0.05,               # must be > LTP
    trigger_price=0,
    order_type='LIMIT',       # ✅ always LIMIT — MARKET not allowed from Apr 2026
    transaction_type='BUY',
    trade_type='MIS'          # MIS, CNC, MARGIN, MTF, CO, BO
)

# SELL limit order (price < LTP for instant fill)
order_id = tsl.order_placement(
    tradingsymbol='NIFTY 19 DEC 23300 CALL',
    exchange='NFO',
    quantity=75,
    price=0.04,               # must be < LTP
    trigger_price=0,
    order_type='LIMIT',
    transaction_type='SELL',
    trade_type='MIS'
)

# Sliced/Iceberg order (exceeds freeze limit)
order_ids = tsl.order_placement(
    tradingsymbol="NIFTY 27 JAN 26000 CALL", exchange="NFO",
    transaction_type="BUY", quantity=1820, order_type="LIMIT",
    trade_type="MIS", price=21.05, should_slice=True
)
Modify / Cancel
tsl.modify_order(order_id=orderid, order_type="LIMIT", quantity=50, price=0.1)
tsl.cancel_order(OrderID=orderid)
tsl.cancel_all_orders()  # Cancel all intraday + square off positions
Order Info
tsl.get_order_detail(orderid=orderid)
tsl.get_order_status(orderid=orderid)     # 'Pending', 'Completed', etc.
tsl.get_executed_price(orderid=orderid)
tsl.get_exchange_time(orderid=orderid)

8. Super Orders (Entry + Target + SL in one shot)

✅ Preferred order type for algo strategies. When a user asks to build a trading algo with entry + SL + target, always use place_super_order() first — not 3 separate orders. It handles the full order lifecycle in 1 call with optional trailing SL.

order_id = tsl.place_super_order(
    tradingsymbol="TRIDENT", exchange="NSE",
    transaction_type="BUY", quantity=1,
    order_type="LIMIT", trade_type="MIS",
    price=25, target_price=27,
    stop_loss_price=24, trailing_jump=0.2
)

# Modify specific leg: ENTRY_LEG, TARGET_LEG, STOP_LOSS_LEG
tsl.modify_super_order(order_id=order_id, order_type="LIMIT", quantity=1,
                        price=24.9, leg_name="ENTRY_LEG")

tsl.cancel_super_order(order_id=order_id, leg_name="STOP_LOSS_LEG")
super_orders = tsl.get_super_orders()

9. Forever Orders / GTT

✅ Preferred order type for positional strategies — swing, BTST, positional. When a user asks to build a swing trade, BTST, or multi-day positional algo, always use Forever Orders (GTT) — not regular orders. Forever Orders stay active across sessions until triggered or cancelled.

Strategy typeUse
Intraday with SL + targetplace_super_order()
Swing / BTST / Positionalplace_forever_order()
# SINGLE trigger — fires once when price condition met
forever_id = tsl.place_forever_order(
    tradingsymbol="TRIDENT", exchange="NSE",
    transaction_type="BUY", quantity=1,
    order_type="LIMIT", trade_type="CNC",
    price=25, trigger_price=25.05, order_flag="SINGLE"
)

# OCO — One Cancels Other (target + SL together)
forever_id = tsl.place_forever_order(
    tradingsymbol="TRIDENT", exchange="NSE",
    transaction_type="SELL", quantity=1,
    order_type="LIMIT", trade_type="CNC",
    price=27, trigger_price=27.05,          # target leg
    order_flag="OCO",
    quantity_1=1, price_1=24, trigger_price_1=23.95   # SL leg
)

# Modify a specific leg
tsl.modify_forever_order(
    order_id=forever_id, order_type="LIMIT",
    quantity=1, price=24.9, trigger_price=24.7,
    disclosed_quantity=0, validity="DAY",
    leg_name="STOP_LOSS_LEG", order_flag="SINGLE"
)

# Cancel
tsl.cancel_forever_order(order_id=forever_id)   # returns cancel order_id str

# Fetch all active forever orders
forever_orders = tsl.get_forever_orders()        # returns list

10. Conditional Trigger Orders

⚠️ Not used in TradeHull strategies. We use TA-Lib to compute indicators and check conditions in Python directly. This gives full control over indicator parameters, multi-condition logic, and custom signals — far more flexible than Dhan's built-in trigger conditions.

Do NOT use place_conditional_trigger() in new code. Use get_historical_data() + TA-Lib instead.


11. Portfolio Management

holdings  = tsl.get_holdings()        # DataFrame
positions = tsl.get_positions()        # DataFrame
orderbook = tsl.get_orderbook()        # DataFrame
tradebook = tsl.get_trade_book()       # DataFrame
balance   = tsl.get_balance()          # float
pnl       = tsl.get_live_pnl()        # float

lot_size  = tsl.get_lot_size(tradingsymbol='NIFTY 19 DEC 24400 CALL')
# ⚠️ ALWAYS fetch lot size dynamically — never hardcode
# SEBI revises lot sizes periodically (e.g. NIFTY was 75, now 65)
# Hardcoded quantity causes order rejection when lot size changes

margin = tsl.margin_calculator(
    tradingsymbol='NIFTY DEC FUT', exchange='NFO',
    transaction_type='BUY', quantity=75,
    trade_type='MARGIN', price=24350, trigger_price=0
)

12. Full Market Depth (20-level)

# Single
depth_client = tsl.full_market_depth_data(("RELIANCE", "NSE"))
bid_df, ask_df = tsl.get_market_depth_df(depth_client)

# Multiple
symbol_list = [("RELIANCE","NSE"), ("NIFTY 09 DEC 26000 CALL","NFO")]
depth_data = tsl.full_market_depth_data(symbol_list)
for key, dc in depth_data.items():
    bid_df, ask_df = tsl.get_market_depth_df(dc)

13. Expired Options Historical Data

data = tsl.get_expired_option_data(
    tradingsymbol="NIFTY", exchange="NSE",
    interval=5,               # 1,5,15,25,60
    expiry_flag="WEEK",       # WEEK or MONTH
    expiry_code=1,            # 1=near, 2=next, 3=far
    strike="ATM",             # ATM, ATM+3, ATM-3 etc.
    option_type="CALL",
    from_date="2024-10-01", to_date="2024-10-31"
)

14. P&L Based Exit

# Auto exit when profit/loss threshold hit
tsl.enable_pnl_based_exit(
    profit_value=1000, loss_value=800,
    product_types=("INTRADAY", "DELIVERY"),
    enable_kill_switch=True   # True = full kill switch on trigger
)

15. Telegram Alerts

tsl.send_telegram_alert(
    message="BUY NIFTY 24400 CE @ 120 executed",
    receiver_chat_id="123456789",
    bot_token="YOUR_BOT_TOKEN"
)

📖 If user doesn't have receiver_chat_id or bot_token: Direct them to → https://tradehull.com/telegram-integration-for-algo-trading/


Common Patterns

OI Scalping Loop skeleton
import time
from Dhan_Tradehull import Tradehull

tsl = Tradehull(client_code, token_id, mode="access_token")

while True:
    atm, chain = tsl.get_option_chain(Underlying="NIFTY", exchange="INDEX", expiry=0, num_strikes=5)
    # analyse chain for OI buildup / unwinding

    # place orders — always LIMIT for F&O (MARKET banned from Apr 2026)
    ce_name, pe_name, strike = tsl.ATM_Strike_Selection(Underlying="NIFTY", Expiry=0)
    ltp          = tsl.get_ltp_data(names=[ce_name])[ce_name]
    limit_price  = round(ltp * 1.02, 1)   # 2% above LTP for instant BUY fill
    order_id     = tsl.order_placement(
        tradingsymbol=ce_name, exchange="NFO", quantity=tsl.get_lot_size(ce_name),
        price=limit_price, trigger_price=0,
        order_type="LIMIT", transaction_type="BUY", trade_type="MIS"
    )
    time.sleep(1)
Condition-based entry (TradeHull pattern)
# Use TA-Lib to compute indicator, check condition in Python — not conditional trigger API
import talib

chart       = tsl.get_historical_data(tradingsymbol="RELIANCE", exchange="NSE", timeframe="15")
chart["rsi"] = talib.RSI(chart["close"], timeperiod=14)
rc           = chart.iloc[-1]   # last completed candle

bc1          = rc["rsi"] < 30   # oversold
bc2          = orderbook["RELIANCE"]["traded"] is None

if bc1 and bc2:
    ltp         = tsl.get_ltp_data(names=["RELIANCE"])["RELIANCE"]
    limit_price = round(ltp * 1.002, 1)
    order_id    = tsl.order_placement(
        tradingsymbol="RELIANCE", exchange="NSE", quantity=10,
        price=limit_price, trigger_price=0,
        order_type="LIMIT", transaction_type="BUY", trade_type="CNC"
    )

Debug Mode

Add debug="YES" to any data-fetch method to print raw API response:

data = tsl.get_ltp_data(names=['NIFTY'], debug="YES")

Notes

  • access_token expires daily — regenerate each morning before market open
  • pin_totp PIN is lifetime — preferred for fully automated / scheduled algos
  • should_slice=True for large qty orders exceeding exchange freeze limit
  • All portfolio methods return pd.DataFrame
  • Exchange values: equity=NSE/BSE, index=INDEX, F&O=NFO/BFO, commodity=MCX
  • Always fetch lot size dynamically via get_lot_size() — never hardcode
  • Latest version: 3.3.1 (Jun 3, 2026)

Comments

Markdown supported