DhanHQ 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -1
- data/CODE_REVIEW_ISSUES.md +2 -2
- data/GUIDE.md +2 -2
- data/README.md +194 -741
- data/REVIEW_SUMMARY.md +2 -2
- data/{README1.md → docs/ARCHIVE_README.md} +4 -4
- data/docs/AUTHENTICATION.md +116 -2
- data/docs/CONFIGURATION.md +109 -0
- data/docs/SUPER_ORDERS.md +284 -0
- data/docs/TESTING_GUIDE.md +8 -8
- data/docs/TROUBLESHOOTING.md +117 -0
- data/docs/WEBSOCKET_PROTOCOL.md +154 -0
- data/docs/live_order_updates.md +2 -2
- data/docs/rails_integration.md +7 -7
- data/docs/standalone_ruby_websocket_integration.md +24 -24
- data/docs/technical_analysis.md +1 -1
- data/docs/websocket_integration.md +4 -4
- data/examples/comprehensive_websocket_examples.rb +2 -2
- data/examples/instrument_finder_test.rb +2 -2
- data/examples/market_depth_example.rb +2 -2
- data/examples/market_feed_example.rb +2 -2
- data/examples/order_update_example.rb +2 -2
- data/examples/trading_fields_example.rb +2 -2
- data/lib/DhanHQ/auth/token_generator.rb +33 -0
- data/lib/DhanHQ/auth/token_manager.rb +88 -0
- data/lib/DhanHQ/auth/token_renewal.rb +25 -0
- data/lib/DhanHQ/auth.rb +91 -31
- data/lib/DhanHQ/client.rb +42 -2
- data/lib/DhanHQ/configuration.rb +2 -2
- data/lib/DhanHQ/contracts/order_contract.rb +0 -23
- data/lib/DhanHQ/contracts/trade_by_order_id_contract.rb +12 -0
- data/lib/DhanHQ/contracts/trade_contract.rb +0 -65
- data/lib/DhanHQ/contracts/trade_history_contract.rb +52 -0
- data/lib/DhanHQ/core/auth_api.rb +21 -0
- data/lib/DhanHQ/helpers/request_helper.rb +1 -1
- data/lib/DhanHQ/models/alert_order.rb +22 -0
- data/lib/DhanHQ/models/edis.rb +110 -0
- data/lib/DhanHQ/models/kill_switch.rb +22 -0
- data/lib/DhanHQ/models/margin.rb +49 -0
- data/lib/DhanHQ/models/pnl_exit.rb +130 -0
- data/lib/DhanHQ/models/position.rb +22 -0
- data/lib/DhanHQ/models/postback.rb +123 -0
- data/lib/DhanHQ/models/token_response.rb +88 -0
- data/lib/DhanHQ/resources/kill_switch.rb +8 -0
- data/lib/DhanHQ/resources/margin_calculator.rb +9 -0
- data/lib/DhanHQ/resources/pnl_exit.rb +37 -0
- data/lib/DhanHQ/resources/positions.rb +8 -0
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/dhan_hq.rb +31 -81
- metadata +46 -4
- data/lib/DhanHQ/config.rb +0 -33
data/REVIEW_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# DhanHQ Client Gem - Review Summary
|
|
2
2
|
|
|
3
|
-
**API Documentation**: https://api.dhan.co/v2/#/
|
|
3
|
+
**API Documentation**: https://api.dhan.co/v2/#/
|
|
4
4
|
**Review Date**: 2025-01-27
|
|
5
5
|
|
|
6
6
|
## 🚨 Top 10 Critical Issues
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
- **File**: `lib/DhanHQ/client.rb:40`
|
|
16
16
|
- **Issue**: Partial configuration can lead to runtime authentication failures
|
|
17
17
|
- **Impact**: Silent failures, difficult debugging
|
|
18
|
-
- **Fix**: Validate both `
|
|
18
|
+
- **Fix**: Validate both `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN` before proceeding
|
|
19
19
|
|
|
20
20
|
### 3. **Memory Leak in Order Tracker** (HIGH)
|
|
21
21
|
- **File**: `lib/DhanHQ/ws/orders/client.rb:18`
|
|
@@ -45,8 +45,8 @@ DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Lo
|
|
|
45
45
|
|
|
46
46
|
**Minimum environment variables**
|
|
47
47
|
|
|
48
|
-
* `
|
|
49
|
-
* `
|
|
48
|
+
* `DHAN_CLIENT_ID` – trading account client id issued by Dhan.
|
|
49
|
+
* `DHAN_ACCESS_TOKEN` – API access token generated from the Dhan console.
|
|
50
50
|
|
|
51
51
|
If either key is missing `configure_with_env` raises an error. Ensure your
|
|
52
52
|
application loads them into `ENV` before requiring the gem.
|
|
@@ -64,8 +64,8 @@ Create a `.env` file in your project root to supply the minimum values (and any
|
|
|
64
64
|
optional overrides you need):
|
|
65
65
|
|
|
66
66
|
```dotenv
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
DHAN_CLIENT_ID=your_client_id
|
|
68
|
+
DHAN_ACCESS_TOKEN=your_access_token
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
The gem requires `dotenv/load`, so these variables are loaded automatically when you require `DhanHQ`.
|
data/docs/AUTHENTICATION.md
CHANGED
|
@@ -23,7 +23,7 @@ Set `access_token` once; it is sent on every request:
|
|
|
23
23
|
```ruby
|
|
24
24
|
DhanHQ.configure do |config|
|
|
25
25
|
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
26
|
-
config.access_token = ENV["
|
|
26
|
+
config.access_token = ENV["DHAN_ACCESS_TOKEN"]
|
|
27
27
|
end
|
|
28
28
|
```
|
|
29
29
|
|
|
@@ -68,7 +68,7 @@ Example: refresh in provider and cache the result until near expiry:
|
|
|
68
68
|
config.access_token_provider = lambda do
|
|
69
69
|
stored = YourTokenStore.current
|
|
70
70
|
if stored.nil? || stored.expired_soon?
|
|
71
|
-
response = DhanHQ::Auth.renew_token(stored&.access_token || ENV["
|
|
71
|
+
response = DhanHQ::Auth.renew_token(stored&.access_token || ENV["DHAN_ACCESS_TOKEN"], config.client_id)
|
|
72
72
|
YourTokenStore.update!(response["accessToken"], response["expiryTime"])
|
|
73
73
|
stored = YourTokenStore.current
|
|
74
74
|
end
|
|
@@ -108,3 +108,117 @@ Rescue `AuthenticationError` for local config/token resolution failures; rescue
|
|
|
108
108
|
- [rails_integration.md](rails_integration.md) — Rails initializer with optional `access_token_provider` and RenewToken
|
|
109
109
|
- [TESTING_GUIDE.md](TESTING_GUIDE.md) — Config examples and RenewToken
|
|
110
110
|
- [CHANGELOG.md](../CHANGELOG.md) — 2.2.0 and 2.2.1 auth and token changes
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# SUPPORTED AUTHENTICATION AND TOKEN GENERATION APPROACHES:
|
|
114
|
+
|
|
115
|
+
We now support five distinct authentication approaches in this gem.
|
|
116
|
+
|
|
117
|
+
1️⃣ Static token (manual, simplest)
|
|
118
|
+
What: You paste a token you got from Dhan (web, OAuth, partner, whatever) into config.
|
|
119
|
+
How:
|
|
120
|
+
```ruby
|
|
121
|
+
DhanHQ.configure do |config|
|
|
122
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
123
|
+
config.access_token = ENV["DHAN_ACCESS_TOKEN"]
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
When: You’re OK rotating tokens manually (e.g. cron job, ops runbook).
|
|
127
|
+
|
|
128
|
+
2️⃣ Dynamic token via access_token_provider
|
|
129
|
+
What: Gem asks you for a token on every request (proc/lambda).
|
|
130
|
+
How:
|
|
131
|
+
```ruby
|
|
132
|
+
DhanHQ.configure do |config|
|
|
133
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
134
|
+
config.access_token_provider = -> { MyTokenStore.fetch_current_token }
|
|
135
|
+
config.on_token_expired = ->(error) { MyTokenStore.refresh!(error) }
|
|
136
|
+
end
|
|
137
|
+
```
|
|
138
|
+
Behavior:
|
|
139
|
+
On 401 (auth failure), client calls on_token_expired, then retries once using a fresh token from access_token_provider.
|
|
140
|
+
|
|
141
|
+
3️⃣ Fetch-from-token-endpoint (configure_from_token_endpoint)
|
|
142
|
+
What: Gem calls your HTTP endpoint once to get access_token + client_id.
|
|
143
|
+
How:
|
|
144
|
+
```ruby
|
|
145
|
+
DhanHQ.configure_from_token_endpoint(
|
|
146
|
+
base_url: "https://myapp.com",
|
|
147
|
+
bearer_token: ENV["DHAN_TOKEN_ENDPOINT_BEARER"]
|
|
148
|
+
)
|
|
149
|
+
# expects JSON: { access_token: "...", client_id: "...", base_url: "..." (optional) }
|
|
150
|
+
```
|
|
151
|
+
When: Multi-tenant or central credential service, you don’t want tokens in ENV directly.
|
|
152
|
+
|
|
153
|
+
4️⃣ TOTP-based token generation (new DhanHQ::Auth flow)
|
|
154
|
+
Module API (low-level)
|
|
155
|
+
What: Direct call to Dhan’s generateAccessToken endpoint using TOTP.
|
|
156
|
+
```ruby
|
|
157
|
+
totp = DhanHQ::Auth.generate_totp(ENV["DHAN_TOTP_SECRET"])
|
|
158
|
+
response = DhanHQ::Auth.generate_access_token(
|
|
159
|
+
dhan_client_id: ENV["DHAN_CLIENT_ID"],
|
|
160
|
+
pin: ENV["DHAN_PIN"],
|
|
161
|
+
totp: totp
|
|
162
|
+
)
|
|
163
|
+
token = response["accessToken"]
|
|
164
|
+
expiry = response["expiryTime"]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Client API (high-level, returns TokenResponse)
|
|
168
|
+
```
|
|
169
|
+
client = DhanHQ::Client.new(api_type: :order_api)
|
|
170
|
+
token = client.generate_access_token(
|
|
171
|
+
dhan_client_id: ENV["DHAN_CLIENT_ID"],
|
|
172
|
+
pin: ENV["DHAN_PIN"],
|
|
173
|
+
totp_secret: ENV["DHAN_TOTP_SECRET"] # or `totp:` if you computed it
|
|
174
|
+
)
|
|
175
|
+
# auto-applies token + client_id to `DhanHQ.configuration`
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
When: Fully automated individual setup (no manual web token generation).
|
|
179
|
+
|
|
180
|
+
5️⃣ Auto token lifecycle management (TokenManager)
|
|
181
|
+
What: Gem handles generate + renew + retry around every API call.
|
|
182
|
+
How:
|
|
183
|
+
```ruby
|
|
184
|
+
client = DhanHQ::Client.new(api_type: :order_api)
|
|
185
|
+
client.enable_auto_token_management!(
|
|
186
|
+
dhan_client_id: ENV["DHAN_CLIENT_ID"],
|
|
187
|
+
pin: ENV["DHAN_PIN"],
|
|
188
|
+
totp_secret: ENV["DHAN_TOTP_SECRET"]
|
|
189
|
+
)
|
|
190
|
+
# From now on, `client.request` auto-ensures a valid token.
|
|
191
|
+
```
|
|
192
|
+
Behavior:
|
|
193
|
+
On first use: calls TOTP TokenGenerator → applies token.
|
|
194
|
+
Before each request: ensure_valid_token!:
|
|
195
|
+
If no token → generate.
|
|
196
|
+
If needs_refresh? → TokenRenewal (POST /v2/RenewToken) with current token + dhanClientId.
|
|
197
|
+
If renewal fails with auth error → falls back to full generate.
|
|
198
|
+
|
|
199
|
+
6️⃣ Web-token renewal only (RenewToken)
|
|
200
|
+
Module API:
|
|
201
|
+
```ruby
|
|
202
|
+
response = DhanHQ::Auth.renew_token(
|
|
203
|
+
access_token: current_token,
|
|
204
|
+
client_id: ENV["DHAN_CLIENT_ID"]
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
Client / manager (high-level):
|
|
208
|
+
|
|
209
|
+
`client.renew_access_token` (returns TokenResponse, updates config).
|
|
210
|
+
`TokenManager#refresh!` internally uses Auth::TokenRenewal.
|
|
211
|
+
When: You’re using web-generated 24h tokens and want to extend them without switching to TOTP.
|
|
212
|
+
|
|
213
|
+
TL;DR
|
|
214
|
+
Manual: Static token (access_token)
|
|
215
|
+
|
|
216
|
+
Dynamic: access_token_provider (+ optional on_token_expired)
|
|
217
|
+
|
|
218
|
+
Central service: configure_from_token_endpoint
|
|
219
|
+
|
|
220
|
+
Fully automated: TOTP generate (Auth / Client#generate_access_token)
|
|
221
|
+
|
|
222
|
+
Production-grade automation: enable_auto_token_management! (generate + renew)
|
|
223
|
+
|
|
224
|
+
If you tell me your exact deployment style (single user box, multi-user SaaS, on-prem, etc.), I can tell you which one you should actually use and what to delete as overkill.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Configuration Reference
|
|
2
|
+
|
|
3
|
+
This document covers all configuration options for the DhanHQ Ruby client.
|
|
4
|
+
|
|
5
|
+
## Quick Setup
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require 'dhan_hq'
|
|
9
|
+
DhanHQ.configure_with_env
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`configure_with_env` reads `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN` from `ENV` and raises if either is missing.
|
|
13
|
+
|
|
14
|
+
## Required Environment Variables
|
|
15
|
+
|
|
16
|
+
| Variable | Purpose |
|
|
17
|
+
| -------------------- | ------------------------------------------------- |
|
|
18
|
+
| `DHAN_CLIENT_ID` | Trading account client ID issued by Dhan |
|
|
19
|
+
| `DHAN_ACCESS_TOKEN` | API access token generated from the Dhan console |
|
|
20
|
+
|
|
21
|
+
## Optional Environment Variables
|
|
22
|
+
|
|
23
|
+
Set these _before_ calling `configure_with_env` to override defaults:
|
|
24
|
+
|
|
25
|
+
| Variable | Default | Description |
|
|
26
|
+
| -------------------------------- | ---------- | ------------------------------------------------------- |
|
|
27
|
+
| `DHAN_LOG_LEVEL` | `INFO` | Logger verbosity (`DEBUG`, `INFO`, `WARN`, `ERROR`) |
|
|
28
|
+
| `DHAN_BASE_URL` | Dhan prod | Point REST calls to a different API hostname |
|
|
29
|
+
| `DHAN_WS_VERSION` | latest | Pin WebSocket connections to a specific API version |
|
|
30
|
+
| `DHAN_WS_ORDER_URL` | Dhan prod | Override the order update WebSocket endpoint |
|
|
31
|
+
| `DHAN_WS_USER_TYPE` | `SELF` | Switch between `SELF` and `PARTNER` streaming modes |
|
|
32
|
+
| `DHAN_PARTNER_ID` | — | Required when `DHAN_WS_USER_TYPE=PARTNER` |
|
|
33
|
+
| `DHAN_PARTNER_SECRET` | — | Required when `DHAN_WS_USER_TYPE=PARTNER` |
|
|
34
|
+
| `DHAN_CONNECT_TIMEOUT` | `10` | Connection timeout in seconds |
|
|
35
|
+
| `DHAN_READ_TIMEOUT` | `30` | Read timeout in seconds |
|
|
36
|
+
| `DHAN_WRITE_TIMEOUT` | `30` | Write timeout in seconds |
|
|
37
|
+
| `DHAN_WS_MAX_TRACKED_ORDERS` | `10000` | Maximum orders to track in WebSocket |
|
|
38
|
+
| `DHAN_WS_MAX_ORDER_AGE` | `604800` | Maximum order age in seconds before cleanup (7 days) |
|
|
39
|
+
|
|
40
|
+
## `.env` File Setup
|
|
41
|
+
|
|
42
|
+
Create a `.env` file in your project root:
|
|
43
|
+
|
|
44
|
+
```dotenv
|
|
45
|
+
DHAN_CLIENT_ID=your_client_id
|
|
46
|
+
DHAN_ACCESS_TOKEN=your_access_token
|
|
47
|
+
|
|
48
|
+
# Optional overrides
|
|
49
|
+
DHAN_LOG_LEVEL=DEBUG
|
|
50
|
+
DHAN_CONNECT_TIMEOUT=15
|
|
51
|
+
DHAN_READ_TIMEOUT=60
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The gem requires `dotenv/load`, so these variables are loaded automatically when you require `dhan_hq`.
|
|
55
|
+
|
|
56
|
+
## Block-Style Configuration
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
DhanHQ.configure do |config|
|
|
60
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
61
|
+
config.access_token = ENV["DHAN_ACCESS_TOKEN"]
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Logging
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Set `DHAN_LOG_LEVEL=DEBUG` for full HTTP request/response and WebSocket frame logging during development.
|
|
72
|
+
|
|
73
|
+
## Dynamic Access Token
|
|
74
|
+
|
|
75
|
+
For production or OAuth-style flows, resolve the token at **request time**:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
DhanHQ.configure do |config|
|
|
79
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
80
|
+
config.access_token_provider = lambda do
|
|
81
|
+
token = YourTokenStore.active_token # e.g. from DB or OAuth
|
|
82
|
+
raise "Token expired or missing" unless token
|
|
83
|
+
token
|
|
84
|
+
end
|
|
85
|
+
# Optional: called when the API returns 401/token-expired
|
|
86
|
+
config.on_token_expired = ->(error) { YourTokenStore.refresh! }
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- **`access_token_provider`**: Callable (Proc/lambda) returning the token string. Called on every request (no memoization). When set, the gem uses it instead of `access_token`.
|
|
91
|
+
- **`on_token_expired`**: Optional callable invoked when a 401/token-expired triggers a **single retry** (only when `access_token_provider` is set).
|
|
92
|
+
|
|
93
|
+
For detailed authentication flows, see [AUTHENTICATION.md](AUTHENTICATION.md).
|
|
94
|
+
|
|
95
|
+
## Available Resources
|
|
96
|
+
|
|
97
|
+
| Resource | Model | Actions |
|
|
98
|
+
| ------------------------ | -------------------------------------- | --------------------------------------------------- |
|
|
99
|
+
| Orders | `DhanHQ::Models::Order` | `find`, `all`, `where`, `place`, `update`, `cancel` |
|
|
100
|
+
| Trades | `DhanHQ::Models::Trade` | `all`, `find_by_order_id` |
|
|
101
|
+
| Forever Orders | `DhanHQ::Models::ForeverOrder` | `create`, `find`, `modify`, `cancel`, `all` |
|
|
102
|
+
| Holdings | `DhanHQ::Models::Holding` | `all` |
|
|
103
|
+
| Positions | `DhanHQ::Models::Position` | `all`, `find`, `exit!` |
|
|
104
|
+
| Funds & Margin | `DhanHQ::Models::Fund` | `fund_limit`, `margin_calculator` |
|
|
105
|
+
| Ledger | `DhanHQ::Models::Ledger` | `all` |
|
|
106
|
+
| Market Feeds | `DhanHQ::Models::MarketFeed` | `ltp`, `ohlc`, `quote` |
|
|
107
|
+
| Historical Data (Charts) | `DhanHQ::Models::HistoricalData` | `daily`, `intraday` |
|
|
108
|
+
| Option Chain | `DhanHQ::Models::OptionChain` | `fetch`, `fetch_expiry_list` |
|
|
109
|
+
| Super Orders | `DhanHQ::Models::SuperOrder` | `create`, `modify`, `cancel`, `all` |
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Super Orders — Full API Reference
|
|
2
|
+
|
|
3
|
+
Super orders are built for smart execution. They club the entry, target, and stop-loss legs (with optional trailing jump) into a single request so you can manage risk immediately after entry.
|
|
4
|
+
|
|
5
|
+
This gem exposes the full REST surface to create, modify, cancel, and list super orders across all supported exchanges and segments.
|
|
6
|
+
|
|
7
|
+
## Endpoints
|
|
8
|
+
|
|
9
|
+
| Method | Path | Description |
|
|
10
|
+
| -------- | -------------------------------------- | ------------------------------------- |
|
|
11
|
+
| `POST` | `/super/orders` | Create a new super order |
|
|
12
|
+
| `PUT` | `/super/orders/{order_id}` | Modify a pending super order |
|
|
13
|
+
| `DELETE` | `/super/orders/{order_id}/{order_leg}` | Cancel a pending super order leg |
|
|
14
|
+
| `GET` | `/super/orders` | Retrieve the list of all super orders |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Place Super Order
|
|
19
|
+
|
|
20
|
+
The place endpoint lets you submit a new super order that can include entry, target, stop-loss, and optional trailing jump definitions. It is available across exchanges and segments, and supports intraday, carry-forward, or MTF orders.
|
|
21
|
+
|
|
22
|
+
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
curl --request POST \
|
|
26
|
+
--url https://api.dhan.co/v2/super/orders \
|
|
27
|
+
--header 'Content-Type: application/json' \
|
|
28
|
+
--header 'access-token: JWT' \
|
|
29
|
+
--data '{Request JSON}'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Request body
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"dhan_client_id": "1000000003",
|
|
37
|
+
"correlation_id": "123abc678",
|
|
38
|
+
"transaction_type": "BUY",
|
|
39
|
+
"exchange_segment": "NSE_EQ",
|
|
40
|
+
"product_type": "CNC",
|
|
41
|
+
"order_type": "LIMIT",
|
|
42
|
+
"security_id": "11536",
|
|
43
|
+
"quantity": 5,
|
|
44
|
+
"price": 1500,
|
|
45
|
+
"target_price": 1600,
|
|
46
|
+
"stop_loss_price": 1400,
|
|
47
|
+
"trailing_jump": 10
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Parameters
|
|
52
|
+
|
|
53
|
+
| Field | Type | Description |
|
|
54
|
+
| ------------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. When you call through `DhanHQ::Models::SuperOrder`, the gem injects your configured client id so you can omit this key locally. |
|
|
56
|
+
| `correlation_id` | string | Caller generated correlation identifier |
|
|
57
|
+
| `transaction_type` | enum string *(required)* | Trading side. `BUY` or `SELL`. |
|
|
58
|
+
| `exchange_segment` | enum string *(required)* | Exchange segment (see appendix). |
|
|
59
|
+
| `product_type` | enum string *(required)* | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
|
|
60
|
+
| `order_type` | enum string *(required)* | Order type. `LIMIT` or `MARKET`. |
|
|
61
|
+
| `security_id` | string *(required)* | Exchange standard security identifier. |
|
|
62
|
+
| `quantity` | integer *(required)* | Number of shares for the order. |
|
|
63
|
+
| `price` | float *(required)* | Price at which the entry leg is placed. |
|
|
64
|
+
| `target_price` | float *(required)* | Target price for the super order. |
|
|
65
|
+
| `stop_loss_price` | float *(required)* | Stop-loss price for the super order. |
|
|
66
|
+
| `trailing_jump` | float *(required)* | Price jump size used to trail the stop-loss. |
|
|
67
|
+
|
|
68
|
+
> 🐍 When you call `DhanHQ::Models::SuperOrder.create`, pass snake_case keys as shown above. The client automatically camelizes
|
|
69
|
+
> them before posting to Dhan's REST API and injects your configured `dhan_client_id`, so you can omit that key in Ruby code.
|
|
70
|
+
|
|
71
|
+
### Response
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"order_id": "112111182198",
|
|
76
|
+
"order_status": "PENDING"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| Field | Type | Description |
|
|
81
|
+
| -------------- | ----------- | --------------------------------------------------- |
|
|
82
|
+
| `order_id` | string | Order identifier generated by Dhan |
|
|
83
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, or `REJECTED`. |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Modify Super Order
|
|
88
|
+
|
|
89
|
+
Use the modify endpoint to update any leg while the super order remains in `PENDING` or `PART_TRADED` status.
|
|
90
|
+
|
|
91
|
+
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
curl --request PUT \
|
|
95
|
+
--url https://api.dhan.co/v2/super/orders/{order_id} \
|
|
96
|
+
--header 'Content-Type: application/json' \
|
|
97
|
+
--header 'access-token: JWT' \
|
|
98
|
+
--data '{Request JSON}'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Request body
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"dhan_client_id": "1000000009",
|
|
106
|
+
"order_id": "112111182045",
|
|
107
|
+
"order_type": "LIMIT",
|
|
108
|
+
"leg_name": "ENTRY_LEG",
|
|
109
|
+
"quantity": 40,
|
|
110
|
+
"price": 1300,
|
|
111
|
+
"target_price": 1450,
|
|
112
|
+
"stop_loss_price": 1350,
|
|
113
|
+
"trailing_jump": 20
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Parameters
|
|
118
|
+
|
|
119
|
+
| Field | Type | Description |
|
|
120
|
+
| ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. Automatically added when you call through the Ruby models. |
|
|
122
|
+
| `order_id` | string *(required)* | Super order identifier generated by Dhan. |
|
|
123
|
+
| `order_type` | enum string *(conditionally required)* | `LIMIT` or `MARKET`. Required when modifying `ENTRY_LEG`. |
|
|
124
|
+
| `leg_name` | enum string *(required)* | `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. Entry leg updates entire order while status is `PENDING` or `PART_TRADED`. |
|
|
125
|
+
| `quantity` | integer *(conditionally required)* | Quantity update for `ENTRY_LEG`. |
|
|
126
|
+
| `price` | float *(conditionally required)* | Entry price update for `ENTRY_LEG`. |
|
|
127
|
+
| `target_price` | float *(conditionally required)* | Target price update for `ENTRY_LEG` or `TARGET_LEG`. |
|
|
128
|
+
| `stop_loss_price` | float *(conditionally required)* | Stop-loss price update for `ENTRY_LEG` or `STOP_LOSS_LEG`. |
|
|
129
|
+
| `trailing_jump` | float *(conditionally required)* | Trailing jump update for `ENTRY_LEG` or `STOP_LOSS_LEG`. Omit or set to `0` to cancel trailing. |
|
|
130
|
+
|
|
131
|
+
> ℹ️ Once the entry leg status becomes `TRADED`, only the `TARGET_LEG` and `STOP_LOSS_LEG` can be modified (price and trailing jump).
|
|
132
|
+
|
|
133
|
+
### Response
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"order_id": "112111182045",
|
|
138
|
+
"order_status": "TRANSIT"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
| Field | Type | Description |
|
|
143
|
+
| -------------- | ----------- | ------------------------------------------------------------- |
|
|
144
|
+
| `order_id` | string | Order identifier generated by Dhan |
|
|
145
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `TRADED`. |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Cancel Super Order
|
|
150
|
+
|
|
151
|
+
Cancel a pending or active super order leg using the order ID. Cancelling the entry leg removes every leg. Cancelling a specific target or stop-loss leg removes only that leg and it cannot be re-added.
|
|
152
|
+
|
|
153
|
+
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
curl --request DELETE \
|
|
157
|
+
--url https://api.dhan.co/v2/super/orders/{order_id}/{order_leg} \
|
|
158
|
+
--header 'Content-Type: application/json' \
|
|
159
|
+
--header 'access-token: JWT'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Path parameters
|
|
163
|
+
|
|
164
|
+
| Field | Description | Example |
|
|
165
|
+
| ----------- | ------------------------------------------------------------- | ------------- |
|
|
166
|
+
| `order_id` | Super order identifier. | `11211182198` |
|
|
167
|
+
| `order_leg` | Leg to cancel. `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. | `ENTRY_LEG` |
|
|
168
|
+
|
|
169
|
+
### Response
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"order_id": "112111182045",
|
|
174
|
+
"order_status": "CANCELLED"
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Field | Type | Description |
|
|
179
|
+
| -------------- | ----------- | ---------------------------------------------------------------- |
|
|
180
|
+
| `order_id` | string | Order identifier generated by Dhan |
|
|
181
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `CANCELLED`. |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Super Order List
|
|
186
|
+
|
|
187
|
+
List every super order placed during the trading day. The API nests leg details under the entry leg, and individual legs also appear in the main order book.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
curl --request GET \
|
|
191
|
+
--url https://api.dhan.co/v2/super/orders \
|
|
192
|
+
--header 'Content-Type: application/json' \
|
|
193
|
+
--header 'access-token: JWT'
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Response
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
"dhan_client_id": "1100003626",
|
|
202
|
+
"order_id": "5925022734212",
|
|
203
|
+
"correlation_id": "string",
|
|
204
|
+
"order_status": "PENDING",
|
|
205
|
+
"transaction_type": "BUY",
|
|
206
|
+
"exchange_segment": "NSE_EQ",
|
|
207
|
+
"product_type": "CNC",
|
|
208
|
+
"order_type": "LIMIT",
|
|
209
|
+
"validity": "DAY",
|
|
210
|
+
"trading_symbol": "HDFCBANK",
|
|
211
|
+
"security_id": "1333",
|
|
212
|
+
"quantity": 10,
|
|
213
|
+
"remaining_quantity": 10,
|
|
214
|
+
"ltp": 1660.95,
|
|
215
|
+
"price": 1500,
|
|
216
|
+
"after_market_order": false,
|
|
217
|
+
"leg_name": "ENTRY_LEG",
|
|
218
|
+
"exchange_order_id": "11925022734212",
|
|
219
|
+
"create_time": "2025-02-27 19:09:42",
|
|
220
|
+
"update_time": "2025-02-27 19:09:42",
|
|
221
|
+
"exchange_time": "2025-02-27 19:09:42",
|
|
222
|
+
"oms_error_description": "",
|
|
223
|
+
"average_traded_price": 0,
|
|
224
|
+
"filled_qty": 0,
|
|
225
|
+
"leg_details": [
|
|
226
|
+
{
|
|
227
|
+
"order_id": "5925022734212",
|
|
228
|
+
"leg_name": "STOP_LOSS_LEG",
|
|
229
|
+
"transaction_type": "SELL",
|
|
230
|
+
"total_quantity": 0,
|
|
231
|
+
"remaining_quantity": 0,
|
|
232
|
+
"triggered_quantity": 0,
|
|
233
|
+
"price": 1400,
|
|
234
|
+
"order_status": "PENDING",
|
|
235
|
+
"trailing_jump": 10
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"order_id": "5925022734212",
|
|
239
|
+
"leg_name": "TARGET_LEG",
|
|
240
|
+
"transaction_type": "SELL",
|
|
241
|
+
"remaining_quantity": 0,
|
|
242
|
+
"triggered_quantity": 0,
|
|
243
|
+
"price": 1550,
|
|
244
|
+
"order_status": "PENDING",
|
|
245
|
+
"trailing_jump": 0
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Response Parameters
|
|
253
|
+
|
|
254
|
+
| Field | Type | Description |
|
|
255
|
+
| ----------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
|
|
256
|
+
| `dhan_client_id` | string | User specific identification generated by Dhan. |
|
|
257
|
+
| `order_id` | string | Order identifier generated by Dhan. |
|
|
258
|
+
| `correlation_id` | string | Correlation identifier supplied by the caller. |
|
|
259
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `CLOSED`, `REJECTED`, `CANCELLED`, `PART_TRADED`, or `TRADED`. |
|
|
260
|
+
| `transaction_type` | enum string | Trading side. `BUY` or `SELL`. |
|
|
261
|
+
| `exchange_segment` | enum string | Exchange segment. |
|
|
262
|
+
| `product_type` | enum string | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
|
|
263
|
+
| `order_type` | enum string | Order type. `LIMIT` or `MARKET`. |
|
|
264
|
+
| `validity` | enum string | Order validity. `DAY`. |
|
|
265
|
+
| `trading_symbol` | string | Trading symbol reference. |
|
|
266
|
+
| `security_id` | string | Exchange security identifier. |
|
|
267
|
+
| `quantity` | integer | Ordered quantity. |
|
|
268
|
+
| `remaining_quantity` | integer | Quantity pending execution. |
|
|
269
|
+
| `ltp` | float | Last traded price. |
|
|
270
|
+
| `price` | float | Order price. |
|
|
271
|
+
| `after_market_order` | boolean | Indicates if the order was placed after market hours. |
|
|
272
|
+
| `leg_name` | enum string | Leg identifier: `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. |
|
|
273
|
+
| `trailing_jump` | float | Trailing jump for stop-loss. |
|
|
274
|
+
| `exchange_order_id` | string | Exchange-generated order identifier. |
|
|
275
|
+
| `create_time` | string | Order creation timestamp. |
|
|
276
|
+
| `update_time` | string | Latest update timestamp. |
|
|
277
|
+
| `exchange_time` | string | Exchange timestamp. |
|
|
278
|
+
| `oms_error_description` | string | OMS error description when applicable. |
|
|
279
|
+
| `average_traded_price` | float | Average traded price. |
|
|
280
|
+
| `filled_qty` | integer | Quantity traded on the exchange. |
|
|
281
|
+
| `triggered_quantity` | integer | Quantity triggered for stop-loss or target legs. |
|
|
282
|
+
| `leg_details` | array | Nested leg details for the super order. |
|
|
283
|
+
|
|
284
|
+
> ✅ `CLOSED` indicates the entry leg plus either target or stop-loss leg completed for the entire quantity. `TRIGGERED` appears on target and stop-loss legs to show which leg fired; inspect `triggered_quantity` for the executed quantity.
|
data/docs/TESTING_GUIDE.md
CHANGED
|
@@ -72,8 +72,8 @@ puts "Token provider: #{DhanHQ.configuration.access_token_provider ? 'Set' : 'No
|
|
|
72
72
|
|
|
73
73
|
```ruby
|
|
74
74
|
# Check current environment variables
|
|
75
|
-
puts "
|
|
76
|
-
puts "
|
|
75
|
+
puts "DHAN_CLIENT_ID: #{ENV['DHAN_CLIENT_ID']}"
|
|
76
|
+
puts "DHAN_ACCESS_TOKEN: #{ENV['DHAN_ACCESS_TOKEN'] ? 'Set' : 'Not Set'}"
|
|
77
77
|
puts "DHAN_LOG_LEVEL: #{ENV['DHAN_LOG_LEVEL'] || 'INFO'}"
|
|
78
78
|
puts "DHAN_CONNECT_TIMEOUT: #{ENV['DHAN_CONNECT_TIMEOUT'] || '10'}"
|
|
79
79
|
puts "DHAN_READ_TIMEOUT: #{ENV['DHAN_READ_TIMEOUT'] || '30'}"
|
|
@@ -521,7 +521,7 @@ if position
|
|
|
521
521
|
to_product_type: "MARGIN",
|
|
522
522
|
quantity: position.net_qty.abs
|
|
523
523
|
)
|
|
524
|
-
|
|
524
|
+
|
|
525
525
|
if result
|
|
526
526
|
puts "Position converted successfully"
|
|
527
527
|
else
|
|
@@ -784,22 +784,22 @@ if instrument
|
|
|
784
784
|
# Get LTP
|
|
785
785
|
ltp_data = instrument.ltp
|
|
786
786
|
puts "LTP: ₹#{ltp_data[:last_price]}"
|
|
787
|
-
|
|
787
|
+
|
|
788
788
|
# Get OHLC
|
|
789
789
|
ohlc_data = instrument.ohlc
|
|
790
790
|
puts "OHLC: #{ohlc_data[:ohlc]}"
|
|
791
|
-
|
|
791
|
+
|
|
792
792
|
# Get Quote
|
|
793
793
|
quote_data = instrument.quote
|
|
794
794
|
puts "Quote: #{quote_data[:ltp]}"
|
|
795
|
-
|
|
795
|
+
|
|
796
796
|
# Get Daily Historical Data
|
|
797
797
|
daily_data = instrument.daily(
|
|
798
798
|
from_date: Date.today - 7,
|
|
799
799
|
to_date: Date.today
|
|
800
800
|
)
|
|
801
801
|
puts "Daily candles: #{daily_data.size}"
|
|
802
|
-
|
|
802
|
+
|
|
803
803
|
# Get Intraday Historical Data
|
|
804
804
|
intraday_data = instrument.intraday(
|
|
805
805
|
from_date: Date.today,
|
|
@@ -807,7 +807,7 @@ if instrument
|
|
|
807
807
|
interval: 5
|
|
808
808
|
)
|
|
809
809
|
puts "Intraday candles: #{intraday_data.size}"
|
|
810
|
-
|
|
810
|
+
|
|
811
811
|
# Get Expiry List (for F&O instruments)
|
|
812
812
|
if instrument.instrument == "FUTSTK" || instrument.instrument == "OPTSTK"
|
|
813
813
|
expiry_list = instrument.expiry_list
|