Dhan API - Java implementation of data apis

Hi There, has anyone successfully implemented the dhan api client using java programming tools. If yes, could you share the snippets of byte code that needs to be generated during the authentication and the response from the server after the successful auth.

I appreciate the auth and subscriptions are done in async mode but I am not entirely sure whether the ws connection is successful. @Hardik I appreciate you have responded on similar queries for python implementation. But any help on Java would be really appreciated. thanks.

Hello @pnsudesh

I am myself not very well versed with Java, but if you are facing any particular issue while integrating, let us know and we will be happy to assist in any way possible. Are you able to connect to Dhan Live Market Feed or even the connection is not getting established?

Hi @Hardik Thanks for the message.

I am trying to build the Java app (Spring framework) and I am utilizing the EndPoint class as below.

public class WSEndpoint extends Endpoint {
private Session session;

    @Override
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        System.out.println("onOpen");
        this.session.addMessageHandler(new MessageHandler.Whole<byte[]>() {
			@Override
			public void onMessage(byte[] message) {
				System.out.println("from server:" + message);	
			}
        });
    }
    
    @OnMessage
    public void connected(String msg){
        System.out.println("Message from DHAN server: " + msg);
    }
    @OnClose
    public void disconnected(Session session, CloseReason reason){
       // System.out.println("User "+ user + " disconnected as a result of "+ reason.getReasonPhrase());
    	System.out.println("onClose");
    }
    @OnError
    public void disconnected(Session session, Throwable error){
        System.out.println("Error communicating with DHAN server: " + error.getMessage());
    }
    
    public void connectToDhan(byte[] authMsg) throws EncodeException, IOException{
		System.out.println("connectToDhan");
		ByteBuffer buffer = ByteBuffer.wrap(authMsg);
		this.session.getBasicRemote().sendBinary(buffer);
		System.out.println("AFTER connectToDhan");
}

}

While I can see the connectToDhan method execution via System output, neither Onmessage not Onopen get triggered from Dhan server. It seems my auth message itself is incorrect. I am using byte arrays to generate the byte code to pass it on the endpoint call.

Do you have any Java working example of using the Dhan apis for getting the market data? May be I am not passing the right set of byte codes. Anything to help me solve this problem will be highly appreciated. thanks.

Hello @pnsudesh

We do not have any Java implementation samples, but if you can share the byte structure you are sending or the code snippet where you are converting it to byte array, we can look into that and assist you.

package com.tradingbot.dhan.component;

import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.springframework.stereotype.Component;

import com.tradingbot.dhan.dto.Instrument;
import com.tradingbot.dhan.dto.MarketDepth;
import com.tradingbot.dhan.dto.MarketDepthData;
import com.tradingbot.dhan.dto.OIData;
import com.tradingbot.dhan.dto.PreviousCloseData;
import com.tradingbot.dhan.dto.QuoteData;
import com.tradingbot.dhan.dto.ResponseHeader;
import com.tradingbot.dhan.dto.TickerData;

@Component
public class DhanWebSocketClient extends WebSocketClient {

    private static final String SOCKET_URI = "wss://api-feed.dhan.co";
    private static final String CLIENT_ID = "XXXXXXXXXX";
    private static final String API_ACCESS_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    public DhanWebSocketClient() throws Exception {
        super(new URI(SOCKET_URI));
    }

    @PostConstruct
    public void start() {
        this.connect();
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        System.out.println("Connection opened");
        sendAuthorizationPacket();
        subscribeInstruments(); // Call the method to subscribe to instruments
    }

