Dhan API - Java implementation of data apis

Hello @sajos

This looks like error in the Java application that you have written. We have sample code in Python, if you want to use that as reference.

@Hardik ,
The message "Invalid Input Parameters" is coming from the server, not from Java code.
Context of handleTextMessage: The handleTextMessage method is springboot implementation of WebSocketHandler to receive and handle messages sent from the WebSocket server.

Hello @sajos

Can you check the authorisation here then, are you on V1 or V2 while connecting to socket?

Heyy

you can try extending WebsocketClient

@Override
public void onOpen(ServerHandshake handshake) {

	if(feedRequest == null) {
    	// Feed request example (subscribe to NSE segment and specific scrip)
        feedRequest = "{\r\n"
        		+ "    \"RequestCode\" : 15,\r\n"
        		+ "    \"InstrumentCount\" : 2,\r\n"
        		+ "    \"InstrumentList\" : [\r\n"
        		+ "        {\r\n"
        		+ "            \"ExchangeSegment\" : \"NSE_EQ\",\r\n"
        		+ "            \"SecurityId\" : \"1333\"\r\n"
        		+ "        },\r\n"
        		+ "        {\r\n"
        		+ "            \"ExchangeSegment\" : \"BSE_EQ\",\r\n"
        		+ "            \"SecurityId\" : \"532540\"\r\n"
        		+ "        }\r\n"
        		+ "    ]\r\n"
        		+ "}\r\n"
        		+ "";
        send(feedRequest);
        System.out.println("Connected to WebSocket server with Test Json.");
        System.out.println("Sent feed request: " + feedRequest);
	}else {
		send(feedRequest);
        System.out.println("Connected to WebSocket server with Passed Json.");
        System.out.println("Sent feed request: " + feedRequest);
	}   

}

@Override
public void onMessage(ByteBuffer bytes) {

	TickerData tickerData = parseTickerData(bytes);
	System.out.println(tickerData);

}

and Thanks your code really help me.

Hi @Hardik and @rahulbajaj,

Thank you for your clarifications; my code is now working.

The issue was due to a mistake while updating the new token. I missed the &token parameter name when updating it, which caused the “Invalid input parameters” error.

Below is the working code for Version 2 for others to refer to if they encounter any issues.

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.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.ByteOrder;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

@SpringBootApplication
public class TradingApplication {

    // Define client ID, token, and request code as static variables
    private static final String CLIENT_ID = "xxxxxxx";
    private static final String TOKEN = "xxxxxxxx";
    

    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=" + TOKEN
                + "&clientId=" + CLIENT_ID
                + "&authType=2";

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

// A class for handling WebSocket communication and subscription messages
class DhanWebSocketHandler extends AbstractWebSocketHandler {

    private static final int MAX_INSTRUMENTS_PER_REQUEST = 100;
    private static final int REQUEST_CODE = 15;

 // Static map for instrument information with Security ID as the key
    private static final Map<String, InstrumentInfo> INSTRUMENTS = new LinkedHashMap<>();

    static {
        INSTRUMENTS.put("1333", new InstrumentInfo("HDFCBANK", "1333", "NSE_EQ"));//NSE stock
        INSTRUMENTS.put("532540", new InstrumentInfo("TCS", "532540", "BSE_EQ"));//BSE stock
        INSTRUMENTS.put("19237", new InstrumentInfo("MONIFTY500", "19237", "NSE_EQ"));//Motilal Oswal Nifty 500 ETF
        INSTRUMENTS.put("25", new InstrumentInfo("BANKNIFTY", "25", "IDX_I"));//Index
        INSTRUMENTS.put("43972", new InstrumentInfo("NIFTY 14 NOV 24500 CALL", "43972", "NSE_FNO"));//Options
        // Add more instruments as needed
    }

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

    private void sendSubscriptionMessages(WebSocketSession session) throws Exception {
        List<Map.Entry<String, InstrumentInfo>> instrumentList = new ArrayList<>(INSTRUMENTS.entrySet());
        
        for (int i = 0; i < instrumentList.size(); i += MAX_INSTRUMENTS_PER_REQUEST) {
            int end = Math.min(i + MAX_INSTRUMENTS_PER_REQUEST, instrumentList.size());
            List<Map.Entry<String, InstrumentInfo>> batch = instrumentList.subList(i, end);
            String subscriptionMessage = createSubscriptionMessage(batch);
            session.sendMessage(new TextMessage(subscriptionMessage));
            System.out.println("Sent subscription request for batch: " + (i / MAX_INSTRUMENTS_PER_REQUEST + 1));
        }
    }

    private String createSubscriptionMessage(List<Map.Entry<String, InstrumentInfo>> instruments) {
        StringBuilder instrumentListJson = new StringBuilder();
        for (Map.Entry<String, InstrumentInfo> entry : instruments) {
            InstrumentInfo info = entry.getValue();
            instrumentListJson.append("{\"ExchangeSegment\": \"").append(info.getExchangeSegment())
                    .append("\", \"SecurityId\": \"").append(entry.getKey()).append("\"},");
        }
        // Remove trailing comma
        if (instrumentListJson.length() > 0) {
            instrumentListJson.setLength(instrumentListJson.length() - 1);
        }

        return "{"
                + "\"RequestCode\": " + REQUEST_CODE + ","
                + "\"InstrumentCount\": " + instruments.size() + ","
                + "\"InstrumentList\": [" + instrumentListJson + "]"
                + "}";
    }

    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) throws Exception {
        ByteBuffer byteBuffer = message.getPayload();
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // Set byte order if required by API

        byte[] responseHeader = new byte[8];
        byteBuffer.get(responseHeader);
        byte feedResponseCode = responseHeader[0];
        
        if (feedResponseCode == 50) {
            short disconnectCode = byteBuffer.getShort();
            System.out.println("Disconnection Code: " + disconnectCode);
        } else if (feedResponseCode == 2) {
            String securityId = extractSecurityId(responseHeader);
            String instrumentName = INSTRUMENTS.get(securityId).getInstrumentName();
            PacketParser.parseTickerPacket(byteBuffer, instrumentName);
        }
    }

