Now Live: Updated DhanHQ V2 Trading & Data APIs available

Hello Traders,

At Dhan, we continue our journey to improve your experience from time to time. It’s not just about features, but everything - one step at a time. This time we will talk about a major update to our Trading API stack - DhanHQ.

We have recently talked about Dhan Technology Stack and what powers our platform making it robust at scale - DEXT, which is our in-house proprietary trading system on the latest technology stack on top of which all Dhan platforms run. We continue to execute your trades extremely fast, it of course helps to provide you unparalleled experience in terms of execution speeds as well as introducing unique features for super traders and long term investors.

With DhanHQ, we wanted to extend the same tech stack to all algo traders and platforms for them to offer the Dhan experience. This was the reason for us to release DhanHQ APIs just a few months after we released Dhan to all users. And we saw the adoption of our APIs early on due to execution speed and simplicity - today about ~3% to 4% of all our trades are executed via APIs with over 10,000+ traders building their trade execution on top of DhanHQ APIs.

From the time we have launched DhanHQ APIs, we have introduced a range of new features and even optimised them to make it developer friendly - whether that be offering Unlimited Rate Limits on APIs or announcing industry first features like Kill Switch API. Currently, we offer <75 ms latency on our APIs that many of our users have experienced themselves. This has come from our focus on making execution fast for all our users across Dhan platforms - powered by DEXT.

Over the last two years, with the increasing adoption of APIs and tech stack upgradation at our end as well, we wanted to upgrade our API stack as well. From conversations with hundreds of API / Algo traders and our partners, we even realised there are some missing pieces in our API offering that we need to build.

To offer an enhanced API experience for all Dhan users, we are today announcing DhanHQ API Version 2.

This is the first versioning of Dhan APIs and we have introduced a range of new APIs for our users to build upon.

DhanHQ v2 extends execution capability with live order updates and forever orders on superfast APIs. Along with this, we have also added Market Quote APIs, built on top of Live Market Feed which can be integrated with ease. We have also made improvements, added bug fixes and increased stability with the new version.

Here are some key highlights from the DhanHQ V2:

  1. Market Quote API
    You can now fetch LTP, Quote (with OI) and Market Depth data directly via REST APIs, for upto 1000 instruments at once with Market Quote API.
    We realised the developer efforts required to integrate websockets for Live Market Feed and introduced the same offering on APIs, making it simpler for our users to integrate in their code base.

  2. Forever Orders API
    This has been a long pending request for all algo based swing traders. You can now place, modify and manage your Forever Orders, including single and OCO orders to manage risk and trade efficiently with Forever Order API.
    This adds new capability for all Swing Traders to now turn to systematic trading with this API category.

  3. Live Order Update
    We introduced Postback (link here) and removed Rate Limits from all GET APIs for you to get instant updates about your order. Now, taking this one step further, you can get live order updates via DhanHQ.
    Live Order Updates are sent in real time via websockets, which will update order status of all your orders placed via any Dhan platform - Live Order Update.

  4. Margin Calculator API
    Margin Calculation simplifies order placement by providing details about required margin and available balances before placing order. This helps you reduce order failures and ensure smooth order management at your end - Margin Calculator API.

  5. Intraday Historical Data for timeframes 1 min, 5 min, 15 min, 25 min and 60 min
    Now, on DhanHQ Data APIs, you can fetch data for different timeframes for a period upto last 7 days. This can help you construct different indicators across multiple time frames and build comprehensive trading systems - Intraday Historical Data API.

  6. Live Market Feed with JSON requests
    You can now authorise Live Market Feed via Query Parameters and subscribe/unsubscribe to instruments via JSON messages on websockets with this version. Also, FULL packet is now available which will give LTP, Quote, OI and Market Depth data in a single packet.
    This makes it simpler for all our users to integrate and get more data in a single packet.

And there are a range of other improvements and bug-fixes as well. You can find them under Releases in DhanHQ Ver 2.0 API Documentation - here.

This release is not just it. We are going to introduce another range of unique APIs on DhanHQ that will help you build better systems for trading, with added simplicity Also, you can check out entire DhanHQ V2 API Documentation in below link:

https://dhanhq.co/docs/v2/

You can go and check these APIs in live environment in action right now at our v2 API Playground, before making any changes to your code. Just head to Dhan API and try yourself.

We will soon be upgrading our DhanHQ Python library as well for v2.

Note: We insist you to shift to DhanHQ V2 from Version 1 before 31st December 2024, as there will not be any upgrades happening to earlier versions now.