    @Override
    public void onMessage(String message) {
        System.out.println("message :"+message);
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        ResponseHeader header = new ResponseHeader();
        header.setFeedResponseCode(bytes.get());
        header.setMessageLength(bytes.getShort());
        header.setExchangeSegment(bytes.get());
        header.setSecurityId(bytes.getInt());

        System.out.println("header.getFeedResponseCode() : "+header.getFeedResponseCode());
        // Process based on response code
        switch (header.getFeedResponseCode()) {
            case 2:
                TickerData tickerData = new TickerData();
                tickerData.setHeader(header);
                tickerData.setLastTradedPrice(bytes.getFloat());
                tickerData.setLastTradeTime(bytes.getInt());
                System.out.println(tickerData);
                break;
            case 4:
                QuoteData quoteData = new QuoteData();
                quoteData.setHeader(header);
                quoteData.setLatestTradedPrice(bytes.getFloat());
                quoteData.setLastTradedQuantity(bytes.getShort());
                quoteData.setLastTradeTime(bytes.getInt());
                quoteData.setAverageTradePrice(bytes.getFloat());
                quoteData.setVolume(bytes.getInt());
                quoteData.setTotalSellQuantity(bytes.getInt());
                quoteData.setTotalBuyQuantity(bytes.getInt());
                quoteData.setDayOpenValue(bytes.getFloat());
                quoteData.setDayCloseValue(bytes.getFloat());
                quoteData.setDayHighValue(bytes.getFloat());
                quoteData.setDayLowValue(bytes.getFloat());
                System.out.println(quoteData);
                break;
            case 5:
                OIData oiData = new OIData();
                oiData.setHeader(header);
                oiData.setOpenInterest(bytes.getInt());
                System.out.println(oiData);
                break;
            case 3:
                MarketDepthData marketDepthData = new MarketDepthData();
                marketDepthData.setHeader(header);
                marketDepthData.setLatestTradedPrice(bytes.getFloat());
                List<MarketDepth> marketDepthList = new ArrayList<>();
                for (int i = 0; i < 5; i++) {
                    MarketDepth marketDepth = new MarketDepth();
                    marketDepth.setBidQuantity(bytes.getInt());
                    marketDepth.setAskQuantity(bytes.getInt());
                    marketDepth.setBidOrders(bytes.getShort());
                    marketDepth.setAskOrders(bytes.getShort());
                    marketDepth.setBidPrice(bytes.getFloat());
                    marketDepth.setAskPrice(bytes.getFloat());
                    marketDepthList.add(marketDepth);
                }
                marketDepthData.setMarketDepthList(marketDepthList);
                System.out.println(marketDepthData);
                break;
            case 6:
                PreviousCloseData previousCloseData = new PreviousCloseData();
                previousCloseData.setHeader(header);
                previousCloseData.setPreviousClosePrice(bytes.getInt());
                previousCloseData.setPreviousDayOI(bytes.getInt());
                System.out.println(previousCloseData);
                break;
            case 7:
                System.out.println("Market Status: Open/Close notification");
                break;
            default:
                System.out.println("Unknown response code: " + header.getFeedResponseCode());
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        System.out.println("Connection closed with exit code " + code + " additional info: " + reason);
    }

    @Override
    public void onError(Exception ex) {
        ex.printStackTrace();
    }

    private void sendAuthorizationPacket() {
        ByteBuffer buffer = ByteBuffer.allocate(585);
        // Header
        buffer.put((byte) 11); // Feed Request Code for new feed
        buffer.putShort((short) 585); // Message Length
        buffer.put(CLIENT_ID.getBytes()); // Client ID
        buffer.position(34); // Skip to Dhan Auth
        buffer.put(new byte[50]); // Dhan Auth (zeroes)
        // Payload
        buffer.put(API_ACCESS_TOKEN.getBytes()); // API Access Token
        buffer.putShort((short) 2); // Authentication Type

        buffer.flip();
        send(buffer);
    }

    private void subscribeInstruments() {
        // Create a list of instruments to subscribe to
        List<Instrument> instruments = createInstrumentsList();

        // Subscribe to instruments
        ByteBuffer buffer = ByteBuffer.allocate(2188); // 88 bytes for header + 2100 bytes for instruments
        // Header
        buffer.put((byte) 11); // Feed Request Code for new feed
        buffer.putShort((short) 2187); // Message Length (88 + 2100 - 1)
        buffer.put(CLIENT_ID.getBytes()); // Client ID
        buffer.position(34); // Skip to Dhan Auth
        buffer.put(new byte[50]); // Dhan Auth (zeroes)
        // Payload
        buffer.putInt(instruments.size()); // Number of instruments to subscribe
        for (Instrument instrument : instruments) {
            buffer.put(instrument.getExchangeSegment().getBytes()); // Exchange Segment
            buffer.put(instrument.getSecurityId().getBytes()); // Security ID
            // Pad with zeroes if necessary
            for (int k = 0; k < 19; k++) {
                buffer.put((byte) 0);
            }
        }

        buffer.flip();
        send(buffer);
    }

    private List<Instrument> createInstrumentsList() {
        // Implement logic to create a list of instruments to subscribe to
        return List.of(
            //new Instrument("IDX_I", "13"),//NIFTY
            //new Instrument("Index", "25"),//BANKNIFTY
            //new Instrument("NSE", "10940") ,//DIVISLAB
            new Instrument("MCX", "426280") 
        );
    }
}

@Hardik ,

I tried with above code in java springboot. But i am getting response code 50 from dhan in public void onMessage(ByteBuffer bytes)() method. What does this error code mean?

default:
System.out.println("Unknown response code: " + header.getFeedResponseCode());
//the above line in onMessage() method prints
//Unknown response code: 50

Also what should be exchange segment given while subscribing? is it MCX, NSE etc in SEM_EXM_EXCH_ID column in api-scrip-master.csv?

What should be securityId while subscribing? is it the value in SEM_SMST_SECURITY_ID in api-scrip-master.csv?

Hello @sajos

Welcome to Dhan Community!

If the feed is getting disconnected and the message code is 50, then the server is disconnecting the same. There can be multiple reasons for the same, you will have to read the entire byte message to understand.

You can read more about disconnection here: Live Market Feed - DhanHQ Ver 1.0 / API Document

The exchange segment codes are given here: Annexure - DhanHQ Ver 1.0 / API Document and on Security ID, yes you can find the same in the Security ID List.

Error code is 1006
I updated the byte content in request based on exchange segment list in Annexure - DhanHQ Ver 1.0 / API Document. But the call is not coming in onMessage() now.

@Override
    public void onClose(int code, String reason, boolean remote) {
        LOGGER.info("Connection closed with exit code {}. Additional info: {}", code, reason);
    }

prints

Connection closed with exit code 1006. Additional info:

Complete code:

package com.tradingbot.dhan.component;

import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.tradingbot.dhan.dto.Instrument;
import com.tradingbot.dhan.dto.MarketDepth;
import com.tradingbot.dhan.dto.MarketDepthData;
import com.tradingbot.dhan.dto.OIData;
import com.tradingbot.dhan.dto.PreviousCloseData;
import com.tradingbot.dhan.dto.QuoteData;
import com.tradingbot.dhan.dto.ResponseHeader;
import com.tradingbot.dhan.dto.TickerData;

@Component
public class DhanWebSocketClient extends WebSocketClient {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(DhanWebSocketClient.class);
    
