DhanHQ Go Client
A comprehensive Go client library for the DhanHQ Trading API
Features
- 🚀 Complete API Coverage - All DhanHQ v2 APIs supported
- 📊 Trading APIs - Orders, Super Orders (Bracket/Cover), Forever Orders (GTT/GTC)
- 📈 Market Data - Quotes, Historical Data, Option Chains, Market Depth
- 🔌 WebSocket Support - Real-time market feed & order updates
- 🔄 Auto-Reconnect - Resilient WebSocket connections with exponential backoff
- 📝 Structured Logging - Uber Zap integration for production-grade logging
- ⚡ Rate Limiting - Built-in rate limiting to respect API limits
- 🛡️ Type Safe - Strongly typed models for all API responses
Installation
go get github.com/27by10/dhanHQ
Quick Start
package main
import (
"context"
"fmt"
"time"
dhan "github.com/27by10/dhanHQ"
"github.com/27by10/dhanHQ/models"
)
func main() {
// Create client
client := dhan.New("your-client-id", "your-access-token")
defer client.Close()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
// Get fund limits
funds, _ := client.Funds.GetFundLimits(ctx)
fmt.Printf("Available Balance: ₹%.2f\n", funds.AvailableBalance)
// Get holdings
holdings, _ := client.Portfolio.GetHoldings(ctx)
fmt.Printf("Total Holdings: %d\n", len(holdings))
// Place an order
order, _ := client.Orders.Place(ctx, &models.OrderRequest{
TransactionType: models.TransactionBuy,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductCNC,
OrderType: models.OrderTypeLimit,
Validity: models.ValidityDay,
SecurityID: "1333", // HDFC Bank
Quantity: 1,
Price: 1500.00,
})
fmt.Printf("Order ID: %s\n", order.OrderID)
}
Documentation
Table of Contents
Authentication
Get your API credentials from the Dhan Developer Portal.
// Basic initialization
client := dhan.New("client-id", "access-token")
// With custom configuration
cfg := config.DefaultConfig("client-id", "access-token").
WithDebug(true).
WithTimeout(60 * time.Second)
client := dhan.NewWithConfig(cfg)
Trading APIs
Orders
// Place a limit order
order, err := client.Orders.Place(ctx, &models.OrderRequest{
TransactionType: models.TransactionBuy,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductCNC,
OrderType: models.OrderTypeLimit,
Validity: models.ValidityDay,
SecurityID: "1333",
Quantity: 10,
Price: 1500.00,
})
// Place a market order
order, err := client.Orders.Place(ctx, &models.OrderRequest{
TransactionType: models.TransactionBuy,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductIntraday,
OrderType: models.OrderTypeMarket,
Validity: models.ValidityDay,
SecurityID: "2885", // Reliance
Quantity: 5,
})
// Modify an order
modified, err := client.Orders.Modify(ctx, orderID, &models.OrderModifyRequest{
OrderType: models.OrderTypeLimit,
Quantity: 5,
Price: 1510.00,
Validity: models.ValidityDay,
})
// Cancel an order
cancelled, err := client.Orders.Cancel(ctx, orderID)
// Get all orders
orders, err := client.Orders.List(ctx)
// Get order by ID
order, err := client.Orders.Get(ctx, orderID)
// Get order by correlation ID
order, err := client.Orders.GetByCorrelationID(ctx, "my-correlation-id")
// Get all trades
trades, err := client.Orders.GetTrades(ctx)
// Get trades for an order
trades, err := client.Orders.GetTradesByOrderID(ctx, orderID)
Super Orders (Bracket/Cover)
// Place a bracket order
order, err := client.SuperOrders.Place(ctx, &models.SuperOrderRequest{
TransactionType: models.TransactionBuy,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductBO,
OrderType: models.OrderTypeLimit,
Validity: models.ValidityDay,
SecurityID: "1333",
Quantity: 10,
Price: 1500.00,
StopLossPrice: 1480.00,
TargetPrice: 1530.00,
})
// Place a cover order
order, err := client.SuperOrders.Place(ctx, &models.SuperOrderRequest{
TransactionType: models.TransactionBuy,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductCO,
OrderType: models.OrderTypeMarket,
Validity: models.ValidityDay,
SecurityID: "1333",
Quantity: 10,
StopLossPrice: 1480.00,
})
// Modify a leg
modified, err := client.SuperOrders.ModifyLeg(ctx, orderID, &models.SuperOrderModifyRequest{
LegName: models.LegStopLoss,
Price: 1475.00,
Quantity: 10,
OrderType: models.OrderTypeLimit,
Validity: models.ValidityDay,
})
// Cancel super order
cancelled, err := client.SuperOrders.Cancel(ctx, orderID)
Forever Orders (GTT/GTC)
// Create a GTT order (single trigger)
order, err := client.ForeverOrders.Create(ctx, &models.ForeverOrderRequest{
TransactionType: models.TransactionBuy,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductCNC,
OrderType: models.OrderTypeLimit,
Validity: models.ValidityDay,
SecurityID: "1333",
Quantity: 10,
Price: 1450.00,
TriggerPrice: 1455.00,
OrderFlag: models.ForeverOrderSingle,
})
// Create an OCO order (One Cancels Other)
order, err := client.ForeverOrders.Create(ctx, &models.ForeverOrderRequest{
TransactionType: models.TransactionSell,
ExchangeSegment: models.ExchangeNSE,
ProductType: models.ProductCNC,
OrderType: models.OrderTypeLimit,
Validity: models.ValidityDay,
SecurityID: "1333",
Quantity: 10,
Price: 1550.00, // Target
TriggerPrice: 1545.00,
OrderFlag: models.ForeverOrderOCO,
Price1: 1450.00, // Stop loss
TriggerPrice1: 1455.00,
Quantity1: 10,
})
// List forever orders
orders, err := client.ForeverOrders.List(ctx)
// Cancel forever order
cancelled, err := client.ForeverOrders.Cancel(ctx, orderID)
Portfolio
// Get holdings
holdings, err := client.Portfolio.GetHoldings(ctx)
for _, h := range holdings {
fmt.Printf("%s: %d shares @ ₹%.2f (PNL: ₹%.2f)\n",
h.TradingSymbol, h.TotalQuantity, h.AveragePrice, h.PNL)
}
// Get positions
positions, err := client.Portfolio.GetPositions(ctx)
for _, p := range positions {
fmt.Printf("%s: %d qty (Day PNL: ₹%.2f)\n",
p.TradingSymbol, p.NetQuantity, p.DayPNL)
}
// Convert position (e.g., intraday to delivery)
err := client.Portfolio.ConvertPosition(ctx, &models.ConvertPositionRequest{
FromProductType: models.ProductIntraday,
ToProductType: models.ProductCNC,
ExchangeSegment: models.ExchangeNSE,
PositionType: models.PositionLong,
SecurityID: "1333",
TradingSymbol: "HDFCBANK",
Quantity: 10,
})
Funds
// Get fund limits
funds, err := client.Funds.GetFundLimits(ctx)
fmt.Printf("Available: ₹%.2f\n", funds.AvailableBalance)
fmt.Printf("Utilized: ₹%.2f\n", funds.UtilizedAmount)
// Calculate margin for an order
margin, err := client.Funds.CalculateMargin(ctx, &models.MarginCalculatorRequest{
ExchangeSegment: models.ExchangeNSE,
TransactionType: models.TransactionBuy,
Quantity: 100,
ProductType: models.ProductIntraday,
SecurityID: "1333",
Price: 1500.00,
})
fmt.Printf("Required Margin: ₹%.2f\n", margin.TotalMargin)
Data APIs
Market Quotes
// Build quote request using fluent builder
req := data.NewQuoteRequest().
AddNSE("1333", "2885"). // HDFC Bank, Reliance
AddNSEFNO("35001"). // NIFTY Future
AddNSEIndex("13") // NIFTY 50
// Get LTP
ltpQuotes, err := client.Quote.GetLTP(ctx, req)
for secID, quote := range ltpQuotes {
fmt.Printf("%s: ₹%.2f\n", secID, quote.LastTradedPrice)
}
// Get OHLC
ohlcQuotes, err := client.Quote.GetOHLC(ctx, req)
// Get full quote (with depth, OI, etc.)
fullQuotes, err := client.Quote.GetFullQuote(ctx, req)
for secID, quote := range fullQuotes {
fmt.Printf("%s: LTP=%.2f Change=%.2f%% Vol=%d\n",
secID, quote.LastTradedPrice, quote.ChangePercent, quote.Volume)
}
// Single security convenience methods
ltp, err := client.Quote.GetLTPSingle(ctx, models.ExchangeNSE, "1333")
Historical Data
// Get daily candles
candles, err := client.Historical.GetDaily(ctx,
"1333", // Security ID
models.ExchangeNSE, // Exchange
data.InstrumentEquity, // Instrument type
"2024-01-01", // From date
"2024-01-31", // To date
)
for _, c := range candles {
fmt.Printf("%s: O=%.2f H=%.2f L=%.2f C=%.2f V=%d\n",
c.Timestamp.Format("2006-01-02"),
c.Open, c.High, c.Low, c.Close, c.Volume)
}
// Get intraday candles (5-minute)
candles, err := client.Historical.GetIntraday(ctx,
"1333",
models.ExchangeNSE,
data.InstrumentEquity,
models.Resolution5Min,
"2024-01-15",
"2024-01-15",
)
// Convenience methods
candles, err := client.Historical.GetDailyLastNDays(ctx, "1333", models.ExchangeNSE, data.InstrumentEquity, 30)
candles, err := client.Historical.GetIntradayToday(ctx, "1333", models.ExchangeNSE, data.InstrumentEquity, models.Resolution5Min)
Option Chain
// Get expiry dates
expiries, err := client.Options.GetExpiryList(ctx, "13", models.ExchangeNFO)
fmt.Printf("Expiries: %v\n", expiries)
// Get option chain
chain, err := client.Options.GetOptionChain(ctx, "13", models.ExchangeNFO, expiries[0])
fmt.Printf("Underlying: ₹%.2f\n", chain.UnderlyingPrice)
fmt.Printf("Total Strikes: %d\n", len(chain.Chain))
// Get ATM strike
atmStrike := data.GetATMStrike(chain)
// Filter strikes around ATM
filtered := data.FilterOptionChain(chain, &data.OptionChainFilter{
StrikesAroundATM: 5,
})
// Get ITM/OTM strikes
itmCalls := data.GetITMStrikes(chain, models.OptionCall, 3)
otmPuts := data.GetOTMStrikes(chain, models.OptionPut, 3)
Market Depth
// Get market depth (20 levels)
req := data.NewQuoteRequest().AddNSE("1333")
depths, err := client.Depth.GetMarketDepth(ctx, req)
for secID, depth := range depths {
// Analyze depth
analysis := data.AnalyzeDepth(&depth)
fmt.Printf("%s:\n", secID)
fmt.Printf(" Bid/Ask Ratio: %.2f\n", analysis.BidAskRatio)
fmt.Printf(" Spread: ₹%.2f (%.3f%%)\n", analysis.Spread, analysis.SpreadPercentage)
fmt.Printf(" Imbalance: %.2f\n", analysis.Imbalance)
}
// Single security
depth, err := client.Depth.GetMarketDepthSingle(ctx, models.ExchangeNSE, "1333")
midPrice := data.GetMidPrice(depth)
vwap := data.GetVWAP(depth, 5) // Top 5 levels
Instruments
// Download and cache instrument list
err := client.LoadInstruments(ctx)
// Or load specific segments
err := client.LoadEquityInstruments(ctx)
err := client.LoadDerivativeInstruments(ctx)
// Search instruments
instruments := client.Instruments.SearchBySymbol("RELIANCE")
instruments := client.Instruments.SearchByName("Reliance Industries")
// Get by exchange
nseInstruments := client.Instruments.GetByExchange(models.ExchangeNSE)
// Get futures/options
futures := client.Instruments.GetFutures("NIFTY")
options := client.Instruments.GetOptions("BANKNIFTY", models.OptionCall)
// Get specific instrument
inst, found := client.Instruments.GetInstrument(models.ExchangeNSE, "1333")
WebSocket
Live Market Feed
// Set up tick handler
client.LiveFeed.OnTick(func(tick *models.TickData) {
fmt.Printf("%s: ₹%.2f Vol=%d\n",
tick.SecurityID,
tick.LastTradedPrice,
tick.Volume)
})
// Connection callbacks
client.LiveFeed.OnConnect(func() {
fmt.Println("Connected!")
// Subscribe to instruments
client.LiveFeed.SubscribeBySecurityID(
models.ExchangeNSE,
[]string{"1333", "2885", "11536"},
websocket.FeedModeQuote,
)
})
client.LiveFeed.OnDisconnect(func(err error) {
fmt.Printf("Disconnected: %v\n", err)
})
// Connect
err := client.ConnectLiveFeed(ctx)
// Subscribe modes
websocket.FeedModeLTP // Only LTP
websocket.FeedModeQuote // LTP + OHLC + Volume
websocket.FeedModeFull // Full market depth
// Unsubscribe
client.LiveFeed.UnsubscribeBySecurityID(models.ExchangeNSE, []string{"1333"})
Order Updates
// Use the typed handler
handler := websocket.NewOrderUpdateHandler()
handler.OnOrderPlaced = func(update *models.OrderUpdateData) {
fmt.Printf("Order placed: %s\n", update.OrderID)
}
handler.OnOrderFilled = func(update *models.OrderUpdateData) {
fmt.Printf("Order filled: %s @ ₹%.2f\n",
update.TradingSymbol, update.AveragePrice)
}
handler.OnOrderRejected = func(update *models.OrderUpdateData) {
fmt.Printf("Order rejected: %s - %s\n",
update.TradingSymbol, update.Remarks)
}
client.OrderUpdate.OnOrderUpdate(handler.HandleUpdate)
// Connect
err := client.ConnectOrderUpdate(ctx)
// Or connect both feeds at once
err := client.ConnectAll(ctx)
Advanced WebSocket Features
// Tick Aggregator - Build OHLC candles from ticks
aggregator := websocket.NewTickAggregator(time.Minute, func(secID string, candle *models.Candle) {
fmt.Printf("1m Candle [%s]: O=%.2f H=%.2f L=%.2f C=%.2f\n",
secID, candle.Open, candle.High, candle.Low, candle.Close)
})
aggregator.Start()
defer aggregator.Stop()
client.LiveFeed.OnTick(aggregator.HandleTick)
// Tick Stats - Track statistics
stats := websocket.NewTickStats()
client.LiveFeed.OnTick(stats.Update)
// Get stats
s := stats.Get("1333")
fmt.Printf("Ticks: %d, High: %.2f, Low: %.2f, VWAP: %.2f\n",
s.TickCount, s.HighPrice, s.LowPrice, s.VWAP)
// Price Alerts
alerts := websocket.NewAlertManager()
alerts.AddAlert(&websocket.PriceAlert{
SecurityID: "1333",
Price: 1600.00,
Condition: websocket.AlertAbove,
Callback: func(tick *models.TickData) {
fmt.Println("Price alert triggered!")
},
})
client.LiveFeed.OnTick(alerts.HandleTick)
Error Handling
import "github.com/27by10/dhanHQ/pkg/errors"
order, err := client.Orders.Place(ctx, req)
if err != nil {
// Check error type
if errors.IsAuthError(err) {
log.Fatal("Authentication failed - check your credentials")
}
if errors.IsRateLimitError(err) {
log.Println("Rate limited - waiting...")
time.Sleep(time.Second)
// Retry
}
if errors.IsOrderRejectedError(err) {
if apiErr, ok := err.(*errors.APIError); ok {
log.Printf("Order rejected: %s - %s\n", apiErr.Code, apiErr.Message)
}
}
if errors.IsNotFoundError(err) {
log.Println("Resource not found")
}
}
Configuration
import "github.com/27by10/dhanHQ/config"
// Default configuration
cfg := config.DefaultConfig("client-id", "access-token")
// Customize
cfg.WithDebug(true) // Enable debug logging
cfg.WithTimeout(60 * time.Second) // Set HTTP timeout
cfg.WithBaseURL("https://custom.url") // Custom base URL (for testing)
// Create client with config
client := dhan.NewWithConfig(cfg)
// Rate limits (built-in)
// - Order APIs: 25/sec, 250/min, 7000/day
// - Data APIs: 5/sec, 100000/day
// - Quote APIs: 1/sec
// - Non-Trading APIs: 20/sec
Logging
The library uses Uber Zap for structured logging.
import "github.com/27by10/dhanHQ/pkg/logger"
// Initialize with custom config
logger.Init(&logger.Config{
Level: "debug", // debug, info, warn, error
Development: true, // Pretty console output
Encoding: "console", // json or console
})
// Use global logger
logger.Info("Message", zap.String("key", "value"))
logger.Error("Error occurred", zap.Error(err))
Examples
See the cmd/ directory for complete examples:
- cmd/example/ - Basic API usage
- cmd/websocket_example/ - Real-time streaming
# Run basic example
go run ./cmd/example/
# Run WebSocket example
go run ./cmd/websocket_example/
API Reference
Full API documentation is available at pkg.go.dev.
Packages
| Package |
Description |
dhanHQ |
Main client package |
config |
Configuration types and constants |
models |
All request/response models |
trading |
Trading API services |
data |
Market data API services |
websocket |
WebSocket services |
pkg/logger |
Logging utilities |
pkg/errors |
Error types and helpers |
pkg/httpclient |
HTTP client wrapper |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature)
- Commit your changes (
git commit -m 'Add some amazing feature')
- Push to the branch (
git push origin feature/amazing-feature)
- Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Disclaimer
This library is not officially affiliated with Dhan. Use at your own risk. Always test thoroughly in a paper trading environment before using with real money.
Support