You may already know that most of these features were requested by all our users on this community and across forums. We would love to hear your views and feedback on this new version of DhanHQ as well.

Thank you
Hardik

3 Likes

Hi @Hardik Great Work, is MTF enabled for forever orders through API, only CNC is working, can you please check?

Hi Team,
Great additions finally! I hope Market Quote API is part of Trading API bundle? Or do you require to pay for it?

You have to pay even if you are a supertrader since its part of their data offering

Hey @Hardik is the Forever order API available in the python package dhanhq · PyPI.

Oh… So trade APIs are still almost useless? Imagine placing bracket/cover orders without any way to get LTP. This is highly disappointing. Why can’t they make Market Quote API with extra limits a part of trade APIs? So weird.

1 Like

Hi,
I am trying to consume market feed from v2 websocket using node js. I am able to receive messages from socket. But I am facing issue while deserializing binary messages into readable format.

The packet bytes 35-46 is either for derivatives or FNO. But 43-46 is repeated for Day open value and also see discrepancy in Market depth mapping. I tried this during non trading hours.

Market Data Type: Full Data
I am referring to the message structure defined in the table from below link,

Below is my binary data reading logic.
let binpacks = message; // Received from websocket
// header packet
const feedResponseCode = binpacks.readUInt8(0); // 1
const messageLength = binpacks.readUInt16LE(1) // 2
const exchangeSegment = binpacks.readUInt8(3); // 1
const securityId = binpacks.readUInt32LE(4); // 4

// ticker packet
const ltp = binpacks.readFloatLE(8); // 4
const ltq = binpacks.readInt16LE(12); // 2
const ltt = binpacks.readUInt32LE(14); // 4
const atp = binpacks.readFloatLE(18); // 4
const volume = binpacks.readUInt32LE(22); // 4
const total_sell_quantity = binpacks.readUInt32LE(26); // 4
const total_buy_quantity = binpacks.readUInt32LE(30); // 4
const oi_contract = binpacks.readFloatLE(34); // 4
const oi_highest_day = binpacks.readFloatLE(38); // 4
const oi_lowest_day = binpacks.readFloatLE(42); // 4
const day_open = binpacks.readFloatLE(42); // 4
const day_close = binpacks.readFloatLE(46); // 4
const day_high = binpacks.readFloatLE(50); // 4
const day_low = binpacks.readFloatLE(54); // 4

const headerLength = 8;
const otherDataLength = 50;
const depthLevelSize = 20;
const depthLevels = [];

for (let i = 0; i < 5; i++) {
    let offset = headerLength + otherDataLength + i * depthLevelSize;

    if (offset + depthLevelSize > binpacks.length) break;
    //  const depthLevel: DepthLevel = new DepthLevel(
    let bidQuantity = binpacks.readInt32LE(offset); //bidQuantity
    let askQuantity = binpacks.readInt32LE(offset + 4); //askQuantity
    let bidOrders = binpacks.readInt16LE(offset + 8); //bidOrders
    let askOrders = binpacks.readInt16LE(offset + 10); //askOrders
    let bidPrice = binpacks.readFloatLE(offset + 12); //bidPrice
    let askPrice = binpacks.readFloatLE(offset + 16); //askPrice
    // );
    //   depthLevels.push(depthLevel);
}
1 Like

full packet not working:

subscribed tokens = [(2, ‘61375’, 21), (2, ‘61376’, 21), (2, ‘61377’, 21), (2, ‘63189’, 21), (2, ‘63190’, 21)]

this is from your official python library:
“”“Constants for Request Code”“”
Ticker = 15
Quote = 17
Depth = 19

def process_data(self, data):
    """Read binary data and initiate processing in received format"""
    first_byte = struct.unpack('<B', data[0:1])[0]
    if first_byte == 2:
        return self.process_ticker(data)
    elif first_byte == 3:
        return self.process_market_depth(data)
    elif first_byte == 4:
        return self.process_quote(data)
    elif first_byte == 5:
        return self.process_oi(data)
    elif first_byte == 6:
        return self.process_prev_close(data)
    elif first_byte == 7:
        return self.process_status(data)
    elif first_byte == 50:
        return self.server_disconnection(data)

as you can see there is no way you are handling full packet(first byte of full data packet is 8 according to your own docs) data in process data function.

I checked that when subscribed with 21(which is for full packet according to docs v2) [(2, ‘61375’, 21), (2, ‘61376’, 21), (2, ‘61377’, 21), (2, ‘63189’, 21), (2, ‘63190’, 21)] there is no response messages(full packet) coming from dhan server. kindly fix it asap