    private static final String SOCKET_URI = "wss://api-feed.dhan.co";
    private static final String CLIENT_ID = "xxxxxxxxx";
    private static final String API_ACCESS_TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

    public DhanWebSocketClient() throws Exception {
        super(new URI(SOCKET_URI));
    }

    @PostConstruct
    public void start() {
        this.connect();
    }

    @Override
    public void onOpen(ServerHandshake handshakedata) {
        LOGGER.info("Connection opened");
        sendAuthorizationPacket();
        subscribeInstruments();
    }

    @Override
    public void onMessage(String message) {
        LOGGER.info("Received message: {}", message);
    }

    @Override
    public void onMessage(ByteBuffer bytes) {
        ResponseHeader header = new ResponseHeader();
        header.setFeedResponseCode(bytes.get());
        header.setMessageLength(bytes.getShort());
        header.setExchangeSegment(bytes.get());
        header.setSecurityId(bytes.getInt());

        LOGGER.info("Feed Response Code: {}", header.getFeedResponseCode());

        switch (header.getFeedResponseCode()) {
            case 50:
                handleDisconnection(bytes.getShort());
                break;
            case 2:
                TickerData tickerData = parseTickerData(header, bytes);
                LOGGER.info("Ticker Data: {}", tickerData);
                break;
            case 4:
                QuoteData quoteData = parseQuoteData(header, bytes);
                LOGGER.info("Quote Data: {}", quoteData);
                break;
            case 5:
                OIData oiData = parseOIData(header, bytes);
                LOGGER.info("OI Data: {}", oiData);
                break;
            case 3:
                MarketDepthData marketDepthData = parseMarketDepthData(header, bytes);
                LOGGER.info("Market Depth Data: {}", marketDepthData);
                break;
            case 6:
                PreviousCloseData previousCloseData = parsePreviousCloseData(header, bytes);
                LOGGER.info("Previous Close Data: {}", previousCloseData);
                break;
            case 7:
                LOGGER.info("Market Status: Open/Close notification");
                break;
            default:
                LOGGER.warn("Unknown response code: {}", header.getFeedResponseCode());
        }
    }

