DhanHQ 2.6.1 → 2.6.3
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 +15 -3
- data/ARCHITECTURE.md +113 -0
- data/CHANGELOG.md +55 -0
- data/README.md +2 -0
- data/Rakefile +3 -1
- data/docs/API_VERIFICATION.md +10 -8
- data/docs/ENDPOINTS_AND_SANDBOX.md +115 -0
- data/lib/DhanHQ/auth.rb +2 -2
- data/lib/DhanHQ/client.rb +72 -51
- data/lib/DhanHQ/configuration.rb +45 -11
- data/lib/DhanHQ/constants.rb +68 -4
- data/lib/DhanHQ/contracts/alert_order_contract.rb +23 -16
- data/lib/DhanHQ/contracts/expired_options_data_contract.rb +4 -2
- data/lib/DhanHQ/contracts/forever_order_contract.rb +55 -0
- data/lib/DhanHQ/contracts/historical_data_contract.rb +17 -19
- data/lib/DhanHQ/contracts/intraday_historical_data_contract.rb +12 -0
- data/lib/DhanHQ/contracts/margin_calculator_contract.rb +19 -17
- data/lib/DhanHQ/contracts/market_feed_contract.rb +42 -0
- data/lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb +8 -5
- data/lib/DhanHQ/contracts/option_chain_contract.rb +17 -19
- data/lib/DhanHQ/contracts/pnl_based_exit_contract.rb +1 -1
- data/lib/DhanHQ/contracts/slice_order_contract.rb +10 -10
- data/lib/DhanHQ/core/auth_api.rb +1 -1
- data/lib/DhanHQ/core/base_api.rb +10 -9
- data/lib/DhanHQ/core/base_model.rb +4 -1
- data/lib/DhanHQ/core/error_handler.rb +2 -2
- data/lib/DhanHQ/errors.rb +14 -2
- data/lib/DhanHQ/helpers/request_helper.rb +27 -5
- data/lib/DhanHQ/helpers/response_helper.rb +48 -19
- data/lib/DhanHQ/helpers/validation_helper.rb +4 -2
- data/lib/DhanHQ/models/alert_order.rb +6 -2
- data/lib/DhanHQ/models/edis.rb +20 -13
- data/lib/DhanHQ/models/expired_options_data.rb +54 -44
- data/lib/DhanHQ/models/forever_order.rb +17 -7
- data/lib/DhanHQ/models/historical_data.rb +40 -6
- data/lib/DhanHQ/models/instrument_helpers.rb +2 -1
- data/lib/DhanHQ/models/margin.rb +62 -82
- data/lib/DhanHQ/models/market_feed.rb +14 -3
- data/lib/DhanHQ/models/option_chain.rb +50 -150
- data/lib/DhanHQ/models/order.rb +19 -4
- data/lib/DhanHQ/models/super_order.rb +2 -2
- data/lib/DhanHQ/resources/alert_orders.rb +1 -1
- data/lib/DhanHQ/resources/edis.rb +4 -3
- data/lib/DhanHQ/resources/instruments.rb +3 -2
- data/lib/DhanHQ/resources/ip_setup.rb +4 -1
- data/lib/DhanHQ/resources/kill_switch.rb +7 -1
- data/lib/DhanHQ/resources/orders.rb +1 -1
- data/lib/DhanHQ/resources/super_orders.rb +8 -2
- data/lib/DhanHQ/resources/trader_control.rb +13 -4
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/base_connection.rb +1 -1
- data/lib/DhanHQ/ws/client.rb +2 -1
- data/lib/DhanHQ/ws/market_depth/client.rb +16 -8
- data/lib/dhan_hq.rb +37 -32
- data/lib/ta/indicators.rb +15 -18
- metadata +7 -9
- data/CODE_REVIEW_ISSUES.md +0 -397
- data/FIXES_APPLIED.md +0 -373
- data/RELEASING.md +0 -60
- data/REVIEW_SUMMARY.md +0 -120
- data/VERSION_UPDATE.md +0 -82
- data/diagram.md +0 -34
- data/docs/ARCHIVE_README.md +0 -784
data/docs/ARCHIVE_README.md
DELETED
|
@@ -1,784 +0,0 @@
|
|
|
1
|
-
# DhanHQ - Ruby Client for DhanHQ API
|
|
2
|
-
|
|
3
|
-
DhanHQ is a **Ruby client** for interacting with **Dhan API v2.0**. It provides **ActiveRecord-like** behavior, **RESTful resource management**, and **ActiveModel validation** for seamless integration into **algorithmic trading applications**.
|
|
4
|
-
|
|
5
|
-
## ⚡ Features
|
|
6
|
-
|
|
7
|
-
✅ **ORM-like Interface** (`find`, `all`, `where`, `save`, `update`, `destroy`)
|
|
8
|
-
✅ **ActiveModel Integration** (`validations`, `errors`, `serialization`)
|
|
9
|
-
✅ **Resource Objects for Trading** (`Orders`, `Positions`, `Holdings`, etc.)
|
|
10
|
-
✅ **Supports WebSockets for Market Feeds**
|
|
11
|
-
✅ **Error Handling & Validations** (`ActiveModel::Errors`)
|
|
12
|
-
✅ **DRY & Modular Code** (`Helpers`, `Contracts`, `Request Handling`)
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
## 📌 Installation
|
|
17
|
-
|
|
18
|
-
Add this line to your application's Gemfile:
|
|
19
|
-
|
|
20
|
-
```ruby
|
|
21
|
-
gem 'DhanHQ', git: 'https://github.com/shubhamtaywade82/dhanhq-client.git', branch: 'main'
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Then execute:
|
|
25
|
-
|
|
26
|
-
```
|
|
27
|
-
bundle install
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
Or install it manually:
|
|
31
|
-
|
|
32
|
-
```
|
|
33
|
-
gem install dhanhq
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
🔹 Configuration
|
|
37
|
-
Set your DhanHQ API credentials:
|
|
38
|
-
|
|
39
|
-
```ruby
|
|
40
|
-
require 'DhanHQ'
|
|
41
|
-
|
|
42
|
-
DhanHQ.configure_with_env
|
|
43
|
-
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
**Minimum environment variables**
|
|
47
|
-
|
|
48
|
-
* `DHAN_CLIENT_ID` – trading account client id issued by Dhan.
|
|
49
|
-
* `DHAN_ACCESS_TOKEN` – API access token generated from the Dhan console.
|
|
50
|
-
|
|
51
|
-
If either key is missing `configure_with_env` raises an error. Ensure your
|
|
52
|
-
application loads them into `ENV` before requiring the gem.
|
|
53
|
-
|
|
54
|
-
**Optional overrides**
|
|
55
|
-
|
|
56
|
-
* `DHAN_LOG_LEVEL` – change logger verbosity (`INFO` default).
|
|
57
|
-
* `DHAN_BASE_URL` – point REST calls to an alternate host.
|
|
58
|
-
* `DHAN_WS_VERSION` – lock WebSocket connections to a specific version.
|
|
59
|
-
* `DHAN_WS_ORDER_URL` – override the order update WebSocket endpoint.
|
|
60
|
-
* `DHAN_WS_USER_TYPE` – choose between `SELF` and `PARTNER` streaming modes.
|
|
61
|
-
* `DHAN_PARTNER_ID` / `DHAN_PARTNER_SECRET` – required when streaming as a partner.
|
|
62
|
-
|
|
63
|
-
Create a `.env` file in your project root to supply the minimum values (and any
|
|
64
|
-
optional overrides you need):
|
|
65
|
-
|
|
66
|
-
```dotenv
|
|
67
|
-
DHAN_CLIENT_ID=your_client_id
|
|
68
|
-
DHAN_ACCESS_TOKEN=your_access_token
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
The gem requires `dotenv/load`, so these variables are loaded automatically when you require `DhanHQ`.
|
|
72
|
-
|
|
73
|
-
To override defaults (base URL, WebSocket settings, partner credentials), set
|
|
74
|
-
`DHAN_BASE_URL`, `DHAN_WS_VERSION`, `DHAN_WS_ORDER_URL`, `DHAN_WS_USER_TYPE`,
|
|
75
|
-
`DHAN_PARTNER_ID`, and `DHAN_PARTNER_SECRET` before calling
|
|
76
|
-
`configure_with_env`.
|
|
77
|
-
|
|
78
|
-
## 🚀 Usage
|
|
79
|
-
|
|
80
|
-
✅ Placing an Order
|
|
81
|
-
|
|
82
|
-
```ruby
|
|
83
|
-
order = DhanHQ::Models::Order.new(
|
|
84
|
-
transaction_type: DhanHQ::Constants::TransactionType::BUY,
|
|
85
|
-
exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
|
|
86
|
-
product_type: DhanHQ::Constants::ProductType::MARGIN,
|
|
87
|
-
order_type: DhanHQ::Constants::OrderType::LIMIT,
|
|
88
|
-
validity: DhanHQ::Constants::Validity::DAY,
|
|
89
|
-
security_id: "43492",
|
|
90
|
-
quantity: 125,
|
|
91
|
-
price: 100.0
|
|
92
|
-
)
|
|
93
|
-
|
|
94
|
-
order.save
|
|
95
|
-
puts order.persisted? # true
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
✅ Fetching an Order
|
|
99
|
-
|
|
100
|
-
```ruby
|
|
101
|
-
order = DhanHQ::Models::Order.find("452501297117")
|
|
102
|
-
puts order.price # Current price of the order
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
✅ Updating an Order
|
|
106
|
-
|
|
107
|
-
```ruby
|
|
108
|
-
order.update(price: 105.0)
|
|
109
|
-
puts order.price # 105.0
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
✅ Canceling an Order
|
|
113
|
-
|
|
114
|
-
```ruby
|
|
115
|
-
order.cancel
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
✅ Fetching All Orders
|
|
119
|
-
|
|
120
|
-
```ruby
|
|
121
|
-
orders = DhanHQ::Models::Order.all
|
|
122
|
-
puts orders.count
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
✅ Querying Orders
|
|
126
|
-
|
|
127
|
-
```ruby
|
|
128
|
-
pending_orders = DhanHQ::Models::Order.where(status: DhanHQ::Constants::OrderStatus::PENDING)
|
|
129
|
-
puts pending_orders.first.order_id
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
✅ Exiting Positions
|
|
133
|
-
|
|
134
|
-
```ruby
|
|
135
|
-
positions = DhanHQ::Models::Position.all
|
|
136
|
-
position = positions.first
|
|
137
|
-
position.exit!
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Orders
|
|
141
|
-
|
|
142
|
-
#### Place
|
|
143
|
-
|
|
144
|
-
```ruby
|
|
145
|
-
order = DhanHQ::Models::Order.new(transaction_type: DhanHQ::Constants::TransactionType::BUY, security_id: "123", quantity: 1)
|
|
146
|
-
order.save
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
#### Modify
|
|
150
|
-
|
|
151
|
-
```ruby
|
|
152
|
-
order.modify(price: 102.5)
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
#### Cancel
|
|
156
|
-
|
|
157
|
-
```ruby
|
|
158
|
-
order.cancel
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### Trades
|
|
162
|
-
|
|
163
|
-
```ruby
|
|
164
|
-
DhanHQ::Models::Trade.today
|
|
165
|
-
DhanHQ::Models::Trade.find_by_order_id("452501297117")
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
### Positions
|
|
169
|
-
|
|
170
|
-
```ruby
|
|
171
|
-
positions = DhanHQ::Models::Position.all
|
|
172
|
-
active = DhanHQ::Models::Position.active
|
|
173
|
-
DhanHQ::Models::Position.convert(position_id: "1", product_type: DhanHQ::Constants::ProductType::CNC)
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### Holdings
|
|
177
|
-
|
|
178
|
-
```ruby
|
|
179
|
-
DhanHQ::Models::Holding.all
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
### Funds
|
|
183
|
-
|
|
184
|
-
```ruby
|
|
185
|
-
DhanHQ::Models::Funds.fetch
|
|
186
|
-
balance = DhanHQ::Models::Funds.balance
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
### Option Chain
|
|
190
|
-
|
|
191
|
-
```ruby
|
|
192
|
-
DhanHQ::Models::OptionChain.fetch(security_id: "1333", expiry_date: "2024-06-30")
|
|
193
|
-
DhanHQ::Models::OptionChain.fetch_expiry_list(security_id: "1333")
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Historical Data
|
|
197
|
-
|
|
198
|
-
```ruby
|
|
199
|
-
DhanHQ::Models::HistoricalData.daily(security_id: "1333", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, instrument: DhanHQ::Constants::InstrumentType::EQUITY, from_date: "2024-01-01", to_date: "2024-01-31")
|
|
200
|
-
DhanHQ::Models::HistoricalData.intraday(security_id: "1333", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, instrument: DhanHQ::Constants::InstrumentType::EQUITY, interval: "15", from_date: "2024-09-11", to_date: "2024-09-15")
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### Instrument Model with Convenience Methods
|
|
204
|
-
|
|
205
|
-
The Instrument model provides convenient instance methods that automatically use the instrument's attributes:
|
|
206
|
-
|
|
207
|
-
```ruby
|
|
208
|
-
# Find an instrument
|
|
209
|
-
instrument = DhanHQ::Models::Instrument.find("IDX_I", "NIFTY")
|
|
210
|
-
|
|
211
|
-
# Market Feed Methods - automatically uses instrument's attributes
|
|
212
|
-
ltp_data = instrument.ltp # Last traded price
|
|
213
|
-
ohlc_data = instrument.ohlc # OHLC data
|
|
214
|
-
quote_data = instrument.quote # Full quote depth
|
|
215
|
-
|
|
216
|
-
# Historical Data Methods
|
|
217
|
-
daily_data = instrument.daily(from_date: "2024-01-01", to_date: "2024-01-31")
|
|
218
|
-
intraday_data = instrument.intraday(from_date: "2024-09-11", to_date: "2024-09-15", interval: "15")
|
|
219
|
-
|
|
220
|
-
# Option Chain Methods
|
|
221
|
-
expiries = instrument.expiry_list
|
|
222
|
-
chain = instrument.option_chain(expiry: "2024-02-29")
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
## 🔹 Available Resources
|
|
226
|
-
|
|
227
|
-
| Resource | Model | Actions |
|
|
228
|
-
| ------------------------ | ---------------------------------------- | --------------------------------------------------- |
|
|
229
|
-
| Orders | `DhanHQ::Models::Models::Order` | `find`, `all`, `where`, `place`, `update`, `cancel` |
|
|
230
|
-
| Trades | `DhanHQ::Models::Models::Trade` | `all`, `find_by_order_id` |
|
|
231
|
-
| Forever Orders | `DhanHQ::Models::Models::ForeverOrder` | `create`, `find`, `modify`, `cancel`, `all` |
|
|
232
|
-
| Holdings | `DhanHQ::Models::Models::Holding` | `all` |
|
|
233
|
-
| Positions | `DhanHQ::Models::Models::Position` | `all`, `find`, `exit!` |
|
|
234
|
-
| Funds & Margin | `DhanHQ::Models::Models::Funds` | `fund_limit`, `margin_calculator` |
|
|
235
|
-
| Ledger | `DhanHQ::Models::Models::Ledger` | `all` |
|
|
236
|
-
| Market Feeds | `DhanHQ::Models::Models::MarketFeed` | `ltp, ohlc`, `quote` |
|
|
237
|
-
| Historical Data (Charts) | `DhanHQ::Models::Models::HistoricalData` | `daily`, `intraday` |
|
|
238
|
-
| Option Chain | `DhanHQ::Models::Models::OptionChain` | `fetch`, `fetch_expiry_list` |
|
|
239
|
-
|
|
240
|
-
## 📌 Development
|
|
241
|
-
|
|
242
|
-
Set `DHAN_DEBUG=true` to log HTTP requests during development:
|
|
243
|
-
|
|
244
|
-
```bash
|
|
245
|
-
export DHAN_DEBUG=true
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
Running Tests
|
|
249
|
-
|
|
250
|
-
```bash
|
|
251
|
-
bundle exec rspec
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
Installing Locally
|
|
255
|
-
|
|
256
|
-
```bash
|
|
257
|
-
bundle exec rake install
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
Releasing a New Version
|
|
261
|
-
|
|
262
|
-
```bash
|
|
263
|
-
bundle exec rake release
|
|
264
|
-
```
|
|
265
|
-
---
|
|
266
|
-
|
|
267
|
-
## WebSocket Market Feed (NEW)
|
|
268
|
-
|
|
269
|
-
### What you get
|
|
270
|
-
|
|
271
|
-
* **Modes**
|
|
272
|
-
|
|
273
|
-
* `:ticker` → LTP + LTT
|
|
274
|
-
* `:quote` → OHLCV + totals (recommended default)
|
|
275
|
-
* `:full` → quote + **OI** + **best-5 depth**
|
|
276
|
-
* **Normalized ticks** (Hash):
|
|
277
|
-
|
|
278
|
-
```ruby
|
|
279
|
-
{
|
|
280
|
-
kind: :quote, # :ticker | :quote | :full | :oi | :prev_close | :misc
|
|
281
|
-
segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO, # string enum
|
|
282
|
-
security_id: "12345",
|
|
283
|
-
ltp: 101.5,
|
|
284
|
-
ts: 1723791300, # LTT epoch (sec) if present
|
|
285
|
-
vol: 123456, # quote/full
|
|
286
|
-
atp: 100.9, # quote/full
|
|
287
|
-
day_open: 100.1, day_high: 102.4, day_low: 99.5, day_close: nil,
|
|
288
|
-
oi: 987654, # full or OI packet
|
|
289
|
-
bid: 101.45, ask: 101.55 # from depth (mode :full)
|
|
290
|
-
}
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Start, subscribe, stop
|
|
294
|
-
|
|
295
|
-
```ruby
|
|
296
|
-
DhanHQ.configure_with_env
|
|
297
|
-
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
|
298
|
-
|
|
299
|
-
ws = DhanHQ::WS::Client.new(mode: :quote).start
|
|
300
|
-
|
|
301
|
-
ws.on(:tick) do |t|
|
|
302
|
-
puts "[#{t[:segment]}:#{t[:security_id]}] LTP=#{t[:ltp]} kind=#{t[:kind]}"
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
# Subscribe instruments (≤100 per frame; send multiple frames if needed)
|
|
306
|
-
ws.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13") # NIFTY index value
|
|
307
|
-
ws.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO, security_id: "12345") # an option
|
|
308
|
-
|
|
309
|
-
# Unsubscribe
|
|
310
|
-
ws.unsubscribe_one(segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO, security_id: "12345")
|
|
311
|
-
|
|
312
|
-
# Graceful disconnect (sends broker disconnect code 12, no reconnect)
|
|
313
|
-
ws.disconnect!
|
|
314
|
-
|
|
315
|
-
# Or hard stop (no broker message, just closes and halts loop)
|
|
316
|
-
ws.stop
|
|
317
|
-
|
|
318
|
-
# Safety: kill all local sockets (useful in IRB)
|
|
319
|
-
DhanHQ::WS.disconnect_all_local!
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
### Under the hood
|
|
323
|
-
|
|
324
|
-
* **Request codes** (per Dhan docs)
|
|
325
|
-
|
|
326
|
-
* Subscribe: **15** (ticker), **17** (quote), **21** (full)
|
|
327
|
-
* Unsubscribe: **16**, **18**, **22**
|
|
328
|
-
* Disconnect: **12**
|
|
329
|
-
* **Limits**
|
|
330
|
-
|
|
331
|
-
* Up to **100 instruments per SUB/UNSUB** message (client auto-chunks)
|
|
332
|
-
* Up to 5 WS connections per user (per Dhan)
|
|
333
|
-
* **Backoff & 429 cool-off**
|
|
334
|
-
|
|
335
|
-
* Exponential backoff with jitter
|
|
336
|
-
* Handshake **429** triggers a **60s cool-off** before retry
|
|
337
|
-
* **Reconnect & resubscribe**
|
|
338
|
-
|
|
339
|
-
* On reconnect the client resends the **current subscription snapshot** (idempotent)
|
|
340
|
-
* **Graceful shutdown**
|
|
341
|
-
|
|
342
|
-
* `ws.disconnect!` or `ws.stop` prevents reconnects
|
|
343
|
-
* An `at_exit` hook stops all registered WS clients to avoid leaked sockets
|
|
344
|
-
|
|
345
|
-
---
|
|
346
|
-
|
|
347
|
-
## Exchange Segment Enums
|
|
348
|
-
|
|
349
|
-
Use the string enums below in WS `subscribe_*` and REST params:
|
|
350
|
-
|
|
351
|
-
| Enum | Exchange | Segment |
|
|
352
|
-
| -------------- | -------- | ----------------- |
|
|
353
|
-
| `IDX_I` | Index | Index Value |
|
|
354
|
-
| `NSE_EQ` | NSE | Equity Cash |
|
|
355
|
-
| `NSE_FNO` | NSE | Futures & Options |
|
|
356
|
-
| `NSE_CURRENCY` | NSE | Currency |
|
|
357
|
-
| `BSE_EQ` | BSE | Equity Cash |
|
|
358
|
-
| `MCX_COMM` | MCX | Commodity |
|
|
359
|
-
| `BSE_CURRENCY` | BSE | Currency |
|
|
360
|
-
| `BSE_FNO` | BSE | Futures & Options |
|
|
361
|
-
|
|
362
|
-
---
|
|
363
|
-
|
|
364
|
-
## Accessing ticks elsewhere in your app
|
|
365
|
-
|
|
366
|
-
### Direct handler
|
|
367
|
-
|
|
368
|
-
```ruby
|
|
369
|
-
ws.on(:tick) { |t| do_something_fast(t) } # avoid heavy work here
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
### Shared TickCache (recommended)
|
|
373
|
-
|
|
374
|
-
```ruby
|
|
375
|
-
# app/services/live/tick_cache.rb
|
|
376
|
-
class TickCache
|
|
377
|
-
MAP = Concurrent::Map.new
|
|
378
|
-
def self.put(t) = MAP["#{t[:segment]}:#{t[:security_id]}"] = t
|
|
379
|
-
def self.get(seg, sid) = MAP["#{seg}:#{sid}"]
|
|
380
|
-
def self.ltp(seg, sid) = get(seg, sid)&.dig(:ltp)
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
ws.on(:tick) { |t| TickCache.put(t) }
|
|
384
|
-
ltp = TickCache.ltp("NSE_FNO", "12345")
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
### Filtered callback
|
|
388
|
-
|
|
389
|
-
```ruby
|
|
390
|
-
def on_tick_for(ws, segment:, security_id:, &blk)
|
|
391
|
-
key = "#{segment}:#{security_id}"
|
|
392
|
-
ws.on(:tick){ |t| blk.call(t) if "#{t[:segment]}:#{t[:security_id]}" == key }
|
|
393
|
-
end
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
---
|
|
397
|
-
|
|
398
|
-
## Rails integration (example)
|
|
399
|
-
|
|
400
|
-
**Goal:** Generate signals from clean **Historical Intraday OHLC** (5-min bars), and use **WebSocket** only for **exits/trailing** on open option legs.
|
|
401
|
-
|
|
402
|
-
1. **Initializer**
|
|
403
|
-
`config/initializers/dhanhq.rb`
|
|
404
|
-
|
|
405
|
-
```ruby
|
|
406
|
-
DhanHQ.configure_with_env
|
|
407
|
-
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
2. **Start WS supervisor**
|
|
411
|
-
`config/initializers/stream.rb`
|
|
412
|
-
|
|
413
|
-
```ruby
|
|
414
|
-
INDICES = [
|
|
415
|
-
{ segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13" }, # NIFTY index value
|
|
416
|
-
{ segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25" } # BANKNIFTY index value
|
|
417
|
-
]
|
|
418
|
-
|
|
419
|
-
Rails.application.config.to_prepare do
|
|
420
|
-
$WS = DhanHQ::WS::Client.new(mode: :quote).start
|
|
421
|
-
$WS.on(:tick) do |t|
|
|
422
|
-
TickCache.put(t)
|
|
423
|
-
Execution::PositionGuard.instance.on_tick(t) # trailing & fast exits
|
|
424
|
-
end
|
|
425
|
-
INDICES.each { |i| $WS.subscribe_one(segment: i[:segment], security_id: i[:security_id]) }
|
|
426
|
-
end
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
3. **Bar fetch (every 5 min) via Historical API**
|
|
430
|
-
|
|
431
|
-
* Fetch intraday OHLC at 5-minute boundaries.
|
|
432
|
-
* Update your `CandleSeries`; on each closed bar, run strategy to emit signals.
|
|
433
|
-
*(Use your existing `Bars::FetchLoop` + `CandleSeries` code.)*
|
|
434
|
-
|
|
435
|
-
4. **Routing & orders**
|
|
436
|
-
|
|
437
|
-
* On signal: place **Super Order** (SL/TP/TSL) or fallback to Market + local trailing.
|
|
438
|
-
* After a successful place, **register** the leg in `PositionGuard` and **subscribe** its option on WS.
|
|
439
|
-
|
|
440
|
-
5. **Shutdown**
|
|
441
|
-
|
|
442
|
-
```ruby
|
|
443
|
-
at_exit { DhanHQ::WS.disconnect_all_local! }
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
---
|
|
447
|
-
|
|
448
|
-
## Super Orders
|
|
449
|
-
|
|
450
|
-
Super orders are built for smart execution. They bundle entry, target, and stop-loss legs (with optional trailing jump) into one atomic request so the broker manages risk for you.
|
|
451
|
-
|
|
452
|
-
The gem exposes all related REST endpoints so you can create, modify, cancel, and list super orders programmatically across exchanges and segments.
|
|
453
|
-
|
|
454
|
-
### Endpoints
|
|
455
|
-
|
|
456
|
-
| Method | Path | Description |
|
|
457
|
-
| -------- | -------------------------------------- | ------------------------------------- |
|
|
458
|
-
| `POST` | `/super/orders` | Create a new super order |
|
|
459
|
-
| `PUT` | `/super/orders/{order_id}` | Modify a pending super order |
|
|
460
|
-
| `DELETE` | `/super/orders/{order_id}/{order_leg}` | Cancel a pending super order leg |
|
|
461
|
-
| `GET` | `/super/orders` | Retrieve the list of all super orders |
|
|
462
|
-
|
|
463
|
-
### Place Super Order
|
|
464
|
-
|
|
465
|
-
Use this endpoint to create a new super order consisting of entry, target, stop-loss, and optional trailing jump. It is available for intraday, carry-forward, and MTF across supported exchanges.
|
|
466
|
-
|
|
467
|
-
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
468
|
-
|
|
469
|
-
```bash
|
|
470
|
-
curl --request POST \
|
|
471
|
-
--url https://api.dhan.co/v2/super/orders \
|
|
472
|
-
--header 'Content-Type: application/json' \
|
|
473
|
-
--header 'access-token: JWT' \
|
|
474
|
-
--data '{Request JSON}'
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
#### Request body
|
|
478
|
-
|
|
479
|
-
```json
|
|
480
|
-
{
|
|
481
|
-
"dhan_client_id": "1000000003",
|
|
482
|
-
"correlation_id": "123abc678",
|
|
483
|
-
"transaction_type": "BUY",
|
|
484
|
-
"exchange_segment": "NSE_EQ",
|
|
485
|
-
"product_type": "CNC",
|
|
486
|
-
"order_type": "LIMIT",
|
|
487
|
-
"security_id": "11536",
|
|
488
|
-
"quantity": 5,
|
|
489
|
-
"price": 1500,
|
|
490
|
-
"target_price": 1600,
|
|
491
|
-
"stop_loss_price": 1400,
|
|
492
|
-
"trailing_jump": 10
|
|
493
|
-
}
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
#### Parameters
|
|
497
|
-
|
|
498
|
-
| Field | Type | Description |
|
|
499
|
-
| ------------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
500
|
-
| `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. When you call via `DhanHQ::Models::SuperOrder`, the gem injects your configured client id so you can omit the key in Ruby. |
|
|
501
|
-
| `correlation_id` | string | Caller supplied identifier for tracking |
|
|
502
|
-
| `transaction_type` | enum string *(required)* | Trading side. `BUY` or `SELL`. |
|
|
503
|
-
| `exchange_segment` | enum string *(required)* | Exchange segment (see appendix). |
|
|
504
|
-
| `product_type` | enum string *(required)* | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
|
|
505
|
-
| `order_type` | enum string *(required)* | Order type. `LIMIT` or `MARKET`. |
|
|
506
|
-
| `security_id` | string *(required)* | Exchange standard security identifier. |
|
|
507
|
-
| `quantity` | integer *(required)* | Quantity for the entry leg. |
|
|
508
|
-
| `price` | float *(required)* | Entry price. |
|
|
509
|
-
| `target_price` | float *(required)* | Target price for the super order. |
|
|
510
|
-
| `stop_loss_price` | float *(required)* | Stop-loss price for the super order. |
|
|
511
|
-
| `trailing_jump` | float *(required)* | Price jump size for trailing stop-loss. |
|
|
512
|
-
|
|
513
|
-
> 🐍 When you call `DhanHQ::Models::SuperOrder.create`, supply snake_case keys exactly as shown. The client automatically camelizes
|
|
514
|
-
> them before submitting to Dhan's REST API and injects your configured `dhan_client_id`, allowing you to omit the key in Ruby code.
|
|
515
|
-
|
|
516
|
-
#### Response
|
|
517
|
-
|
|
518
|
-
```json
|
|
519
|
-
{
|
|
520
|
-
"order_id": "112111182198",
|
|
521
|
-
"order_status": "PENDING"
|
|
522
|
-
}
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
| Field | Type | Description |
|
|
526
|
-
| -------------- | ----------- | --------------------------------------------------- |
|
|
527
|
-
| `order_id` | string | Order identifier generated by Dhan |
|
|
528
|
-
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, or `REJECTED`. |
|
|
529
|
-
|
|
530
|
-
### Modify Super Order
|
|
531
|
-
|
|
532
|
-
Modify any leg while the order is `PENDING` or `PART_TRADED`. Once the entry leg trades fully, only the target and stop-loss legs (price and trailing jump) remain editable.
|
|
533
|
-
|
|
534
|
-
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
535
|
-
|
|
536
|
-
```bash
|
|
537
|
-
curl --request PUT \
|
|
538
|
-
--url https://api.dhan.co/v2/super/orders/{order_id} \
|
|
539
|
-
--header 'Content-Type: application/json' \
|
|
540
|
-
--header 'access-token: JWT' \
|
|
541
|
-
--data '{Request JSON}'
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
#### Request body
|
|
545
|
-
|
|
546
|
-
```json
|
|
547
|
-
{
|
|
548
|
-
"dhan_client_id": "1000000009",
|
|
549
|
-
"order_id": "112111182045",
|
|
550
|
-
"order_type": "LIMIT",
|
|
551
|
-
"leg_name": "ENTRY_LEG",
|
|
552
|
-
"quantity": 40,
|
|
553
|
-
"price": 1300,
|
|
554
|
-
"target_price": 1450,
|
|
555
|
-
"stop_loss_price": 1350,
|
|
556
|
-
"trailing_jump": 20
|
|
557
|
-
}
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
#### Parameters
|
|
561
|
-
|
|
562
|
-
| Field | Type | Description |
|
|
563
|
-
| ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
564
|
-
| `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. Automatically merged when using the Ruby model helpers. |
|
|
565
|
-
| `order_id` | string *(required)* | Super order identifier generated by Dhan. |
|
|
566
|
-
| `order_type` | enum string *(conditionally required)* | `LIMIT` or `MARKET`. Required when changing `ENTRY_LEG`. |
|
|
567
|
-
| `leg_name` | enum string *(required)* | `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. Entry edits allowed only while status is `PENDING` or `PART_TRADED`. |
|
|
568
|
-
| `quantity` | integer *(conditionally required)* | Quantity update for `ENTRY_LEG`. |
|
|
569
|
-
| `price` | float *(conditionally required)* | Entry price update for `ENTRY_LEG`. |
|
|
570
|
-
| `target_price` | float *(conditionally required)* | Target price update for `ENTRY_LEG` or `TARGET_LEG`. |
|
|
571
|
-
| `stop_loss_price` | float *(conditionally required)* | Stop-loss price update for `ENTRY_LEG` or `STOP_LOSS_LEG`. |
|
|
572
|
-
| `trailing_jump` | float *(conditionally required)* | Trailing jump update for `ENTRY_LEG` or `STOP_LOSS_LEG`. Omit or set to `0` to cancel trailing. |
|
|
573
|
-
|
|
574
|
-
#### Response
|
|
575
|
-
|
|
576
|
-
```json
|
|
577
|
-
{
|
|
578
|
-
"order_id": "112111182045",
|
|
579
|
-
"order_status": "TRANSIT"
|
|
580
|
-
}
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
| Field | Type | Description |
|
|
584
|
-
| -------------- | ----------- | ------------------------------------------------------------- |
|
|
585
|
-
| `order_id` | string | Order identifier generated by Dhan |
|
|
586
|
-
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `TRADED`. |
|
|
587
|
-
|
|
588
|
-
### Cancel Super Order
|
|
589
|
-
|
|
590
|
-
Cancel a pending or active super order leg with the order ID. Cancelling the entry leg removes all legs. Cancelling an individual target or stop-loss leg removes only that leg and it cannot be recreated on the same order.
|
|
591
|
-
|
|
592
|
-
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
593
|
-
|
|
594
|
-
```bash
|
|
595
|
-
curl --request DELETE \
|
|
596
|
-
--url https://api.dhan.co/v2/super/orders/{order_id}/{order_leg} \
|
|
597
|
-
--header 'Content-Type: application/json' \
|
|
598
|
-
--header 'access-token: JWT'
|
|
599
|
-
```
|
|
600
|
-
|
|
601
|
-
#### Path parameters
|
|
602
|
-
|
|
603
|
-
| Field | Description | Example |
|
|
604
|
-
| ----------- | ------------------------------------------------------------- | ------------- |
|
|
605
|
-
| `order_id` | Super order identifier. | `11211182198` |
|
|
606
|
-
| `order_leg` | Leg to cancel. `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. | `ENTRY_LEG` |
|
|
607
|
-
|
|
608
|
-
#### Response
|
|
609
|
-
|
|
610
|
-
```json
|
|
611
|
-
{
|
|
612
|
-
"order_id": "112111182045",
|
|
613
|
-
"order_status": "CANCELLED"
|
|
614
|
-
}
|
|
615
|
-
```
|
|
616
|
-
|
|
617
|
-
| Field | Type | Description |
|
|
618
|
-
| -------------- | ----------- | ---------------------------------------------------------------- |
|
|
619
|
-
| `order_id` | string | Order identifier generated by Dhan |
|
|
620
|
-
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `CANCELLED`. |
|
|
621
|
-
|
|
622
|
-
### Super Order List
|
|
623
|
-
|
|
624
|
-
Fetch all super orders placed during the trading day. The response nests leg details under the entry leg, and each leg is also available in the standard order book.
|
|
625
|
-
|
|
626
|
-
```bash
|
|
627
|
-
curl --request GET \
|
|
628
|
-
--url https://api.dhan.co/v2/super/orders \
|
|
629
|
-
--header 'Content-Type: application/json' \
|
|
630
|
-
--header 'access-token: JWT'
|
|
631
|
-
```
|
|
632
|
-
|
|
633
|
-
#### Response
|
|
634
|
-
|
|
635
|
-
```json
|
|
636
|
-
[
|
|
637
|
-
{
|
|
638
|
-
"dhan_client_id": "1100003626",
|
|
639
|
-
"order_id": "5925022734212",
|
|
640
|
-
"correlation_id": "string",
|
|
641
|
-
"order_status": "PENDING",
|
|
642
|
-
"transaction_type": "BUY",
|
|
643
|
-
"exchange_segment": "NSE_EQ",
|
|
644
|
-
"product_type": "CNC",
|
|
645
|
-
"order_type": "LIMIT",
|
|
646
|
-
"validity": "DAY",
|
|
647
|
-
"trading_symbol": "HDFCBANK",
|
|
648
|
-
"security_id": "1333",
|
|
649
|
-
"quantity": 10,
|
|
650
|
-
"remaining_quantity": 10,
|
|
651
|
-
"ltp": 1660.95,
|
|
652
|
-
"price": 1500,
|
|
653
|
-
"after_market_order": false,
|
|
654
|
-
"leg_name": "ENTRY_LEG",
|
|
655
|
-
"exchange_order_id": "11925022734212",
|
|
656
|
-
"create_time": "2025-02-27 19:09:42",
|
|
657
|
-
"update_time": "2025-02-27 19:09:42",
|
|
658
|
-
"exchange_time": "2025-02-27 19:09:42",
|
|
659
|
-
"oms_error_description": "",
|
|
660
|
-
"average_traded_price": 0,
|
|
661
|
-
"filled_qty": 0,
|
|
662
|
-
"leg_details": [
|
|
663
|
-
{
|
|
664
|
-
"order_id": "5925022734212",
|
|
665
|
-
"leg_name": "STOP_LOSS_LEG",
|
|
666
|
-
"transaction_type": "SELL",
|
|
667
|
-
"total_quantity": 0,
|
|
668
|
-
"remaining_quantity": 0,
|
|
669
|
-
"triggered_quantity": 0,
|
|
670
|
-
"price": 1400,
|
|
671
|
-
"order_status": "PENDING",
|
|
672
|
-
"trailing_jump": 10
|
|
673
|
-
},
|
|
674
|
-
{
|
|
675
|
-
"order_id": "5925022734212",
|
|
676
|
-
"leg_name": "TARGET_LEG",
|
|
677
|
-
"transaction_type": "SELL",
|
|
678
|
-
"remaining_quantity": 0,
|
|
679
|
-
"triggered_quantity": 0,
|
|
680
|
-
"price": 1550,
|
|
681
|
-
"order_status": "PENDING",
|
|
682
|
-
"trailing_jump": 0
|
|
683
|
-
}
|
|
684
|
-
]
|
|
685
|
-
}
|
|
686
|
-
]
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
#### Parameters
|
|
690
|
-
|
|
691
|
-
| Field | Type | Description |
|
|
692
|
-
| ----------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
|
|
693
|
-
| `dhan_client_id` | string | User specific identification generated by Dhan. |
|
|
694
|
-
| `order_id` | string | Order identifier generated by Dhan. |
|
|
695
|
-
| `correlation_id` | string | Caller supplied correlation identifier. |
|
|
696
|
-
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `CLOSED`, `REJECTED`, `CANCELLED`, `PART_TRADED`, or `TRADED`. |
|
|
697
|
-
| `transaction_type` | enum string | Trading side. `BUY` or `SELL`. |
|
|
698
|
-
| `exchange_segment` | enum string | Exchange segment. |
|
|
699
|
-
| `product_type` | enum string | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
|
|
700
|
-
| `order_type` | enum string | Order type. `LIMIT` or `MARKET`. |
|
|
701
|
-
| `validity` | enum string | Order validity. `DAY`. |
|
|
702
|
-
| `trading_symbol` | string | Trading symbol reference. |
|
|
703
|
-
| `security_id` | string | Exchange security identifier. |
|
|
704
|
-
| `quantity` | integer | Ordered quantity. |
|
|
705
|
-
| `remaining_quantity` | integer | Quantity pending execution. |
|
|
706
|
-
| `ltp` | float | Last traded price. |
|
|
707
|
-
| `price` | float | Order price. |
|
|
708
|
-
| `after_market_order` | boolean | Indicates if order was placed after market hours. |
|
|
709
|
-
| `leg_name` | enum string | Leg identifier: `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. |
|
|
710
|
-
| `trailing_jump` | float | Trailing jump for stop-loss. |
|
|
711
|
-
| `exchange_order_id` | string | Exchange generated identifier. |
|
|
712
|
-
| `create_time` | string | Order creation timestamp. |
|
|
713
|
-
| `update_time` | string | Latest update timestamp. |
|
|
714
|
-
| `exchange_time` | string | Exchange timestamp. |
|
|
715
|
-
| `oms_error_description` | string | OMS error description when applicable. |
|
|
716
|
-
| `average_traded_price` | float | Average traded price. |
|
|
717
|
-
| `filled_qty` | integer | Quantity traded on the exchange. |
|
|
718
|
-
| `triggered_quantity` | integer | Quantity triggered for stop-loss or target legs. |
|
|
719
|
-
| `leg_details` | array | Nested leg details for the super order. |
|
|
720
|
-
|
|
721
|
-
> ✅ `CLOSED` indicates the entry leg and either target or stop-loss completed for the full quantity. `TRIGGERED` appears on target and stop-loss legs to highlight which one fired; inspect `triggered_quantity` for executed quantity.
|
|
722
|
-
|
|
723
|
-
---
|
|
724
|
-
|
|
725
|
-
## Packet parsing (for reference)
|
|
726
|
-
|
|
727
|
-
* **Response Header (8 bytes)**:
|
|
728
|
-
`feed_response_code (u8, BE)`, `message_length (u16, BE)`, `exchange_segment (u8, BE)`, `security_id (i32, LE)`
|
|
729
|
-
* **Packets supported**:
|
|
730
|
-
|
|
731
|
-
* **1** Index (surface as raw/misc unless documented)
|
|
732
|
-
* **2** Ticker: `ltp`, `ltt`
|
|
733
|
-
* **4** Quote: `ltp`, `ltt`, `atp`, `volume`, totals, `day_*`
|
|
734
|
-
* **5** OI: `open_interest`
|
|
735
|
-
* **6** Prev Close: `prev_close`, `oi_prev`
|
|
736
|
-
* **7** Market Status (raw/misc unless documented)
|
|
737
|
-
* **8** Full: quote + `open_interest` + 5× depth (bid/ask)
|
|
738
|
-
* **50** Disconnect: reason code
|
|
739
|
-
|
|
740
|
-
---
|
|
741
|
-
|
|
742
|
-
## Best practices
|
|
743
|
-
|
|
744
|
-
* Keep the `on(:tick)` handler **non-blocking**; push work to a queue/thread.
|
|
745
|
-
* Use `mode: :quote` for most strategies; switch to `:full` only if you need depth/OI in real-time.
|
|
746
|
-
* Call **`ws.disconnect!`** (or `ws.stop`) when leaving IRB / tests.
|
|
747
|
-
Use **`DhanHQ::WS.disconnect_all_local!`** to be extra safe.
|
|
748
|
-
* Don’t exceed **100 instruments per SUB frame** (the client auto-chunks).
|
|
749
|
-
* Avoid rapid connect/disconnect loops; the client already **backs off & cools off** when server replies 429.
|
|
750
|
-
|
|
751
|
-
---
|
|
752
|
-
|
|
753
|
-
## Troubleshooting
|
|
754
|
-
|
|
755
|
-
* **429: Unexpected response code**
|
|
756
|
-
You connected too frequently or have too many sockets. The client auto-cools off for **60s** and backs off. Prefer `ws.disconnect!` before reconnecting; and call `DhanHQ::WS.disconnect_all_local!` to kill stragglers.
|
|
757
|
-
* **No ticks after reconnect**
|
|
758
|
-
Ensure you re-subscribed after a clean start (the client resends the snapshot automatically on reconnect).
|
|
759
|
-
* **Binary parse errors**
|
|
760
|
-
Run with `DHAN_LOG_LEVEL=DEBUG` to inspect; we safely drop malformed frames and keep the loop alive.
|
|
761
|
-
|
|
762
|
-
---
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
## 📌 Contributing
|
|
766
|
-
|
|
767
|
-
Bug reports and pull requests are welcome on GitHub at:
|
|
768
|
-
🔗 <https://github.com/shubhamtaywade82/dhanhq>
|
|
769
|
-
|
|
770
|
-
This project follows a code of conduct to maintain a safe and welcoming community.
|
|
771
|
-
|
|
772
|
-
## 📌 License
|
|
773
|
-
|
|
774
|
-
This gem is available under the MIT License.
|
|
775
|
-
🔗 <https://opensource.org/licenses/MIT>
|
|
776
|
-
|
|
777
|
-
## 📌 Code of Conduct
|
|
778
|
-
|
|
779
|
-
Everyone interacting in the DhanHQ project is expected to follow the
|
|
780
|
-
🔗 Code of Conduct.
|
|
781
|
-
|
|
782
|
-
```markdown
|
|
783
|
-
This **README.md** file is structured and formatted for **GitHub** or any **Markdown-compatible** documentation system. 🚀
|
|
784
|
-
```
|