I further debug and extends DhanFeed class to check if even any response coming from your (dhan) server side with full packet(21 enum or first byte) and I found that there is no response coming from your end if I subscribe with full packet, works well with 17 quote first byte or enum, I kindly request you to fix this issue asap.

def validate_and_process_tuples(tuples_list, batch_size=100):
“”“Create a list of all instruments to be added and add in batches of 100"”"
# Check if all tuples have the same size (either all size 2 or all size 3)
tuple_lengths = set(len(tup) for tup in tuples_list)
if len(tuple_lengths) > 1:
raise ValueError(“All tuples must be of the same size, either all 2 or all 3.”)

# Assign default type 15 to tuples of size 2
processed_tuples = [ ]
for tup in tuples_list:
    if len(tup) == 2:
        processed_tuples.append((tup[0], tup[1], 15))
    else:
        processed_tuples.append(tup)

# Eliminate duplicate tuples
processed_tuples = list(set(processed_tuples))

# Separate tuples by type and prepare for batching
batches = defaultdict(list)
for tup in processed_tuples:
    exchange, instrument_id, type_ = tup
    batches[type_].append((exchange, instrument_id))

# Create the final dictionary with batches of the specified size
final_dict = {}
for type_ in [15, 17, 19, 21]:
    type_batches = [batches[type_][i:i+batch_size] for i in range(0, len(batches[type_]), batch_size)]
    final_dict[str(type_)] = type_batches

print(final_dict, "55555")
return final_dict

result= {‘15’: , ‘17’: , ‘19’: , ‘21’: [[(2, ‘44127’), (2, ‘54622’), (2, ‘47058’), (2, ‘47212’), (2, ‘47567’), (2, ‘50150’), (2, ‘59801’), (2, ‘47247’), (2, ‘36190’)]]}

And please do share, how many ticks we receive in single packet? like kite api’s websocket has around 600 to 1000 ticks data in single packet?

Brother the websocket data api is useless if you guys provide only 1 tick per packet received, as when you subscribe large amount of tokens you will not get synced prices or ltp or data and will get delayed data which will cause huge slipages.

I recently move back to dhan just because you guys start providing socket live feed api.

Hello @mayank_chopra

Welcome to MadeForTrade community!

Full packet is part of the Version 2 of APIs and the same is not updated yet on Python. Hence, you are not able to get data for the same.

On number of ticks that you can get in single packet, we send single tick per packet to ensure that there’s no delay in creating structure as well as destructuring the same. Rest assured on the latency, as we have tested these sockets with 5000 instruments at once and benchmarked it.
Just to clarify, API and Websocket are two different modules. On APIs, you get 1000 instrument data at once.

hi @Hardik

any news on bracket order facility (I remember it was being touted for api v2?) but would be great if you could provide some update on it and when it is being planned for availability

Hello, Hardik

quote packet should include oi data, right now the oi data is sent separately or it will be best if there is v2 with full packet support on python as well.

Any idea when you guys will update python to v2 ? ?

mainly I am interested in websockets streaming and just placing orders.

best regards.

Hello @mayank_chopra @mnikhil

We had Bracket order on v1 as well. Although, we have optimised modifying BO order in V2 now.

We have started work on this. It will be live soon.

Thanks. Can you please share couple of dev snippets with v2 api (perhaps with python)

I guess I was under the impression that bracket orders or (multiple orders in single order api call) still do not work

And Hardik Brother, can you share the endpoints to v2 websocket urls and any docs on binary data on unpacking full packet for the same ??

I will do it manually till the update comes.

best regards

Please help here, how to unpack v2 full packet:

unpacked_data = struct.unpack(‘<BHBIfHIfIIIffffff’, data[0:58])

        print(unpacked_data, len(unpacked_data))

        # Organize the unpacked data into a dictionary
        packet_data = {
            "instrument_token": unpacked_data[3],  # First byte as an integer
            "latest_traded_price": unpacked_data[4],  # LTP (float32)
            "last_traded_quantity": unpacked_data[2],  # LTQ (int16)
            "last_trade_time": unpacked_data[3],  # LTT (int32)
            "average_trade_price": unpacked_data[4],  # ATP (float32)
            "volume": unpacked_data[5],  # Volume (int32)
            "total_sell_quantity": unpacked_data[6],  # Total Sell Quantity (int32)
            "total_buy_quantity": unpacked_data[7],  # Total Buy Quantity (int32)
            "open_interest": unpacked_data[8],  # Open Interest (float32)
            "highest_open_interest": unpacked_data[9],  # Highest Open Interest (float32)
            "lowest_open_interest": unpacked_data[10],  # Lowest Open Interest (float32)
            "day_open_value": unpacked_data[11],  # Day Open Value (float32)
            "day_close_value": unpacked_data[12],  # Day Close Value (float32)
            "day_high_value": unpacked_data[13],  # Day High Value (float32)
            "day_low_value": unpacked_data[14]  # Day Low Value (float32)
        }