    @Override
    public void onClose(int code, String reason, boolean remote) {
        LOGGER.info("Connection closed with exit code {}. Additional info: {}", code, reason);
    }

    @Override
    public void onError(Exception ex) {
        LOGGER.error("WebSocket error: ", ex);
    }

    /**
     * Sends the authorization packet to establish connection with DhanHQ WebSocket.
     * The authorization packet consists of a binary message with header and API access token.
     */
    private void sendAuthorizationPacket() {
        ByteBuffer buffer = ByteBuffer.allocate(585);
        
        // Header
        buffer.put((byte) 11); // Feed Request Code to connect new feed
        buffer.putShort((short) 585); // Message Length

        // Client ID
        putPaddedBytes(buffer, CLIENT_ID.getBytes(), 30);
        
        // Dhan Auth - passed as zero
        buffer.put(new byte[50]);
        
        // API Access Token
        putPaddedBytes(buffer, API_ACCESS_TOKEN.getBytes(), 500);
        
        // Authentication Type - 2P by default
        buffer.putShort((short) 2);

        buffer.flip();
        send(buffer);
    }

    /**
     * Subscribes to market data for a list of instruments.
     * A bulk subscribe request is sent with a header and up to 100 instruments.
     */
    private void subscribeInstruments() {
        List<Instrument> instruments = createInstrumentsList();
        if (instruments.size() > 100) {
            throw new IllegalArgumentException("Cannot subscribe more than 100 instruments in a single request.");
        }
    
        ByteBuffer buffer = ByteBuffer.allocate(2188);
    
        // Header
        buffer.put((byte) 15);  // Subscription message code
        buffer.putShort((short) 2188);  // Message length
        putPaddedBytes(buffer, CLIENT_ID.getBytes(), 30);
        buffer.put(new byte[50]);  // Dhan Auth - passed as zero
        buffer.putInt(instruments.size());  // Number of instruments
    
        // Instruments
        for (Instrument instrument : instruments) {
            buffer.put(mapExchangeSegmentToByte(instrument.getExchangeSegment()));
            putPaddedBytes(buffer, instrument.getSecurityId().getBytes(), 20);
        }
    
        // Padding
        buffer.put(new byte[2100 - instruments.size() * 21]);
    
        // Logging buffer content
        logBufferContent(buffer);
    
        buffer.flip();
        send(buffer);
    }
    