    private String extractSecurityId(byte[] responseHeader) {
        // Extract the Security ID from the header
        return String.valueOf(ByteBuffer.wrap(responseHeader, 4, 4).order(ByteOrder.LITTLE_ENDIAN).getInt());
    }

    @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.");
    }
}

// A helper class to store instrument information
class InstrumentInfo {
    private final String instrumentName;
    private final String exchangeSegment;
    private final String securityId;

    public InstrumentInfo(String instrumentName, String securityId, String exchangeSegment) {
        this.instrumentName = instrumentName;
        this.securityId = securityId;
        this.exchangeSegment = exchangeSegment;
    }

    public String getInstrumentName() {
        return instrumentName;
    }

    public String getExchangeSegment() {
        return exchangeSegment;
    }

    public String getSecurityId() {
        return securityId;
    }
}

// A separate class for parsing packets
class PacketParser {
    private static final DecimalFormat priceFormat = new DecimalFormat("#.##");
    private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
            .withZone(ZoneId.systemDefault());

    public static void parseTickerPacket(ByteBuffer byteBuffer, String instrumentName) {
        float lastTradedPrice = byteBuffer.getFloat();
        int lastTradeTime = byteBuffer.getInt();
        
        String formattedDate = formatTradeTime(lastTradeTime);
        System.out.println("Instrument: " + instrumentName + ", Last Traded Price: " + priceFormat.format(lastTradedPrice) +
                           ", Last Trade Time: " + formattedDate);
    }

    private static String formatTradeTime(int lastTradeTime) {
        Instant instant = Instant.ofEpochMilli((long) lastTradeTime * 1000);
        return dateFormatter.format(instant);
    }
}

Output:

Connected to Dhan WebSocket
Sent subscription request for batch: 1
e[2m2024-11-12T12:25:49.975+05:30e[0;39m e[32m INFOe[0;39m e[35m11460e[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
Instrument: HDFCBANK, Last Traded Price: 1740, Last Trade Time: 2024-11-12 17:55:49
Instrument: TCS, Last Traded Price: 4190, Last Trade Time: 2024-11-12 17:55:32
Instrument: MONIFTY500, Last Traded Price: 23, Last Trade Time: 2024-11-12 17:54:34
Instrument: BANKNIFTY, Last Traded Price: 51717.2, Last Trade Time: 2024-11-12 17:55:49
Instrument: NIFTY 14 NOV 24500 CALL, Last Traded Price: 12.85, Last Trade Time: 2024-11-12 17:55:48
Instrument: BANKNIFTY, Last Traded Price: 51714.35, Last Trade Time: 2024-11-12 17:55:49
Instrument: NIFTY 14 NOV 24500 CALL, Last Traded Price: 12.85, Last Trade Time: 2024-11-12 17:55:49
Instrument: HDFCBANK, Last Traded Price: 1740, Last Trade Time: 2024-11-12 17:55:50
Instrument: BANKNIFTY, Last Traded Price: 51716.5, Last Trade Time: 2024-11-12 17:55:50
Instrument: BANKNIFTY, Last Traded Price: 51715.7, Last Trade Time: 2024-11-12 17:55:50
2 Likes

@Hardik ,
One doubt, the last trade time given in millis is in GMT or IST?

It is in IST only, you just need to unpack it to UTC format.

hey, just saw your response. I am wondering if you would print complete raw data coming from server side. Do they also have day open for different strike price of nifty.

Instrument: NIFTY 14 NOV 24500 CALL, Last Traded Price: 12.85, Last Trade Time: 2024-11-12 17:55:49

Can you please check in Json response that if it has day open price for strike price of nifty. not for the nifty 50 index but for strike price like 23500 CE. Json has open price or not

Hi,

I’ve started exploring dhanAPI’s. I’ve subscribed to Dhan Data API’s. Able to fetch historical data, option data, etc. I’ve now started experimenting with websockets, live market feed. Saw this message and copy pasted the code in my spring boot app.

So, I’m getting a 50 feed response from server and below is the logs. Ran the same during market hours.

Request:

{“InstrumentCount”:1,“InstrumentList”:[{“SecurityId”:“52527”,“ExchangeSegment”:“NSE_FNO”}],“RequestCode”:15}

Sent subscription request for batch: 1
2024-12-29T14:56:28.568+05:30 INFO 25827 — [dhan-trading] [cTaskExecutor-1] o.s.w.s.c.WebSocketConnectionManager : Successfully connected
Disconnection Code: 808
Connection closed.

Response header byte values: [50, 10, 0, 0, 0, 0, 0, 0]

Never mind: The above code works, I was passing wrong clientId. This has to be the DHAN clientId, instead I was passing the App client which I had registered on DHAN.

1 Like