sample response with v2 with(
{
“RequestCode” : 21,
“InstrumentCount” : 1,
“InstrumentList” : [
{
“ExchangeSegment” : “NSE_FNO”,
“SecurityId” : “44118”
}
]
}
00000000: 08 A2 00 02 56 AC 00 00 33 F3 64 43 3C 00 F7 4F …V…3.dC<…O
00000010: 05 67 8F 42 94 43 A3 C2 C2 04 ED D4 01 00 8F 9C .g.B.C…
00000020: 01 00 57 C5 1F 00 0D C8 2A 00 54 9D 0E 00 00 80 …W…*.T…
00000030: 09 44 66 26 1B 44 CD 5C 32 44 CD 0C 28 43 4A 01 .Df&.D.\2D…(CJ.
00000040: 00 00 1E 00 00 00 01 00 01 00 66 E6 64 43 00 00 …f.dC…
00000050: 69 43 0F 00 00 00 C2 01 00 00 01 00 01 00 9A 99 iC…
00000060: 62 43 00 00 6B 43 2C 01 00 00 4B 00 00 00 01 00 bC…kC,…K…
00000070: 02 00 66 A6 61 43 00 00 6D 43 86 01 00 00 2C 01 …f.aC…mC…,.
00000080: 00 00 04 00 01 00 00 00 61 43 CD 0C 6D 43 2D 00 …aC…mC-.
00000090: 00 00 2D 00 00 00 01 00 01 00 33 F3 5E 43 66 26 …-…3.^Cf&
000000a0: 6D 43 mC

docs are correct, I have managed to unpack it with market data, Thank You, but still I’d love to know your unpacking process, docs should be more clear.

{‘instrument_token’: 44118, ‘latest_traded_price’: 202.35000610351562, ‘last_traded_quantity’: 15, ‘last_trade_time’: 1728465429, ‘average_trade_price’: 167.75999450683594, ‘volume’: 2028105, ‘total_sell_quantity’: 88935, ‘total_buy_quantity’: 19894875, ‘open_interest’: 2105580, ‘highest_open_interest’: 2105580, ‘lowest_open_interest’: 2036385, ‘day_open_value’: 160.0, ‘day_close_value’: 247.1999969482422, ‘day_high_value’: 208.5500030517578, ‘day_low_value’: 132.60000610351562, ‘market_depth’: [{‘bid_quantity’: 256050329, ‘ask_quantity’: 234881024, ‘number_of_bid_orders’: 1, ‘number_of_ask_orders’: 256, ‘bid_price’: -2.647139518883078e-23, ‘ask_price’: -204776848.0}, {‘bid_quantity’: 1766017740, ‘ask_quantity’: 1006632960, ‘number_of_bid_orders’: 0, ‘number_of_ask_orders’: 512, ‘bid_price’: -134225920.0, ‘ask_price’: -4.0385071399629314e-23}, {‘bid_quantity’: 2017675993, ‘ask_quantity’: 234881024, ‘number_of_bid_orders’: 1, ‘number_of_ask_orders’: 512, ‘bid_price’: 7.174648137343064e-43, ‘ask_price’: 2.3055778372676344e+23}, {‘bid_quantity’: -2025633050, ‘ask_quantity’: 1996488704, ‘number_of_bid_orders’: 1, ‘number_of_ask_orders’: 768, ‘bid_price’: 2.9805960366502404e-08, ‘ask_price’: 6.179595906915261e-39}, {‘bid_quantity’: 1514359552, ‘ask_quantity’: 1761607680, ‘number_of_bid_orders’: 0, ‘number_of_ask_orders’: 1024, ‘bid_price’: 1.5112956250988393e+23, ‘ask_price’: -204776032.0}]}

Hi Mayank,

No. I am not able to deserialize binary data completely. I am using node js for the market feed subscription. I am able to map partially. I am looking for sample packet with expected mapping result, as I will not be able to verify if my mapping result is proper.

Hi @Hardik

Is it possible to share the full packet data as file and expected mapping result? Even though we deserialize the data, we should be able to confirm it is proper.