    /**
     * Logs the content of the buffer for debugging purposes.
     * Converts the buffer content to a hex string representation.
     */
    private void logBufferContent(ByteBuffer buffer) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < buffer.position(); i++) {
            sb.append(String.format("%02X ", buffer.get(i)));
        }
        LOGGER.info("Buffer contents before sending: {}", sb.toString());
    }
    
    /**
     * Maps exchange segment strings to corresponding byte values.
     */
    private byte mapExchangeSegmentToByte(String exchangeSegment) {
        switch (exchangeSegment) {
            case "IDX_I":
                return 0;
            case "NSE_EQ":
                return 1;
            case "NSE_FNO":
                return 2;
            case "NSE_CURRENCY":
                return 3;
            case "BSE_EQ":
                return 4;
            case "MCX_COMM":
                return 5;
            case "BSE_CURRENCY":
                return 7;
            case "BSE_FNO":
                return 8;
            default:
                throw new IllegalArgumentException("Unknown exchange segment: " + exchangeSegment);
        }
    }
    
    /**
     * Adds padding bytes to the buffer.
     * Ensures that the byte array fits the specified length by adding zero padding if necessary.
     */
    private void putPaddedBytes(ByteBuffer buffer, byte[] bytes, int length) {
        byte[] paddedBytes = new byte[length];
        System.arraycopy(bytes, 0, paddedBytes, 0, Math.min(bytes.length, length));
        buffer.put(paddedBytes);
    }

    /**
     * Handles disconnection messages received from the server.
     * Logs the disconnection reason based on the disconnection message code.
     */
    private void handleDisconnection(short disconnectionMessageCode) {
        String reason;
        switch (disconnectionMessageCode) {
            case 805:
                reason = "Connection limit exceeded";
                break;
            case 806:
                reason = "Data APIs not subscribed";
                break;
            case 807:
                reason = "Access token is expired";
                break;
            case 808:
                reason = "Authentication Failed - Check Client ID";
                break;
            case 809:
                reason = "Access token is invalid";
                break;
            default:
                reason = "Unknown reason code " + disconnectionMessageCode;
        }
        LOGGER.info("Disconnection Reason: {}", reason);
    }

    /**
     * Parses ticker data from the received binary message.
     * Constructs a TickerData object with the extracted information.
     */
    private TickerData parseTickerData(ResponseHeader header, ByteBuffer bytes) {
        TickerData tickerData = new TickerData();
        tickerData.setHeader(header);
        tickerData.setLastTradedPrice(bytes.getFloat());
        tickerData.setLastTradeTime(bytes.getInt());
        return tickerData;
    }

    /**
     * Parses quote data from the received binary message.
     * Constructs a QuoteData object with the extracted information.
     */
    private QuoteData parseQuoteData(ResponseHeader header, ByteBuffer bytes) {
        QuoteData quoteData = new QuoteData();
        quoteData.setHeader(header);
        quoteData.setLatestTradedPrice(bytes.getFloat());
        quoteData.setLastTradedQuantity(bytes.getShort());
        quoteData.setLastTradeTime(bytes.getInt());
        quoteData.setAverageTradePrice(bytes.getFloat());
        quoteData.setVolume(bytes.getInt());
        // quoteData.setValue(bytes.getFloat());
        // quoteData.setOpen(bytes.getFloat());
        // quoteData.setHigh(bytes.getFloat());
        // quoteData.setLow(bytes.getFloat());
        // quoteData.setClose(bytes.getFloat());
        return quoteData;
    }

    /**
     * Parses OI data from the received binary message.
     * Constructs an OIData object with the extracted information.
     */
    private OIData parseOIData(ResponseHeader header, ByteBuffer bytes) {
        OIData oiData = new OIData();
        oiData.setHeader(header);
        // oiData.setLatestTradedPrice(bytes.getFloat());
        // oiData.setLastTradedQuantity(bytes.getShort());
        // oiData.setLastTradeTime(bytes.getInt());
        // oiData.setAverageTradePrice(bytes.getFloat());
        // oiData.setVolume(bytes.getInt());
        // oiData.setValue(bytes.getFloat());
        // oiData.setOpen(bytes.getFloat());
        // oiData.setHigh(bytes.getFloat());
        // oiData.setLow(bytes.getFloat());
        // oiData.setClose(bytes.getFloat());
        // oiData.setOi(bytes.getFloat());
        return oiData;
    }

    /**
     * Parses market depth data from the received binary message.
     * Constructs a MarketDepthData object with the extracted information.
     */
    private MarketDepthData parseMarketDepthData(ResponseHeader header, ByteBuffer bytes) {
        MarketDepthData marketDepthData = new MarketDepthData();
        marketDepthData.setHeader(header);
        List<MarketDepth> buyMarketDepth = new ArrayList<>();
        List<MarketDepth> sellMarketDepth = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            MarketDepth buyDepth = new MarketDepth();
            // buyDepth.setPrice(bytes.getFloat());
            // buyDepth.setQuantity(bytes.getInt());
            // buyDepth.setOrder(bytes.getInt());
            buyMarketDepth.add(buyDepth);
        }
        for (int i = 0; i < 5; i++) {
            MarketDepth sellDepth = new MarketDepth();
            // sellDepth.setPrice(bytes.getFloat());
            // sellDepth.setQuantity(bytes.getInt());
            // sellDepth.setOrder(bytes.getInt());
            sellMarketDepth.add(sellDepth);
        }
        // marketDepthData.setBuyMarketDepth(buyMarketDepth);
        // marketDepthData.setSellMarketDepth(sellMarketDepth);
        return marketDepthData;
    }

    /**
     * Parses previous close data from the received binary message.
     * Constructs a PreviousCloseData object with the extracted information.
     */
    private PreviousCloseData parsePreviousCloseData(ResponseHeader header, ByteBuffer bytes) {
        PreviousCloseData previousCloseData = new PreviousCloseData();
        previousCloseData.setHeader(header);
        // previousCloseData.setClosePrice(bytes.getFloat());
        return previousCloseData;
    }

    /**
     * Creates a list of Instrument objects to subscribe to market data.
     * Replace this with actual instrument data in a real-world scenario.
     */
    private List<Instrument> createInstrumentsList() {
        List<Instrument> instruments = new ArrayList<>();
        // Add instruments to the list
        instruments.add(new Instrument("IDX_I", "2"));//NIFTY
        instruments.add(new Instrument("NSE_EQ", "10666"));//PNB
        // Add more instruments as needed
        return instruments;
    }
}

