We are very happy using Dhan.
Requesting some extra features.
- IV Chart
- Able to select specific target price in the strategy builder.
We are very happy using Dhan.
Requesting some extra features.
Hi @Ketan
Thanks for reaching out to us & sharing your feedback.
We have noted your suggestion. Few of the things are in plan and others we will try to incorporate in our roadmap. But more than that, we would like to understand the problems these features will solve for you?
Happy to connect with you over a brief call. I will DM you my details.
Hey @Ketan, you had put in a short wishlist a while back and IV Chart was right at the top of it. That one is done. The ATM IV Chart on Options Trader Web plots ATM implied volatility against spot, refreshes live through the session, and carries up to 45 days of history. Head to Insights, then IV Chart, on options-trader.dhan.co. Does this cover what you were after when you asked?
@Tradehull_Imran i would be grateful if i could connect with you over call.
import datetime
import time
import pandas as pd
from Dhan_Tradehull import Tradehull # Optimized 3.3.2 structure
# ---------------------------------------------------------
# 1. API INITIALIZATION & SECURE CONFIGURATION
# ---------------------------------------------------------
# Placed as straight initialization strings to comply with sandbox rules.
# Replace these placeholders with your actual active Dhan credentials.
CLIENT_CODE = “YOUR_DHAN_CLIENT_CODE”
PIN = “YOUR_DHAN_PIN”
TOTP_SECRET = “YOUR_DHAN_TOTP_SECRET”
# Initialize via Tradehull v3.3.2 Framework
tsl = Tradehull(client_code=CLIENT_CODE, mode=“pin_totp”, pin=PIN, totp_secret=TOTP_SECRET)
# Global Configuration Constants
CONFIG = {
"START_TIME": datetime.time(9, 45),
"END_TIME": datetime.time(15, 0),
"MAX_DAILY_TRADES": 3,
"MAX_DIR_TRADES": 2,
"RISK_REWARD_RATIO": 3.0,
"TAG": "MULTI_TF_ALGO_V3",
"NIFTY_SPOT_SID": "13", # Dhan Spot Security ID
"LOT_SIZE": 50
}
# ---------------------------------------------------------
# 2. STATE TRACKING & STATS DATABASE
# ---------------------------------------------------------
state = {
"total_trades": 0,
"bullish_trades": 0,
"bearish_trades": 0,
"active_position": None,
"trade_history": \[\]
}
def is_expiry_day():
"""Checks if today is a weekly contract expiry day (e.g., Thursday for Nifty)."""
return datetime.date.today().weekday() == 3
# ---------------------------------------------------------
# 3. INDICATOR & DATA PIPELINES (NATIVE CALCULATIONS)
# ---------------------------------------------------------
def get_processed_data(interval_string):
"""Fetches historical records and computes technical indicators natively to bypass external blocks."""
try:
df = tsl.get_historical_data(
security_id=CONFIG\["NIFTY_SPOT_SID"\],
exchange_segment="NSE_INDEX",
duration=5,
interval=interval_string
)
if df.empty or len(df) < 30:
return pd.DataFrame()
\# Bollinger Bands (21, 2) computed natively
df\['bb_mid'\] = df\['close'\].rolling(window=21).mean()
rolling_std = df\['close'\].rolling(window=21).std()
df\['bb_lower'\] = df\['bb_mid'\] - (2 \* rolling_std)
\# EMA 9 & Smooth EMA 9 computed natively
df\['ema9'\] = df\['close'\].ewm(span=9, adjust=False).mean()
df\['ema9_smooth'\] = df\['ema9'\].ewm(span=9, adjust=False).mean()
\# RSI 9 & Smooth RSI 9 computed natively
delta = df\['close'\].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=9).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=9).mean()
rs = gain / (loss + 1e-10)
df\['rsi9'\] = 100 - (100 / (1 + rs))
df\['rsi9_smooth'\] = df\['rsi9'\].ewm(span=9, adjust=False).mean()
return df
except Exception as e:
print(f"Data Pipeline Error ({interval_string}m): {str(e)}")
return pd.DataFrame()
# ---------------------------------------------------------
# 4. MULTI-TIMEFRAME TRADE SCANNER
# ---------------------------------------------------------
def check_strategy_signals():
"""Evaluates cross-over structural rules simultaneously across 5m, 3m, and 1m charts."""
df5 = get_processed_data("5")
df3 = get_processed_data("3")
df1 = get_processed_data("1")
if df5.empty or df3.empty or df1.empty:
return None, 0.0, 0.0
\# Current Vectors
c5, bbm5, e5, es5, r5, rs5 = df5\['close'\].iloc\[-1\], df5\['bb_mid'\].iloc\[-1\], df5\['ema9'\].iloc\[-1\], df5\['ema9_smooth'\].iloc\[-1\], df5\['rsi9'\].iloc\[-1\], df5\['rsi9_smooth'\].iloc\[-1\]
c3, bbm3, e3, es3, r3, rs3 = df3\['close'\].iloc\[-1\], df3\['bb_mid'\].iloc\[-1\], df3\['ema9'\].iloc\[-1\], df3\['ema9_smooth'\].iloc\[-1\], df3\['rsi9'\].iloc\[-1\], df3\['rsi9_smooth'\].iloc\[-1\]
\# 1-Minute Core Crossovers
c1, bbm1 = df1\['close'\].iloc\[-1\], df1\['bb_mid'\].iloc\[-1\]
e1_curr, e1_prev = df1\['ema9'\].iloc\[-1\], df1\['ema9'\].iloc\[-2\]
es1_curr, es1_prev = df1\['ema9_smooth'\].iloc\[-1\], df1\['ema9_smooth'\].iloc\[-2\]
r1_curr, r1_prev = df1\['rsi9'\].iloc\[-1\], df1\['rsi9'\].iloc\[-2\]
rs1_curr, rs1_prev = df1\['rsi9_smooth'\].iloc\[-1\], df1\['rsi9_smooth'\].iloc\[-2\]
spot_price = c1
bb_lower_spot = df3\['bb_lower'\].iloc\[-1\]
\# Rule Validation Check lists
bullish_alignment = (c5 > bbm5) and (e5 > es5) and (r5 > rs5) and \\
(c3 > bbm3) and (e3 > es3) and (r3 > rs3) and \\
(c1 > bbm1) and (e1_prev <= es1_prev and e1_curr > es1_curr) and \\
(r1_prev <= rs1_prev and r1_curr > rs1_curr) and (rs1_curr > 49)
if bullish_alignment:
return "BULLISH", spot_price, bb_lower_spot
bearish_alignment = (c5 < bbm5) and (e5 < es5) and (r5 < rs5) and \\
(c3 < bbm3) and (e3 < es3) and (r3 < rs3) and \\
(c1 < bbm1) and (e1_prev >= es1_prev and e1_curr < es1_curr) and \\
(r1_prev >= rs1_prev and r1_curr < rs1_curr) and (rs1_curr > 51)
if bearish_alignment:
return "BEARISH", spot_price, bb_lower_spot
return None, spot_price, bb_lower_spot
# ---------------------------------------------------------
# 5. ORDER PLACEMENT ROUTINE
# ---------------------------------------------------------
def enter_trade_position(direction, spot_price, bb_lower_spot):
"""Finds option strike, checks expiry constraints, and maps trade entries."""
global state
if is_expiry_day():
print("Strategy Skipping Trades: Today is Weekly Expiry.")
return
atm = round(spot_price / 50) \* 50
if direction == "BULLISH":
target_strike = atm + 100
option_type = "CE"
else:
target_strike = atm - 100
option_type = "PE"
try:
expiry_type = "NEXT_EXPIRY" if is_expiry_day() else "CURRENT_EXPIRY"
contract = tsl.get_option_contract(
underlying="NIFTY",
strike=target_strike,
option_type=option_type,
expiry=expiry_type
)
if not contract:
print("Failed to dynamically fetch live option chain contract ID.")
return
order_res = tsl.place_order(
security_id=contract\["security_id"\],
exchange_segment="NSE_FNO",
transaction_type="BUY",
quantity=CONFIG\["LOT_SIZE"\],
order_type="MARKET",
product_type="MARGIN",
tag=CONFIG\["TAG"\]
)
if order_res.get("status") == "SUCCESS":
premium_entry = float(tsl.get_ltp(contract\["security_id"\]))
spot_risk_points = abs(spot_price - bb_lower_spot)
premium_risk = max(spot_risk_points \* 0.5, 5.0)
premium_sl = premium_entry - premium_risk
premium_target = premium_entry + (premium_risk \* CONFIG\["RISK_REWARD_RATIO"\])
state\["total_trades"\] += 1
if direction == "BULLISH":
state\["bullish_trades"\] += 1
else:
state\["bearish_trades"\] += 1
state\["active_position"\] = {
"direction": direction,
"security_id": contract\["security_id"\],
"strike": target_strike,
"option_type": option_type,
"entry_premium": premium_entry,
"current_sl": premium_sl,
"initial_sl": premium_sl,
"target": premium_target,
"risk_points": premium_risk,
"trailed_to_cost": False
}
print(f"🟢 ENTRY SUBMITTED \[{CONFIG\['TAG'\]}\]: {direction} {target_strike} {option_type} | Entry Premium: {premium_entry:.2f} | SL: {premium_sl:.2f} | Target: {premium_target:.2f}")
except Exception as e:
print(f"Order Execution Exception: {str(e)}")
# ---------------------------------------------------------
# 6. ACTIVE POSITION MANAGEMENT & MONITORING
# ---------------------------------------------------------
def manage_active_positions():
"""Handles continuous live premium execution trailing and technical trend filters."""
global state
pos = state\["active_position"\]
if not pos:
return
try:
current_premium = float(tsl.get_ltp(pos\["security_id"\]))
df3 = get_processed_data("3")
if df3.empty:
return
spot_close_3m = df3\['close'\].iloc\[-1\]
ema9_smooth_3m = df3\['ema9_smooth'\].iloc\[-1\]
if not pos\["trailed_to_cost"\] and current_premium >= (pos\["entry_premium"\] + pos\["risk_points"\]):
pos\["current_sl"\] = pos\["entry_premium"\]
pos\["trailed_to_cost"\] = True
print("📈 TRAILING UPDATE: Premium reached +1R. Stop Loss moved to entry cost.")
reason_to_exit = None
if current_premium >= pos\["target"\]:
reason_to_exit = "Target Profit Attained"
elif current_premium <= pos\["current_sl"\]:
reason_to_exit = "Stop Loss Breached"
elif pos\["direction"\] == "BULLISH" and spot_close_3m < ema9_smooth_3m:
reason_to_exit = "Bullish Structural Exit (3m Spot closed below Smooth EMA9)"
elif pos\["direction"\] == "BEARISH" and spot_close_3m > ema9_smooth_3m:
reason_to_exit = "Bearish Structural Exit (3m Spot closed above Smooth EMA9)"
if reason_to_exit:
exit_res = tsl.place_order(
security_id=pos\["security_id"\],
exchange_segment="NSE_FNO",
transaction_type="SELL",
quantity=CONFIG\["LOT_SIZE"\],
order_type="MARKET",
product_type="MARGIN",
tag=CONFIG\["TAG"\]
)
if exit_res.get("status") == "SUCCESS":
pnl = (current_premium - pos\["entry_premium"\]) \* CONFIG\["LOT_SIZE"\]
\# COMPLETELY PACK THE DICTIONARY MAP VALUES TO RESOLVE THE SYNTAX EXCEPTION
trade_record = {
"timestamp": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"tag": CONFIG\["TAG"\],
"contract": f"{pos\['strike'\]}\_{pos\['option_type'\]}",
"direction": pos\["direction"\],
"pnl": pnl,
"reason": reason_to_exit
}
state\["trade_history"\].append(trade_record)
print(f"🔴 POSITION CLOSED: {reason_to_exit} | Final Premium: {current_premium:.2f} | Trade P&L: ₹{pnl:.2f}")
state\["active_position"\] = None
display_performance_dashboard()
except Exception as e:
print(f"Error handling Active Position Management: {str(e)}") **›**\[2026-07-20 22:33:27 IST\] Packages restored from cache
›[2026-07-20 22:33:28 IST] ==================== SCRIPT OUTPUT START ====================
›[2026-07-20 22:33:28 IST] Traceback (most recent call last):
›[2026-07-20 22:33:28 IST] File “/tmp/project/main.py”, line 4, in
›[2026-07-20 22:33:28 IST] from Dhan_Tradehull import Tradehull # Optimized 3.3.2 structure
›[2026-07-20 22:33:28 IST] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
›[2026-07-20 22:33:28 IST] ModuleNotFoundError: No module named ‘Dhan_Tradehull’
›[2026-07-20 22:33:28 IST] ==================== SCRIPT OUTPUT END ====================
›[2026-07-20 22:33:28 IST] Execution failed with exit code: 1 finding error. kindly help