DhanHQ 2.1.10 → 2.2.1
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/.rubocop.yml +1 -1
- data/.rubocop_todo.yml +143 -118
- data/CHANGELOG.md +127 -0
- data/CODE_REVIEW_ISSUES.md +397 -0
- data/FIXES_APPLIED.md +373 -0
- data/GUIDE.md +41 -0
- data/README.md +55 -0
- data/RELEASING.md +60 -0
- data/REVIEW_SUMMARY.md +120 -0
- data/VERSION_UPDATE.md +82 -0
- data/core +0 -0
- data/docs/AUTHENTICATION.md +63 -0
- data/docs/DATA_API_PARAMETERS.md +278 -0
- data/docs/PR_2.2.0.md +48 -0
- data/docs/RELEASE_GUIDE.md +492 -0
- data/docs/TESTING_GUIDE.md +1514 -0
- data/docs/live_order_updates.md +25 -1
- data/docs/rails_integration.md +29 -0
- data/docs/websocket_integration.md +22 -0
- data/lib/DhanHQ/client.rb +65 -9
- data/lib/DhanHQ/configuration.rb +27 -0
- data/lib/DhanHQ/constants.rb +1 -1
- data/lib/DhanHQ/contracts/place_order_contract.rb +51 -0
- data/lib/DhanHQ/core/base_model.rb +17 -10
- data/lib/DhanHQ/errors.rb +4 -0
- data/lib/DhanHQ/helpers/request_helper.rb +17 -2
- data/lib/DhanHQ/helpers/response_helper.rb +34 -13
- data/lib/DhanHQ/models/expired_options_data.rb +0 -6
- data/lib/DhanHQ/models/instrument_helpers.rb +4 -4
- data/lib/DhanHQ/models/order.rb +19 -2
- data/lib/DhanHQ/models/order_update.rb +0 -4
- data/lib/DhanHQ/rate_limiter.rb +40 -6
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/client.rb +12 -5
- data/lib/DhanHQ/ws/connection.rb +16 -2
- data/lib/DhanHQ/ws/market_depth/client.rb +3 -1
- data/lib/DhanHQ/ws/market_depth.rb +12 -12
- data/lib/DhanHQ/ws/orders/client.rb +76 -11
- data/lib/DhanHQ/ws/orders/connection.rb +3 -1
- metadata +18 -5
- data/lib/DhanHQ/contracts/modify_order_contract_copy.rb +0 -100
data/VERSION_UPDATE.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Version Update Summary
|
|
2
|
+
|
|
3
|
+
**Previous Version**: 2.1.10
|
|
4
|
+
**New Version**: 2.1.11
|
|
5
|
+
**Release Date**: 2025-01-27
|
|
6
|
+
|
|
7
|
+
## Version Bump Rationale
|
|
8
|
+
|
|
9
|
+
This is a **PATCH version** bump (2.1.10 → 2.1.11) because:
|
|
10
|
+
|
|
11
|
+
1. **All changes are backward compatible** - No breaking API changes
|
|
12
|
+
2. **Primarily bug fixes** - Critical thread safety, memory leaks, and error handling improvements
|
|
13
|
+
3. **No new features** - Only improvements to existing functionality
|
|
14
|
+
4. **Follows semantic versioning** - PATCH for bug fixes
|
|
15
|
+
|
|
16
|
+
## Changes Included
|
|
17
|
+
|
|
18
|
+
### Critical Fixes (4)
|
|
19
|
+
- Rate limiter race condition
|
|
20
|
+
- Client initialization validation
|
|
21
|
+
- WebSocket error handling
|
|
22
|
+
- Price field validation (NaN/Infinity)
|
|
23
|
+
|
|
24
|
+
### High Priority Fixes (5)
|
|
25
|
+
- Order tracker memory leak
|
|
26
|
+
- JSON parse error handling
|
|
27
|
+
- Timeout configuration
|
|
28
|
+
- WebSocket thread safety
|
|
29
|
+
- Error object handling consistency
|
|
30
|
+
|
|
31
|
+
### Medium Priority Fixes (8)
|
|
32
|
+
- Retry logic for transient errors
|
|
33
|
+
- Order modification state validation
|
|
34
|
+
- Error mapping improvements
|
|
35
|
+
- Rate limiter cleanup thread shutdown
|
|
36
|
+
- Order operation logging
|
|
37
|
+
- And more...
|
|
38
|
+
|
|
39
|
+
### Low Priority Fixes (6)
|
|
40
|
+
- Code cleanup and deduplication
|
|
41
|
+
- Type checking improvements
|
|
42
|
+
- Response format logging
|
|
43
|
+
- And more...
|
|
44
|
+
|
|
45
|
+
### API Compliance (2)
|
|
46
|
+
- Header validation
|
|
47
|
+
- 202 Accepted status handling
|
|
48
|
+
|
|
49
|
+
## Files Updated
|
|
50
|
+
|
|
51
|
+
- `lib/DhanHQ/version.rb` - Version updated to 2.1.11
|
|
52
|
+
- `CHANGELOG.md` - Added comprehensive changelog entry
|
|
53
|
+
- All fix files as documented in `FIXES_APPLIED.md`
|
|
54
|
+
|
|
55
|
+
## Testing
|
|
56
|
+
|
|
57
|
+
All fixes include comprehensive test coverage:
|
|
58
|
+
- ✅ 36 spec files total
|
|
59
|
+
- ✅ New test files created for new functionality
|
|
60
|
+
- ✅ Existing tests updated for improved behavior
|
|
61
|
+
- ✅ No linter errors
|
|
62
|
+
|
|
63
|
+
## Backward Compatibility
|
|
64
|
+
|
|
65
|
+
✅ **100% Backward Compatible - Verified**
|
|
66
|
+
- No breaking API changes
|
|
67
|
+
- All existing code continues to work without modification
|
|
68
|
+
- Validation moved to request time (not initialization) to maintain compatibility
|
|
69
|
+
- Order modification validation is warning-only (API handles final validation)
|
|
70
|
+
- JSON parsing handles empty strings gracefully (backward compatible)
|
|
71
|
+
- New features are opt-in via environment variables
|
|
72
|
+
- Default behavior unchanged
|
|
73
|
+
- All fixes align with API documentation at https://api.dhan.co/v2/#/
|
|
74
|
+
|
|
75
|
+
## Next Steps
|
|
76
|
+
|
|
77
|
+
1. Run full test suite: `bundle exec rspec`
|
|
78
|
+
2. Verify integration tests pass
|
|
79
|
+
3. Update documentation if needed
|
|
80
|
+
4. Tag release: `git tag v2.1.11`
|
|
81
|
+
5. Build gem: `gem build DhanHQ.gemspec`
|
|
82
|
+
6. Push to repository
|
data/core
ADDED
|
Binary file
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Authentication & token handling
|
|
2
|
+
|
|
3
|
+
This document describes how the gem handles access tokens, including dynamic resolution, retry-on-401, and related errors.
|
|
4
|
+
|
|
5
|
+
## Static token (default)
|
|
6
|
+
|
|
7
|
+
Set `access_token` once; it is sent on every request:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
DhanHQ.configure do |config|
|
|
11
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
12
|
+
config.access_token = ENV["ACCESS_TOKEN"]
|
|
13
|
+
end
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Dynamic access token
|
|
17
|
+
|
|
18
|
+
For production or OAuth-style flows, resolve the token at **request time** so it can rotate without restarting the app:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
DhanHQ.configure do |config|
|
|
22
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
23
|
+
config.access_token_provider = lambda do
|
|
24
|
+
record = YourTokenStore.active # e.g. from DB or OAuth
|
|
25
|
+
raise "Token expired or missing" unless record
|
|
26
|
+
record.access_token
|
|
27
|
+
end
|
|
28
|
+
config.on_token_expired = ->(error) { YourTokenStore.refresh! } # optional
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **`access_token_provider`**: Callable (Proc/lambda) that returns the access token string. Called on **every request** (no memoization). When set, the gem uses it instead of `access_token`.
|
|
33
|
+
- **`on_token_expired`**: Optional callable invoked when a 401/token-expired triggers a **single retry** (only when `access_token_provider` is set). Use for logging or refreshing your store; the retry then uses the token from the provider.
|
|
34
|
+
|
|
35
|
+
REST and WebSocket clients both use `config.resolved_access_token`, which calls the provider when set or falls back to `access_token`.
|
|
36
|
+
|
|
37
|
+
## Retry-on-401
|
|
38
|
+
|
|
39
|
+
When the API returns **401** or a token-expired error (e.g. error code **807**), and `access_token_provider` is set:
|
|
40
|
+
|
|
41
|
+
1. The client catches the auth error (`InvalidAuthenticationError`, `InvalidTokenError`, `TokenExpiredError`, or `AuthenticationFailedError`).
|
|
42
|
+
2. It calls `on_token_expired&.call(error)` if configured.
|
|
43
|
+
3. It retries the request **once**. The retry uses `build_headers` → `resolved_access_token` → provider again, so the next token is used.
|
|
44
|
+
|
|
45
|
+
If the provider is not set, or the retry also returns 401, the error is raised. There is no second retry for auth failures.
|
|
46
|
+
|
|
47
|
+
## Errors
|
|
48
|
+
|
|
49
|
+
| Error | When |
|
|
50
|
+
| ----- | ----- |
|
|
51
|
+
| **`DhanHQ::AuthenticationError`** | Token could not be resolved: missing config, or `access_token_provider` returned nil/empty. |
|
|
52
|
+
| **`DhanHQ::InvalidAuthenticationError`** | API returned 401 or error code DH-901 (invalid/expired token). |
|
|
53
|
+
| **`DhanHQ::TokenExpiredError`** | API returned error code **807** (token expired). |
|
|
54
|
+
| **`DhanHQ::InvalidTokenError`** | API returned error code 809 (invalid token). |
|
|
55
|
+
| **`DhanHQ::AuthenticationFailedError`** | API returned error code 808 (auth failed). |
|
|
56
|
+
|
|
57
|
+
Rescue `AuthenticationError` for local config/token resolution failures; rescue `InvalidAuthenticationError` / `TokenExpiredError` for API-reported auth failures.
|
|
58
|
+
|
|
59
|
+
## See also
|
|
60
|
+
|
|
61
|
+
- [README.md](../README.md) — Configuration and “Dynamic access token”
|
|
62
|
+
- [rails_integration.md](rails_integration.md) — Rails initializer with optional `access_token_provider`
|
|
63
|
+
- [CHANGELOG.md](../CHANGELOG.md) — 2.2.0 auth and token changes
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# Data API Required Parameters
|
|
2
|
+
|
|
3
|
+
This document lists all required parameters for data APIs from LTP (Last Traded Price) to Option Chain.
|
|
4
|
+
|
|
5
|
+
## 1. Market Feed APIs
|
|
6
|
+
|
|
7
|
+
### 1.1 LTP (Last Traded Price)
|
|
8
|
+
**Method:** `DhanHQ::Models::MarketFeed.ltp`
|
|
9
|
+
|
|
10
|
+
**Required Parameters:**
|
|
11
|
+
- `params` [Hash{String => Array<Integer>}] - Payload mapping exchange segments to arrays of security IDs
|
|
12
|
+
- Keys: Exchange segment strings (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", "IDX_I", etc.)
|
|
13
|
+
- Values: Arrays of security IDs (integers)
|
|
14
|
+
|
|
15
|
+
**Example:**
|
|
16
|
+
```ruby
|
|
17
|
+
payload = {
|
|
18
|
+
"NSE_EQ" => [11536, 3456],
|
|
19
|
+
"NSE_FNO" => [49081, 49082]
|
|
20
|
+
}
|
|
21
|
+
response = DhanHQ::Models::MarketFeed.ltp(payload)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Notes:**
|
|
25
|
+
- Up to 1000 instruments per request
|
|
26
|
+
- Rate limit: 1 request per second
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
### 1.2 OHLC (Open, High, Low, Close)
|
|
31
|
+
**Method:** `DhanHQ::Models::MarketFeed.ohlc`
|
|
32
|
+
|
|
33
|
+
**Required Parameters:**
|
|
34
|
+
- `params` [Hash{String => Array<Integer>}] - Payload mapping exchange segments to arrays of security IDs
|
|
35
|
+
- Keys: Exchange segment strings (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", "IDX_I", etc.)
|
|
36
|
+
- Values: Arrays of security IDs (integers)
|
|
37
|
+
|
|
38
|
+
**Example:**
|
|
39
|
+
```ruby
|
|
40
|
+
payload = {
|
|
41
|
+
"NSE_EQ" => [11536]
|
|
42
|
+
}
|
|
43
|
+
response = DhanHQ::Models::MarketFeed.ohlc(payload)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Notes:**
|
|
47
|
+
- Up to 1000 instruments per request
|
|
48
|
+
- Rate limit: 1 request per second
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
### 1.3 Quote (Full Market Depth)
|
|
53
|
+
**Method:** `DhanHQ::Models::MarketFeed.quote`
|
|
54
|
+
|
|
55
|
+
**Required Parameters:**
|
|
56
|
+
- `params` [Hash{String => Array<Integer>}] - Payload mapping exchange segments to arrays of security IDs
|
|
57
|
+
- Keys: Exchange segment strings (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", "IDX_I", etc.)
|
|
58
|
+
- Values: Arrays of security IDs (integers)
|
|
59
|
+
|
|
60
|
+
**Example:**
|
|
61
|
+
```ruby
|
|
62
|
+
payload = {
|
|
63
|
+
"NSE_FNO" => [49081]
|
|
64
|
+
}
|
|
65
|
+
response = DhanHQ::Models::MarketFeed.quote(payload)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Notes:**
|
|
69
|
+
- Up to 1000 instruments per request
|
|
70
|
+
- Rate limit: 1 request per second (uses separate quote API)
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 2. Historical Data APIs
|
|
75
|
+
|
|
76
|
+
### 2.1 Daily Historical Data
|
|
77
|
+
**Method:** `DhanHQ::Models::HistoricalData.daily`
|
|
78
|
+
|
|
79
|
+
**Required Parameters:**
|
|
80
|
+
- `security_id` [String] - Exchange standard ID for each scrip
|
|
81
|
+
- `exchange_segment` [String] - Exchange and segment identifier
|
|
82
|
+
- Valid values: See `DhanHQ::Constants::EXCHANGE_SEGMENTS` (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ", "IDX_I", etc.)
|
|
83
|
+
- `instrument` [String] - Instrument type of the scrip
|
|
84
|
+
- Valid values: See `DhanHQ::Constants::INSTRUMENTS` (e.g., "EQUITY", "FUTIDX", "FUTSTK", "OPTIDX", "OPTSTK", "INDEX", etc.)
|
|
85
|
+
- `from_date` [String] - Start date in YYYY-MM-DD format
|
|
86
|
+
- `to_date` [String] - End date (non-inclusive) in YYYY-MM-DD format
|
|
87
|
+
|
|
88
|
+
**Optional Parameters:**
|
|
89
|
+
- `expiry_code` [Integer] - Expiry of instruments for derivatives (0, 1, or 2)
|
|
90
|
+
- `oi` [Boolean] - Include Open Interest data for Futures & Options (default: false)
|
|
91
|
+
|
|
92
|
+
**Example:**
|
|
93
|
+
```ruby
|
|
94
|
+
data = DhanHQ::Models::HistoricalData.daily(
|
|
95
|
+
security_id: "1333",
|
|
96
|
+
exchange_segment: "NSE_EQ",
|
|
97
|
+
instrument: "EQUITY",
|
|
98
|
+
from_date: "2022-01-08",
|
|
99
|
+
to_date: "2022-02-08"
|
|
100
|
+
)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### 2.2 Intraday Historical Data
|
|
106
|
+
**Method:** `DhanHQ::Models::HistoricalData.intraday`
|
|
107
|
+
|
|
108
|
+
**Required Parameters:**
|
|
109
|
+
- `security_id` [String] - Exchange standard ID for each scrip
|
|
110
|
+
- `exchange_segment` [String] - Exchange and segment identifier
|
|
111
|
+
- Valid values: See `DhanHQ::Constants::EXCHANGE_SEGMENTS`
|
|
112
|
+
- `instrument` [String] - Instrument type of the scrip
|
|
113
|
+
- Valid values: See `DhanHQ::Constants::INSTRUMENTS`
|
|
114
|
+
- `interval` [String] - Minute intervals for the timeframe
|
|
115
|
+
- Valid values: "1", "5", "15", "25", "60"
|
|
116
|
+
- `from_date` [String] - Start date
|
|
117
|
+
- Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS (e.g., "2024-09-11" or "2024-09-11 09:30:00")
|
|
118
|
+
- `to_date` [String] - End date
|
|
119
|
+
- Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS (e.g., "2024-09-15" or "2024-09-15 13:00:00")
|
|
120
|
+
|
|
121
|
+
**Optional Parameters:**
|
|
122
|
+
- `expiry_code` [Integer] - Expiry of instruments for derivatives (0, 1, or 2)
|
|
123
|
+
- `oi` [Boolean] - Include Open Interest data for Futures & Options (default: false)
|
|
124
|
+
|
|
125
|
+
**Example:**
|
|
126
|
+
```ruby
|
|
127
|
+
data = DhanHQ::Models::HistoricalData.intraday(
|
|
128
|
+
security_id: "1333",
|
|
129
|
+
exchange_segment: "NSE_EQ",
|
|
130
|
+
instrument: "EQUITY",
|
|
131
|
+
interval: "15",
|
|
132
|
+
from_date: "2024-09-11",
|
|
133
|
+
to_date: "2024-09-15"
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Notes:**
|
|
138
|
+
- Maximum 90 days of data can be fetched in a single request
|
|
139
|
+
- Data available for the last 5 years
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 3. Option Chain APIs
|
|
144
|
+
|
|
145
|
+
### 3.1 Fetch Option Chain
|
|
146
|
+
**Method:** `DhanHQ::Models::OptionChain.fetch`
|
|
147
|
+
|
|
148
|
+
**Required Parameters:**
|
|
149
|
+
- `underlying_scrip` [Integer] - Security ID of the underlying instrument
|
|
150
|
+
- `underlying_seg` [String] - Exchange and segment of underlying
|
|
151
|
+
- Valid values: "IDX_I" (Index), "NSE_FNO" (NSE F&O), "BSE_FNO" (BSE F&O), "MCX_FO" (MCX)
|
|
152
|
+
- `expiry` [String] - Expiry date in YYYY-MM-DD format
|
|
153
|
+
|
|
154
|
+
**Example:**
|
|
155
|
+
```ruby
|
|
156
|
+
chain = DhanHQ::Models::OptionChain.fetch(
|
|
157
|
+
underlying_scrip: 13,
|
|
158
|
+
underlying_seg: "IDX_I",
|
|
159
|
+
expiry: "2024-10-31"
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Notes:**
|
|
164
|
+
- Rate limit: 1 request per 3 seconds
|
|
165
|
+
- Automatically filters out strikes where both CE and PE have zero `last_price`
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
### 3.2 Fetch Expiry List
|
|
170
|
+
**Method:** `DhanHQ::Models::OptionChain.fetch_expiry_list`
|
|
171
|
+
|
|
172
|
+
**Required Parameters:**
|
|
173
|
+
- `underlying_scrip` [Integer] - Security ID of the underlying instrument
|
|
174
|
+
- `underlying_seg` [String] - Exchange and segment of underlying
|
|
175
|
+
- Valid values: "IDX_I" (Index), "NSE_FNO" (NSE F&O), "BSE_FNO" (BSE F&O), "MCX_FO" (MCX)
|
|
176
|
+
|
|
177
|
+
**Example:**
|
|
178
|
+
```ruby
|
|
179
|
+
expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(
|
|
180
|
+
underlying_scrip: 13,
|
|
181
|
+
underlying_seg: "IDX_I"
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Notes:**
|
|
186
|
+
- Returns array of expiry dates in "YYYY-MM-DD" format
|
|
187
|
+
- Use this to get valid expiry dates before calling `fetch`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 4. Expired Options Data API
|
|
192
|
+
|
|
193
|
+
**Method:** `DhanHQ::Models::ExpiredOptionsData.fetch`
|
|
194
|
+
|
|
195
|
+
**Required Parameters:**
|
|
196
|
+
- `exchange_segment` [String] - Exchange and segment identifier
|
|
197
|
+
- Valid values: "NSE_FNO", "BSE_FNO", "NSE_EQ", "BSE_EQ"
|
|
198
|
+
- `interval` [String] - Minute intervals for the timeframe
|
|
199
|
+
- Valid values: "1", "5", "15", "25", "60"
|
|
200
|
+
- `security_id` [Integer] - Underlying exchange standard ID for each scrip
|
|
201
|
+
- `instrument` [String] - Instrument type of the scrip
|
|
202
|
+
- Valid values: "OPTIDX" (Index Options), "OPTSTK" (Stock Options)
|
|
203
|
+
- `expiry_flag` [String] - Expiry interval of the instrument
|
|
204
|
+
- Valid values: "WEEK", "MONTH"
|
|
205
|
+
- `expiry_code` [Integer] - Expiry code for the instrument
|
|
206
|
+
- `strike` [String] - Strike price specification
|
|
207
|
+
- Format: "ATM" for At The Money, "ATM+X" or "ATM-X" for offset strikes
|
|
208
|
+
- For Index Options (near expiry): Up to ATM+10 / ATM-10
|
|
209
|
+
- For all other contracts: Up to ATM+3 / ATM-3
|
|
210
|
+
- `drv_option_type` [String] - Option type
|
|
211
|
+
- Valid values: "CALL", "PUT"
|
|
212
|
+
- `required_data` [Array<String>] - Array of required data fields
|
|
213
|
+
- Valid values: "open", "high", "low", "close", "iv", "volume", "strike", "oi", "spot"
|
|
214
|
+
- `from_date` [String] - Start date in YYYY-MM-DD format
|
|
215
|
+
- Cannot be more than 5 years ago
|
|
216
|
+
- `to_date` [String] - End date (non-inclusive) in YYYY-MM-DD format
|
|
217
|
+
- Date range cannot exceed 31 days from from_date
|
|
218
|
+
|
|
219
|
+
**Example:**
|
|
220
|
+
```ruby
|
|
221
|
+
data = DhanHQ::Models::ExpiredOptionsData.fetch(
|
|
222
|
+
exchange_segment: "NSE_FNO",
|
|
223
|
+
interval: "1",
|
|
224
|
+
security_id: 13,
|
|
225
|
+
instrument: "OPTIDX",
|
|
226
|
+
expiry_flag: "MONTH",
|
|
227
|
+
expiry_code: 1,
|
|
228
|
+
strike: "ATM",
|
|
229
|
+
drv_option_type: "CALL",
|
|
230
|
+
required_data: ["open", "high", "low", "close", "volume", "iv", "oi", "spot"],
|
|
231
|
+
from_date: "2021-08-01",
|
|
232
|
+
to_date: "2021-09-01"
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Notes:**
|
|
237
|
+
- Up to 31 days of data can be fetched in a single request
|
|
238
|
+
- Historical data available for up to the last 5 years
|
|
239
|
+
- Data is organized by strike price relative to spot
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## Summary Table
|
|
244
|
+
|
|
245
|
+
| API | Required Parameters | Optional Parameters | Rate Limit |
|
|
246
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----------- |
|
|
247
|
+
| **LTP** | `params` (Hash: segment => [security_ids]) | None | 1 req/sec |
|
|
248
|
+
| **OHLC** | `params` (Hash: segment => [security_ids]) | None | 1 req/sec |
|
|
249
|
+
| **Quote** | `params` (Hash: segment => [security_ids]) | None | 1 req/sec |
|
|
250
|
+
| **Daily Historical** | `security_id`, `exchange_segment`, `instrument`, `from_date`, `to_date` | `expiry_code`, `oi` | Standard |
|
|
251
|
+
| **Intraday Historical** | `security_id`, `exchange_segment`, `instrument`, `interval`, `from_date`, `to_date` | `expiry_code`, `oi` | Standard |
|
|
252
|
+
| **Option Chain** | `underlying_scrip`, `underlying_seg`, `expiry` | None | 1 req/3 sec |
|
|
253
|
+
| **Expiry List** | `underlying_scrip`, `underlying_seg` | None | 1 req/3 sec |
|
|
254
|
+
| **Expired Options** | `exchange_segment`, `interval`, `security_id`, `instrument`, `expiry_flag`, `expiry_code`, `strike`, `drv_option_type`, `required_data`, `from_date`, `to_date` | None | Standard |
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Exchange Segments
|
|
259
|
+
|
|
260
|
+
Common exchange segment values:
|
|
261
|
+
- `IDX_I` - Index
|
|
262
|
+
- `NSE_EQ` - NSE Equity Cash
|
|
263
|
+
- `NSE_FNO` - NSE Futures & Options
|
|
264
|
+
- `NSE_CURRENCY` - NSE Currency
|
|
265
|
+
- `BSE_EQ` - BSE Equity Cash
|
|
266
|
+
- `BSE_FNO` - BSE Futures & Options
|
|
267
|
+
- `BSE_CURRENCY` - BSE Currency
|
|
268
|
+
- `MCX_COMM` - MCX Commodity
|
|
269
|
+
|
|
270
|
+
## Instrument Types
|
|
271
|
+
|
|
272
|
+
Common instrument type values:
|
|
273
|
+
- `EQUITY` - Equity
|
|
274
|
+
- `INDEX` - Index
|
|
275
|
+
- `FUTIDX` - Futures Index
|
|
276
|
+
- `FUTSTK` - Futures Stock
|
|
277
|
+
- `OPTIDX` - Options Index
|
|
278
|
+
- `OPTSTK` - Options Stock
|
data/docs/PR_2.2.0.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# PR: Dynamic access token resolution, auto-expiry detection, retry-on-401
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
Adds production-grade auth handling to `dhanhq-client`:
|
|
6
|
+
|
|
7
|
+
1. **Dynamic access token resolution** — Token can come from a callable (`access_token_provider`) at request time.
|
|
8
|
+
2. **Auto-expiry detection** — API error code 807 (token expired) raises `DhanHQ::TokenExpiredError`.
|
|
9
|
+
3. **Retry-on-401 with token re-fetch** — On 401/token-expired, if `access_token_provider` is set, the client retries once; the next request calls the provider again for a fresh token.
|
|
10
|
+
4. **Optional `on_token_expired` hook** — Invoked before retry when auth failure triggers a retry (e.g. for logging or refreshing token in your store).
|
|
11
|
+
|
|
12
|
+
## Changes
|
|
13
|
+
|
|
14
|
+
- **Configuration**: `access_token_provider`, `on_token_expired`, `resolved_access_token`.
|
|
15
|
+
- **Errors**: `DhanHQ::AuthenticationError` (local token resolution failure), `DhanHQ::TokenExpiredError` (API 807).
|
|
16
|
+
- **Client**: Retry-on-401 for `InvalidAuthenticationError`, `InvalidTokenError`, `TokenExpiredError`, `AuthenticationFailedError` when provider is set (single retry).
|
|
17
|
+
- **REST + WebSocket**: All token usage goes through `config.resolved_access_token` (no memoization).
|
|
18
|
+
|
|
19
|
+
## Backward compatibility
|
|
20
|
+
|
|
21
|
+
- **Non-breaking**. Existing `config.access_token = "static-token"` still works. `access_token_provider` is optional. Safe as a **minor** version bump (2.2.0).
|
|
22
|
+
|
|
23
|
+
## Testing
|
|
24
|
+
|
|
25
|
+
- `spec/dhan_hq/configuration_spec.rb` — `#resolved_access_token` (provider, fallback, nil/empty).
|
|
26
|
+
- `spec/dhan_hq/client_spec.rb` — WebMock: 401, 403, 807, retry-on-401 success, retry then raise, `on_token_expired` hook.
|
|
27
|
+
- `spec/dhan_hq/helpers/response_helper_spec.rb` — 807 → TokenExpiredError.
|
|
28
|
+
|
|
29
|
+
## Changelog
|
|
30
|
+
|
|
31
|
+
See [CHANGELOG.md](../CHANGELOG.md) — section **## [2.2.0] - 2026-01-31**.
|
|
32
|
+
|
|
33
|
+
## README & docs
|
|
34
|
+
|
|
35
|
+
- **README.md**: New subsection “Dynamic access token (optional)” under Configuration (access_token_provider, on_token_expired, retry-on-401, AuthenticationError / TokenExpiredError).
|
|
36
|
+
- **GUIDE.md**: Short “Dynamic access token” note and link to docs/AUTHENTICATION.md.
|
|
37
|
+
- **docs/AUTHENTICATION.md**: New doc for static vs dynamic token, retry-on-401, and auth-related errors.
|
|
38
|
+
- **docs/TESTING_GUIDE.md**: Optional access_token_provider / on_token_expired in config examples; verify “Token provider” in console.
|
|
39
|
+
- **docs/rails_integration.md**: New “Dynamic access token (optional)” with Rails initializer example.
|
|
40
|
+
- **docs/websocket_integration.md**, **docs/live_order_updates.md**: One-line pointer to docs/AUTHENTICATION.md for dynamic token.
|
|
41
|
+
|
|
42
|
+
## Checklist
|
|
43
|
+
|
|
44
|
+
- [x] All specs pass
|
|
45
|
+
- [x] No breaking changes
|
|
46
|
+
- [x] CHANGELOG updated
|
|
47
|
+
- [x] Version bumped to 2.2.0
|
|
48
|
+
- [x] README and docs updated
|