Hello @sajos

Error code 1006 is a standard error code where browser/local closes the connection. You can maybe check this: javascript - Getting the reason why websockets closed with close code 1006 - Stack Overflow

@sajos
Hi Sajos, I hope you’re doing well! Were you able to resolve the issue you were facing? I’ve been having some challenges with using the DhanHQ API live stream for Java, especially with Spring Boot, and was wondering if you could share any insights or advice.

@Hardik
Hi Hardik,

I hope you’re doing well. I’m still having trouble with the WebSocket client for the Java Spring Boot project and was hoping I could get a bit more guidance from you. If it’s not possible to share the working code, could you kindly provide the Postman setup that would allow me to fetch WebSocket data? It would be incredibly helpful.

If you have the time, perhaps you could even create a YouTube video showing how to get live market data through Postman? Once I can see the steps you’re following, I think I’ll be able to replicate them in my code. I’m sure this would be really beneficial not just for me as a Java developer, but for others too.

Thank you so much for your time and support!

@Hardik
I was wondering if you might be able to assist me with this. If support for this isn’t available, I may need to explore other API providers that offer more detailed support for Java. I appreciate your help in advance. Thank you!

1 Like

Hello @rahulbajaj

Great to see you around after this long time. Yes, we are coming up with an update soon on this, which will help you integrate faster. Just keep a check on community, will update soon.

1 Like

@Hardik yes I have used the new version documentation and now I am able to connect to the server as well as getting the continuous data via websocket. Now only the issue I am facing is that the service return and when I am converting it into UTF-8 format.

It is getting change into something weird. See this below :

lastTradedPrice=2.2745528E-38, lastTradeTime=177399910.

Not sure what wrong I am doing while transforming it. anyone can help or suggest ? how to handle onMessage(ByteBuffer) method.

@sajos You will be the best person out here to help. Would be really grateful if you respond.

Hey @rahulbajaj

Great to know that. On conversion, do note that you will have to convert byte by byte (as provided in documentation) and as per the type of data variable.

For example, LTP is float32 and LTT is in EPOCH, that you need to convert to UTC.

Hey @Hardik

Thanks for the update but I have notice one thing the last traded price is coming in exponential format. Also, I am getting something like this as response after I convert it e.g “2160153500-102-39-4868-774-24102”. Can you help me decode it.

Hello @rahulbajaj

This might have to do with binary parsing and mapping. The price if converted in correct format will show correct price only. Do note that it is in float and byte size that needs to be converted.

@rahulbajaj, I could not succeed with the initial version. Trying with version 2 now.

@Hardik ,

I’m getting disconnected with error code 9731 when I run the below code. Can you help.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.client.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

@SpringBootApplication
public class TradingApplication {

    public static void main(String[] args) {
        SpringApplication.run(TradingApplication.class, args);
    }

    @Bean
    public WebSocketConnectionManager connectionManager() {
        StandardWebSocketClient client = new StandardWebSocketClient();
        DhanWebSocketHandler handler = new DhanWebSocketHandler();

        String url = "wss://api-feed.dhan.co?version=2"
        		+ "&token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        		+ "&clientId=XXXXXXXXXX"
        		+ "&authType=2";

        WebSocketConnectionManager manager = new WebSocketConnectionManager(client, handler, url);
        manager.setAutoStartup(true);
        return manager;
    }
}

class DhanWebSocketHandler extends AbstractWebSocketHandler {

    private WebSocketSession session;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        this.session = session;
        System.out.println("Connected to Dhan WebSocket");

        // Example: Sending a subscription request in binary format
        String subscriptionMessage = "{"
            + "\"RequestCode\": 15,"
            + "\"InstrumentCount\": 2,"
            + "\"InstrumentList\": ["
            + "  {\"ExchangeSegment\": \"NSE_EQ\", \"SecurityId\": \"1333\"},"//HDFCBANK
            + "  {\"ExchangeSegment\": \"BSE_EQ\", \"SecurityId\": \"532540\"}"//TCS
            + "]}";

        // Convert the JSON string to a byte array
        byte[] messageBytes = subscriptionMessage.getBytes(StandardCharsets.UTF_8);

        // Send the byte array as a BinaryMessage
        session.sendMessage(new BinaryMessage(messageBytes));
        System.out.println("Sent subscription request in binary format");
    }

    // Method to send Feed Disconnect request
    public void sendDisconnectRequest() throws Exception {
        if (session != null && session.isOpen()) {
            String disconnectMessage = "{\"RequestCode\": 16}";
            session.sendMessage(new TextMessage(disconnectMessage));
            System.out.println("Sent Feed Disconnect request");
        }
    }

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        ByteBuffer byteBuffer = message.getPayload();

        // Parse the Response Header
        byte feedResponseCode = byteBuffer.get(); // 1 byte
        short messageLength = byteBuffer.getShort(); // 2 bytes (int16)
        byte exchangeSegment = byteBuffer.get(); // 1 byte
        int securityId = byteBuffer.getInt(); // 4 bytes (int32)

        System.out.println("Feed Response Code: " + feedResponseCode);
        System.out.println("Message Length: " + messageLength);
        System.out.println("Exchange Segment: " + exchangeSegment);
        System.out.println("Security ID: " + securityId);

        // Check for specific packet types
        if (feedResponseCode == 50) { // Code for Feed Disconnect
            parseDisconnectPacket(byteBuffer);
        } else if (feedResponseCode == 8) { // Code for Full Packet
            parseFullPacket(byteBuffer);
        }
        // Add additional checks for other packet types as needed...
    }

    private void parseDisconnectPacket(ByteBuffer byteBuffer) {
        // Read Disconnection Message Code as int16 (2 bytes)
        short disconnectCode = byteBuffer.getShort();
        System.out.println("Received Disconnect Packet with Code: " + disconnectCode);
    }

    private void parseFullPacket(ByteBuffer byteBuffer) {
        // Read Latest Traded Price (LTP) as float32 (4 bytes)
        float latestTradedPrice = byteBuffer.getFloat();

        // Read Last Traded Quantity as int16 (2 bytes)
        short lastTradedQuantity = byteBuffer.getShort();

        // Read Last Trade Time (LTT) as int32 (4 bytes)
        int lastTradeTime = byteBuffer.getInt();

        // Read Average Trade Price (ATP) as float32 (4 bytes)
        float averageTradePrice = byteBuffer.getFloat();

        // Read Volume as int32 (4 bytes)
        int volume = byteBuffer.getInt();

        // Read Total Sell Quantity as int32 (4 bytes)
        int totalSellQuantity = byteBuffer.getInt();

        // Read Total Buy Quantity as int32 (4 bytes)
        int totalBuyQuantity = byteBuffer.getInt();

        // Read Open Interest as int32 (4 bytes)
        int openInterest = byteBuffer.getInt();

        // Read Highest Open Interest of the day (for NSE_FNO) as int32 (4 bytes)
        int highestOpenInterest = byteBuffer.getInt();

        // Read Lowest Open Interest of the day (for NSE_FNO) as int32 (4 bytes)
        int lowestOpenInterest = byteBuffer.getInt();

        // Read Day Open Value as float32 (4 bytes)
        float dayOpenValue = byteBuffer.getFloat();

        // Read Day Close Value as float32 (4 bytes)
        float dayCloseValue = byteBuffer.getFloat();

        // Read Day High Value as float32 (4 bytes)
        float dayHighValue = byteBuffer.getFloat();

        // Read Day Low Value as float32 (4 bytes)
        float dayLowValue = byteBuffer.getFloat();

        // Read Market Depth Structure (5 packets of 20 bytes each)
        for (int i = 0; i < 5; i++) {
            int bidQuantity = byteBuffer.getInt(); // 4 bytes
            int askQuantity = byteBuffer.getInt(); // 4 bytes
            short numberOfBidOrders = byteBuffer.getShort(); // 2 bytes
            short numberOfAskOrders = byteBuffer.getShort(); // 2 bytes
            float bidPrice = byteBuffer.getFloat(); // 4 bytes
            float askPrice = byteBuffer.getFloat(); // 4 bytes

            // Print Market Depth details for each packet
            System.out.println("Market Depth Packet " + (i + 1) + ":");
            System.out.println("  Bid Quantity: " + bidQuantity);
            System.out.println("  Ask Quantity: " + askQuantity);
            System.out.println("  Number of Bid Orders: " + numberOfBidOrders);
            System.out.println("  Number of Ask Orders: " + numberOfAskOrders);
            System.out.println("  Bid Price: " + bidPrice);
            System.out.println("  Ask Price: " + askPrice);
        }

        // Print all other values
        System.out.println("Latest Traded Price (LTP): " + latestTradedPrice);
        System.out.println("Last Traded Quantity: " + lastTradedQuantity);
        System.out.println("Last Trade Time (LTT): " + lastTradeTime);
        System.out.println("Average Trade Price (ATP): " + averageTradePrice);
        System.out.println("Volume: " + volume);
        System.out.println("Total Sell Quantity: " + totalSellQuantity);
        System.out.println("Total Buy Quantity: " + totalBuyQuantity);
        System.out.println("Open Interest: " + openInterest);
        System.out.println("Highest Open Interest: " + highestOpenInterest);
        System.out.println("Lowest Open Interest: " + lowestOpenInterest);
        System.out.println("Day Open Value: " + dayOpenValue);
        System.out.println("Day Close Value: " + dayCloseValue);
        System.out.println("Day High Value: " + dayHighValue);
        System.out.println("Day Low Value: " + dayLowValue);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
    	System.err.println("Dhan gives response in binary format. Text message is not expected from Dhan.");
	}

	protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
		System.out.println("Ping Pong");
	}

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.err.println("Error: " + exception.getMessage());
    }

	@Override
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		System.out.println("Connection closed.");
	}
}

output:

Connected to Dhan WebSocket
Sent subscription request in binary format
e[2m2024-11-04T09:58:24.320+05:30e[0;39m e[32m INFOe[0;39m e[35m18616e[0;39m e[2m---e[0;39m e[2m[tradingapp] [cTaskExecutor-1]e[0;39m e[2me[0;39me[36mo.s.w.s.c.WebSocketConnectionManager    e[0;39m e[2m:e[0;39m Successfully connected
Feed Response Code: 50
Message Length: 2560
Exchange Segment: 0
Security ID: 0
Received Disconnect Packet with Code: 9731
Connection closed.

Hello @sajos

9731 is not a code sent by our server here. On V2, you do not need to send any data in binary. You can directly send JSON message on the socket.

Hi @Hardik,
Updated the code and now I’m sending the subscription message as given below.

@Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        this.session = session;
        System.out.println("Connected to Dhan WebSocket");

        // Example: Sending a subscription request in JSON format
        String subscriptionMessage = "{"
            + "\"RequestCode\": 15,"
            + "\"InstrumentCount\": 2,"
            + "\"InstrumentList\": ["
            + "  {\"ExchangeSegment\": \"NSE_EQ\", \"SecurityId\": \"1333\"},"//HDFCBANK
            + "  {\"ExchangeSegment\": \"BSE_EQ\", \"SecurityId\": \"532540\"}"//TCS
            + "]}";
        System.out.println("subscriptionMessage : " +subscriptionMessage);
        // Send TextMessage
        session.sendMessage(new TextMessage(subscriptionMessage));
        System.out.println("Sent subscription request in JSON format");
    }

But I’m geting disconnected now with feed response code 50. What could be the reason/

Hello @sajos

If you are getting disconnected with feed response code as 50, then I would suggest to print the entire message, so as to see the reason for disconnection. You can find more details here: Live Market Feed - DhanHQ Ver 2.0 / API Document

Hi @Hardik,

I generated a new token and the response code 50 issue is resolved even though i was not able to read the diconnection code.

Now I am getting another error.
After subscribing to the instruments, I’m getting the below text message(So sometimes Dhan gives response as text also?)

@Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        this.session = session;
        System.out.println("Connected to Dhan WebSocket");

        // Example: Sending a subscription request in binary format
        String subscriptionMessage = "{"
            + "\"RequestCode\": 15,"
            + "\"InstrumentCount\": 2,"
            + "\"InstrumentList\": ["
            + "  {\"ExchangeSegment\": \"NSE_EQ\", \"SecurityId\": \"1333\"}," // HDFCBANK
            + "  {\"ExchangeSegment\": \"BSE_EQ\", \"SecurityId\": \"532540\"}" // TCS
            + "]}";
        System.out.println("subscriptionMessage: " + subscriptionMessage);
        session.sendMessage(new TextMessage(subscriptionMessage));
        System.out.println("Sent subscription request in JSON format");
    }
@Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        System.out.println(message.getPayload());// prints Invalid Input Parameters
    }

After subscription, call comes to handleTextMessage() and getting “Invalid Input Parameters”

Connection is closed after this. Can you help?

Also if you have a sample code to get the feed, can you post here?

@rahulbajaj ,
Can you post sample code to get the feed if it is working for you?