DhanHQ 2.6.2 → 2.7.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/.rubocop.yml +15 -3
- data/AGENTS.md +23 -0
- data/ARCHITECTURE.md +115 -0
- data/CHANGELOG.md +65 -0
- data/README.md +55 -0
- data/docs/API_VERIFICATION.md +10 -8
- data/docs/ENDPOINTS_AND_SANDBOX.md +12 -0
- data/lib/DhanHQ/auth.rb +2 -2
- data/lib/DhanHQ/client.rb +42 -34
- data/lib/DhanHQ/concerns/order_audit.rb +69 -0
- data/lib/DhanHQ/configuration.rb +5 -6
- data/lib/DhanHQ/constants.rb +67 -7
- 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 +9 -9
- data/lib/DhanHQ/core/base_model.rb +4 -1
- data/lib/DhanHQ/core/error_handler.rb +2 -2
- data/lib/DhanHQ/errors.rb +16 -2
- data/lib/DhanHQ/helpers/request_helper.rb +11 -2
- 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 +16 -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/resources/alert_orders.rb +23 -1
- data/lib/DhanHQ/resources/edis.rb +4 -3
- data/lib/DhanHQ/resources/forever_orders.rb +10 -0
- 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 +13 -1
- data/lib/DhanHQ/resources/pnl_exit.rb +8 -0
- data/lib/DhanHQ/resources/super_orders.rb +21 -2
- data/lib/DhanHQ/resources/trader_control.rb +13 -4
- data/lib/DhanHQ/utils/network_inspector.rb +71 -0
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/base_connection.rb +1 -1
- data/lib/DhanHQ/ws/market_depth/client.rb +11 -4
- data/lib/dhan_hq.rb +17 -20
- data/lib/ta/indicators.rb +15 -18
- metadata +9 -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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: df325d70c7c2e3c13493ad199ca8e11bb7f5a9df1d62a775452e4fb1eeed5842
|
|
4
|
+
data.tar.gz: 7cfde3f38fa073311ad50992ef497ba7391927b78136346e7549c135e7a4d7cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 945e4a71c15f01e33f7f05a3ed2453e7558c46d9c9bb5067a6225e68e87b4c3aac4a903ff08da384b488f9b8d36764f9f1f47103a8a8db4d5b90d1ea97e09252
|
|
7
|
+
data.tar.gz: 1b805c79a999c6604cd58d8df163c339233b09732d77c1911010dfa3d5ea32e12ae27d166b1484621338ba378acace69003328e9e6d4ec3654eb646df753c2c0
|
data/.rubocop.yml
CHANGED
|
@@ -28,9 +28,21 @@ RSpec/ExampleLength:
|
|
|
28
28
|
RSpec/MultipleMemoizedHelpers:
|
|
29
29
|
Max: 10
|
|
30
30
|
|
|
31
|
+
Lint/ScriptPermission:
|
|
32
|
+
Exclude:
|
|
33
|
+
- "bin/**/*"
|
|
34
|
+
|
|
35
|
+
Metrics/AbcSize:
|
|
36
|
+
Exclude:
|
|
37
|
+
- "bin/call_all_endpoints.rb"
|
|
38
|
+
|
|
39
|
+
Layout/LineLength:
|
|
40
|
+
Exclude:
|
|
41
|
+
- "bin/call_all_endpoints.rb"
|
|
42
|
+
|
|
31
43
|
DhanHQ/UseConstants:
|
|
32
44
|
Enabled: true
|
|
33
45
|
Exclude:
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
46
|
+
- "lib/DhanHQ/constants.rb"
|
|
47
|
+
- "lib/DhanHQ/ws/segments.rb"
|
|
48
|
+
- "spec/**/*"
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Cursor Cloud specific instructions
|
|
4
|
+
|
|
5
|
+
This is a pure-Ruby gem (no Rails, no running servers). Development commands are documented in `CLAUDE.md`.
|
|
6
|
+
|
|
7
|
+
### Quick reference
|
|
8
|
+
|
|
9
|
+
| Task | Command |
|
|
10
|
+
|------|---------|
|
|
11
|
+
| Install deps | `bundle install` |
|
|
12
|
+
| Tests | `bundle exec rspec` |
|
|
13
|
+
| Lint | `bundle exec rubocop` |
|
|
14
|
+
| Both | `bundle exec rake` |
|
|
15
|
+
| Single spec | `bundle exec rspec spec/path/to_spec.rb` |
|
|
16
|
+
|
|
17
|
+
### Non-obvious caveats
|
|
18
|
+
|
|
19
|
+
- **Bundler 4.x required**: The lockfile was bundled with Bundler 4.0.6. Install with `sudo gem install bundler -v 4.0.6` if missing.
|
|
20
|
+
- **`libyaml-dev` must be installed**: The `psych` gem (transitive dep) needs `yaml.h`. Without `libyaml-dev`, `bundle install` fails on native extension build.
|
|
21
|
+
- **`vendor/bundle` path**: Gems are installed to `./vendor/bundle` via `.bundle/config` to avoid needing root write access to `/var/lib/gems`.
|
|
22
|
+
- **No real API calls in tests**: All HTTP interactions are stubbed with WebMock/VCR. No `DHAN_CLIENT_ID` or `DHAN_ACCESS_TOKEN` secrets are needed to run the test suite.
|
|
23
|
+
- **No services to start**: This is a library gem. There is no web server, database, or background worker. Testing is purely `bundle exec rspec` / `bundle exec rubocop`.
|
data/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# dhanhq-client Architecture
|
|
2
|
+
|
|
3
|
+
This document describes the architecture of the DhanHQ v2 API client gem: layers, dependencies, and design patterns in use.
|
|
4
|
+
|
|
5
|
+
## Guiding principles
|
|
6
|
+
|
|
7
|
+
- **Dependency rule**: High-level policy (models, domain) does not depend on low-level details (HTTP, JSON). Infrastructure (Client, BaseAPI) depends on configuration and helpers; models depend on resources (abstractions) and contracts.
|
|
8
|
+
- **Single responsibility**: Each layer has one reason to change. Models own domain behavior; resources own HTTP; contracts own validation rules.
|
|
9
|
+
- **Open/closed**: New endpoints are added by adding new Model + Resource + Contract pairs without modifying BaseAPI or BaseModel core.
|
|
10
|
+
- **Don’t force patterns**: Patterns (Strategy, Factory Method, Facade) emerged from refactoring; we avoid speculative abstraction.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Layer overview
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
18
|
+
│ Entry & configuration (lib/dhan_hq.rb, Configuration) │
|
|
19
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
20
|
+
│ Domain / facade layer (Models) │
|
|
21
|
+
│ Order, Position, MarketFeed, OptionChain, ExpiredOptionsData… │
|
|
22
|
+
│ → validate via Contracts, delegate HTTP to Resources │
|
|
23
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
24
|
+
│ REST / HTTP layer (Resources, BaseAPI, BaseResource) │
|
|
25
|
+
│ → build path, format params, call Client │
|
|
26
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
27
|
+
│ Transport layer (Client, RateLimiter, RequestHelper, │
|
|
28
|
+
│ ResponseHelper) │
|
|
29
|
+
│ → Faraday, headers, retries, error mapping │
|
|
30
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
31
|
+
│ Validation (Contracts) │
|
|
32
|
+
│ → Dry::Validation, shared macros in BaseContract │
|
|
33
|
+
├─────────────────────────────────────────────────────────────────┤
|
|
34
|
+
│ WebSocket (WS::*) — separate subsystem │
|
|
35
|
+
└─────────────────────────────────────────────────────────────────┘
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Directory structure and roles
|
|
41
|
+
|
|
42
|
+
| Path | Role | Responsibility |
|
|
43
|
+
|------|------|----------------|
|
|
44
|
+
| `lib/dhan_hq.rb` | Entry point | Zeitwerk setup, eager load of core/helpers/errors, `DhanHQ.configure` |
|
|
45
|
+
| `core/` | Base abstractions | BaseAPI (HTTP verbs, path building, param formatting), BaseModel (attributes, resource, validation, CRUD helpers), BaseResource (CRUD on BaseAPI), AuthAPI, ErrorHandler |
|
|
46
|
+
| `helpers/` | Cross-cutting | APIHelper, AttributeHelper (keys, normalization), ValidationHelper (validate_params!, validate!), RequestHelper (headers, payload, build_from_response), ResponseHelper (parse_json, handle_response, error mapping) |
|
|
47
|
+
| `models/` | Domain / facade | Typed wrappers (Order, Position, Holding, etc.). Define `resource`, optional `validation_contract`, and domain methods. Validate then delegate to resource. |
|
|
48
|
+
| `resources/` | REST wrappers | One class per API surface (Orders, Positions, MarketFeed, OptionChain, …). Set `HTTP_PATH`, `API_TYPE`; implement get/post/put/delete via BaseAPI. |
|
|
49
|
+
| `contracts/` | Request/response validation | Dry::Validation contracts (PlaceOrderContract, ModifyOrderContract, OptionChainContract, etc.). BaseContract provides shared macros (e.g. lot_size, tick_size). |
|
|
50
|
+
| `auth/` | Token lifecycle | Token generator/renewal/manager for dynamic tokens. |
|
|
51
|
+
| `concerns/` | Shared behavior | Modules included across layers (e.g. `OrderAudit` for live trading guard + audit logging, included in all order resources). |
|
|
52
|
+
| `utils/` | Utilities | Cross-cutting utilities not tied to a single layer (e.g. `NetworkInspector` for IP/hostname/env lookup used by order audit logging). |
|
|
53
|
+
| `ws/` | WebSocket | Connection, packets, decoder, market depth, orders client — isolated from REST. |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Dependency flow
|
|
58
|
+
|
|
59
|
+
- **Configuration** is global (`DhanHQ.configuration`). Client and helpers read it (access_token, client_id, base_url).
|
|
60
|
+
- **Models** depend on:
|
|
61
|
+
- **Resources** (Factory Method: `resource` returns the right BaseAPI subclass)
|
|
62
|
+
- **Contracts** (for validate_params!)
|
|
63
|
+
- **Helpers** (via BaseModel: ValidationHelper, RequestHelper, ResponseHelper, AttributeHelper, APIHelper)
|
|
64
|
+
- **Resources** depend on:
|
|
65
|
+
- **Client** (injected via BaseAPI: `DhanHQ::Client.new(api_type)`)
|
|
66
|
+
- **Helpers** (BaseAPI includes APIHelper, AttributeHelper; Client uses RequestHelper, ResponseHelper)
|
|
67
|
+
- **Client** depends on Configuration, RateLimiter, and helpers. No dependency on Models or Resources.
|
|
68
|
+
- **Contracts** depend on Constants (and optional instrument_meta). No dependency on Models or HTTP.
|
|
69
|
+
|
|
70
|
+
So: **Models → Resources → Client**; **Contracts** are used by Models (and optionally Resources); **Helpers** are used by Client, BaseAPI, and BaseModel.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Design patterns in use
|
|
75
|
+
|
|
76
|
+
| Pattern | Where | Purpose |
|
|
77
|
+
|--------|--------|--------|
|
|
78
|
+
| **Facade** | Models (e.g. `Order.place`, `MarketFeed.ltp`) | Single entry point for “place order” or “get LTP”; hide validation, normalization, and resource call. |
|
|
79
|
+
| **Factory Method** | BaseModel `resource` | Subclasses override `resource` to return the correct REST wrapper (e.g. Orders, OptionChain) without callers knowing the class. |
|
|
80
|
+
| **Strategy** | BaseAPI `param_formatter_for(full_path)` | Choose how to format params by path: pass-through (marketfeed), titleize (optionchain), default camelize. Encapsulated in lambdas. |
|
|
81
|
+
| **Template Method** | BaseModel `save` | `save` calls `new_record? ? create : update`; subclasses override `create`/`update` or collection methods. |
|
|
82
|
+
| **Adapter** | RequestHelper / ResponseHelper | Adapt external API (headers, JSON, status codes) to internal hashes and error classes. |
|
|
83
|
+
| **Singleton (per API type)** | RateLimiter.for(api_type) | One rate limiter per API type so all clients share limits. |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Error handling
|
|
88
|
+
|
|
89
|
+
- **Validation failures**: Raised as `DhanHQ::ValidationError` with a message that includes contract errors (e.g. `"Invalid parameters: #{result.errors.to_h}"`). Used by ValidationHelper, ExpiredOptionsData, Trade, and any code that validates via contracts.
|
|
90
|
+
- **HTTP / API errors**: Mapped in ResponseHelper (e.g. 401 → InvalidAuthenticationError, 807 → TokenExpiredError) and raised as appropriate `DhanHQ::*` subclasses.
|
|
91
|
+
- **Client** retries on auth failures (once, with token refresh) and on transient/network errors (with backoff).
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
- Credentials and URLs live in `DhanHQ::Configuration` (access_token, client_id, base_url, sandbox, optional access_token_provider).
|
|
98
|
+
- `DhanHQ.configure { }`, `configure_with_env`, and `configure_from_token_endpoint` set configuration. Never hardcode credentials; use env vars or token endpoint.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## WebSocket (WS)
|
|
103
|
+
|
|
104
|
+
- Separate subsystem under `DhanHQ::WS`: own connection, packet types, decoder, market depth, orders client.
|
|
105
|
+
- Shares configuration (e.g. access token) but not the REST Client or Resources. Documented in code and specs; not expanded here.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Adding a new API surface
|
|
110
|
+
|
|
111
|
+
1. **Contract** (optional): Add a Dry::Validation contract under `contracts/` if the endpoint has structured input.
|
|
112
|
+
2. **Resource**: Add a class under `resources/` inheriting BaseAPI (or BaseResource). Set `HTTP_PATH`, `API_TYPE`; implement methods that call `get`/`post`/`put`/`delete`.
|
|
113
|
+
3. **Model**: Add a class under `models/` inheriting BaseModel. Override `resource` to return the new resource; override `validation_contract` if needed; implement class/instance methods that call `validate_params!` then `resource.*`.
|
|
114
|
+
|
|
115
|
+
This keeps the dependency rule and keeps each layer focused on one concern.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,68 @@
|
|
|
1
|
+
## [2.7.0] - 2026-03-17
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
|
|
5
|
+
- **Order audit logging across all order types**: Every order submission (regular, super, forever/GTT, and alert orders) now emits a WARN-level structured JSON log line capturing: event type, public IPv4/IPv6, hostname, runtime environment, `security_id`, `correlation_id`, and UTC timestamp. Log output example:
|
|
6
|
+
```json
|
|
7
|
+
{"event":"DHAN_ORDER_ATTEMPT","hostname":"DESKTOP-SHUBHAM","env":"production","ipv4":"122.171.22.40","ipv6":"2401:4900:...","security_id":"11536","correlation_id":"SCALPER_7af1","timestamp":"2026-03-17T06:45:22Z"}
|
|
8
|
+
```
|
|
9
|
+
Events logged: `DHAN_ORDER_ATTEMPT`, `DHAN_ORDER_MODIFY_ATTEMPT`, `DHAN_ORDER_SLICING_ATTEMPT`, `DHAN_SUPER_ORDER_ATTEMPT`, `DHAN_SUPER_ORDER_MODIFY_ATTEMPT`, `DHAN_SUPER_ORDER_CANCEL_ATTEMPT`, `DHAN_FOREVER_ORDER_ATTEMPT`, `DHAN_FOREVER_ORDER_MODIFY_ATTEMPT`, `DHAN_FOREVER_ORDER_CANCEL_ATTEMPT`, `DHAN_ALERT_ORDER_ATTEMPT`, `DHAN_ALERT_ORDER_MODIFY_ATTEMPT`, `DHAN_ALERT_ORDER_DELETE_ATTEMPT`, `DHAN_PNL_EXIT_CONFIGURE_ATTEMPT`, `DHAN_PNL_EXIT_STOP_ATTEMPT`.
|
|
10
|
+
- **`DhanHQ::Concerns::OrderAudit`**: Shared concern providing `log_order_context` and `ensure_live_trading!` — included in `Resources::Orders`, `Resources::SuperOrders`, `Resources::ForeverOrders`, `Resources::AlertOrders`, and `Resources::PnlExit`.
|
|
11
|
+
- **`DhanHQ::Utils::NetworkInspector`**: New utility class that resolves the machine's public IPv4 (via api.ipify.org), IPv6 (via api64.ipify.org), hostname (`Socket.gethostname`), and runtime environment (`RAILS_ENV` / `RACK_ENV` / `APP_ENV`). IP lookups are memoized for the process lifetime; call `NetworkInspector.reset_cache!` to refresh.
|
|
12
|
+
- **Live trading guard**: All mutating order/trader-control calls require `ENV["LIVE_TRADING"]="true"`. Guarded methods:
|
|
13
|
+
- `Resources::Orders#create`, `#update`, `#slicing`, `#cancel`
|
|
14
|
+
- `Resources::SuperOrders#create`, `#update`, `#cancel`
|
|
15
|
+
- `Resources::ForeverOrders#create`, `#update`, `#cancel`
|
|
16
|
+
- `Resources::AlertOrders#create`, `#update`, `#delete`
|
|
17
|
+
- `Resources::PnlExit#configure`, `#stop`
|
|
18
|
+
- **`DhanHQ::LiveTradingDisabledError`**: New error class raised when the live trading guard fires.
|
|
19
|
+
- **Correlation ID prefix convention**: Recommended per-app correlation ID prefixes for instant source identification in the Dhan orderbook (e.g. `SCALPER_7af1`, `TRADER_3bc9`). See README.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- **`Resources::Orders`**: `#create`, `#update`, `#slicing`, `#cancel` log order context and enforce the live trading guard.
|
|
24
|
+
- **`Resources::SuperOrders`**: `#create`, `#update`, `#cancel` log order context and enforce the live trading guard.
|
|
25
|
+
- **`Resources::ForeverOrders`**: `#create`, `#update`, `#cancel` log order context and enforce the live trading guard.
|
|
26
|
+
- **`Resources::AlertOrders`**: `#create`, `#update`, `#delete` log order context and enforce the live trading guard; `#delete` added.
|
|
27
|
+
- **`Resources::PnlExit`**: `#configure` and `#stop` use `OrderAudit` (guard + audit log).
|
|
28
|
+
|
|
29
|
+
### Breaking
|
|
30
|
+
|
|
31
|
+
- **`ENV["LIVE_TRADING"]` required for all order/trader-control mutations**: Any call that creates, updates, cancels, or deletes orders (or configures/stops PnL exit) now raises `LiveTradingDisabledError` unless `ENV["LIVE_TRADING"]="true"`. Affects `Resources::Orders`, `Resources::SuperOrders`, `Resources::ForeverOrders`, `Resources::AlertOrders`, `Resources::PnlExit`, and the corresponding model wrappers. Set `LIVE_TRADING=true` in production and `LIVE_TRADING=false` (or omit) in development/test.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## [2.6.3] - 2026-03-14
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **Constants::Urls**: All canonical Dhan API/auth/WebSocket URLs in one place (`REST_API_BASE`, `SANDBOX_API_BASE`, `AUTH_BASE`, `WS_MARKET_FEED`, `WS_ORDER_UPDATE`, `WS_DEPTH_20`, `WS_DEPTH_200`, `INSTRUMENT_CSV_*`, `DOCS`, `ORIGIN`). Configuration, Auth, and WS defaults now use these constants.
|
|
40
|
+
- **Order modification limit enforcement**: `Order#modify` enforces Dhan’s 25-modifications-per-order cap per instance; the 26th modify raises `DhanHQ::ModificationLimitError` and does not call the API. Count is in-process only (fresh `find` resets it).
|
|
41
|
+
- **DhanHQ::ModificationLimitError**: New error class for the 25-per-order limit (rescuable).
|
|
42
|
+
- **API error payload on exceptions**: Raised API errors now expose the full response body as `error.response_body` (e.g. `errorCode`, `errorMessage`, and any future keys like `errors` or `details`). Useful for logging and debugging.
|
|
43
|
+
- **DH-905 message hint**: For `InputExceptionError` (DH-905), the exception message now includes a note that the Dhan API does not return which field failed, and to check required params and value types for the endpoint.
|
|
44
|
+
- **Kill switch status validation**: `Resources::KillSwitch#update(status)` and `Models::KillSwitch.update(status)` now validate that `status` is `ACTIVATE` or `DEACTIVATE` (case-insensitive); invalid values raise `DhanHQ::ValidationError` before the request.
|
|
45
|
+
- **Super order cancel leg validation**: `Resources::SuperOrders#cancel(order_id, leg_name)` validates `leg_name` against the API path enum (`ENTRY_LEG`, `STOP_LOSS_LEG`, `TARGET_LEG`); invalid values raise `DhanHQ::ValidationError`.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **SliceOrderContract**: Aligned with Dhan v2 orders doc — `amoTime` now allows `PRE_OPEN`; validity restricted to `DAY`/`IOC`; product type restricted to `CNC`/`INTRADAY`/`MARGIN`/`MTF`; `correlationId` max length 30 (was 25).
|
|
50
|
+
- **Order#modify** YARD: Documented modification limit and `ModificationLimitError`.
|
|
51
|
+
- **Error#initialize**: `DhanHQ::Error` now accepts an optional `response_body:` keyword argument so API-raised errors can carry the parsed response. Subclasses unchanged; `raise ErrorClass, "msg"` still works.
|
|
52
|
+
- **ResponseHelper**: When the API returns extra keys (`errors`, `details`, `validationErrors`), they are appended to the exception message. DH-905 errors include the endpoint hint above.
|
|
53
|
+
- **Margin contracts**: `MarginCalculatorContract` and `MultiScripMarginCalcRequestContract` accept optional `orderType` (per OpenAPI; some accounts require it). `Margin` model and `bin/test_all` margin payloads send `order_type: LIMIT` when `DHAN_TEST_MARGIN=true`.
|
|
54
|
+
- **bin/test_all**: Fixed `ArgumentError: wrong number of arguments (given 1, expected 0)` by invoking endpoint lambdas with no arguments inside `Timeout.timeout` (the timeout block receives the duration). Refactored `endpoint_list` for RuboCop Metrics/AbcSize; optional read endpoints (forever order by id, PnL exit, margin) are skipped unless `DHAN_TEST_FOREVER_ORDER_ID`, `DHAN_TEST_PNL_EXIT=true`, or `DHAN_TEST_MARGIN=true` are set.
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **bin/test_all**: All 30 read endpoints now run successfully by default (26 called, 4 optional); margin/PnL/forever-by-id no longer fail when fixtures are missing.
|
|
59
|
+
|
|
60
|
+
### Removed
|
|
61
|
+
|
|
62
|
+
- **docs/DHAN_V2_GAPS.md**: Removed; path/behavior alignment remains in `docs/API_VERIFICATION.md`.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
1
66
|
## [2.6.2] - 2026-03-07
|
|
2
67
|
|
|
3
68
|
### Changed
|
data/README.md
CHANGED
|
@@ -53,6 +53,8 @@ You could wire up Faraday and parse JSON yourself. Here's why you shouldn't:
|
|
|
53
53
|
- **Secure logging** — automatic token sanitization in all log output
|
|
54
54
|
- **Super Orders** — entry + stop-loss + target + trailing jump in one request
|
|
55
55
|
- **Instrument convenience methods** — `.ltp`, `.ohlc`, `.option_chain` directly on instruments
|
|
56
|
+
- **Order audit logging** — every order attempt logs machine, IP, environment, and correlation ID as structured JSON
|
|
57
|
+
- **Live trading guard** — prevents accidental order placement unless `ENV["LIVE_TRADING"]="true"`
|
|
56
58
|
- **Full REST coverage** — Orders, Trades, Forever Orders, Super Orders, Positions, Holdings, Funds, HistoricalData, OptionChain, MarketFeed, EDIS, Kill Switch, P&L Exit, Alert Orders, Margin Calculator
|
|
57
59
|
- **P&L Based Exit** — automatic position exit on profit/loss thresholds
|
|
58
60
|
- **Postback parser** — parse Dhan webhook payloads with `Postback.parse` and status predicates
|
|
@@ -75,6 +77,8 @@ gem install DhanHQ
|
|
|
75
77
|
|
|
76
78
|
> **Bleeding edge?** Use `gem 'DhanHQ', git: 'https://github.com/shubhamtaywade82/dhanhq-client.git', branch: 'main'` only if you need unreleased features.
|
|
77
79
|
|
|
80
|
+
**`bundle update` / `bundle install` warnings** — If you see "Local specification for rexml-3.2.8 has different dependencies" or "Unresolved or ambiguous specs during Gem::Specification.reset: psych", the bundle still completes successfully. To clear the rexml warning once, run: `gem cleanup rexml`. The psych message is a known Bundler quirk and can be ignored.
|
|
81
|
+
|
|
78
82
|
### ⚠️ Breaking Change (v2.1.5+)
|
|
79
83
|
|
|
80
84
|
The require statement changed:
|
|
@@ -118,6 +122,57 @@ When the API returns 401, the client retries **once** with a fresh token from yo
|
|
|
118
122
|
|
|
119
123
|
---
|
|
120
124
|
|
|
125
|
+
## Order Safety
|
|
126
|
+
|
|
127
|
+
### Live Trading Guard
|
|
128
|
+
|
|
129
|
+
Order placement (`create`, `slicing`) is blocked unless you explicitly enable it:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Production (Render, VPS, etc.)
|
|
133
|
+
LIVE_TRADING=true
|
|
134
|
+
|
|
135
|
+
# Development / Test (default — orders are blocked)
|
|
136
|
+
LIVE_TRADING=false # or simply omit
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Attempting to place an order without `LIVE_TRADING=true` raises `DhanHQ::LiveTradingDisabledError`.
|
|
140
|
+
|
|
141
|
+
### Order Audit Logging
|
|
142
|
+
|
|
143
|
+
Every order attempt (place, modify, slice) automatically logs a structured JSON line at WARN level:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"event": "DHAN_ORDER_ATTEMPT",
|
|
148
|
+
"hostname": "DESKTOP-SHUBHAM",
|
|
149
|
+
"env": "production",
|
|
150
|
+
"ipv4": "122.171.22.40",
|
|
151
|
+
"ipv6": "2401:4900:894c:8448:1da9:27f1:48e7:61be",
|
|
152
|
+
"security_id": "11536",
|
|
153
|
+
"correlation_id": "SCALPER_7af1",
|
|
154
|
+
"timestamp": "2026-03-17T06:45:22Z"
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
This tells you instantly which machine, app, IP, and environment placed the order.
|
|
159
|
+
|
|
160
|
+
### Correlation ID Prefixes
|
|
161
|
+
|
|
162
|
+
Use per-app prefixes for instant source identification in the Dhan orderbook:
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
# algo_scalper_api
|
|
166
|
+
correlation_id: "SCALPER_#{SecureRandom.hex(4)}"
|
|
167
|
+
|
|
168
|
+
# algo_trader_api
|
|
169
|
+
correlation_id: "TRADER_#{SecureRandom.hex(4)}"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The Dhan orderbook will show `SCALPER_7af1` or `TRADER_3bc9`, making the source obvious.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
121
176
|
## REST API
|
|
122
177
|
|
|
123
178
|
### Orders — Place, Modify, Cancel
|
data/docs/API_VERIFICATION.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
# API Verification (Dhan v2)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Path/behavior alignment with the official Dhan API v2 docs.
|
|
4
4
|
|
|
5
5
|
**Sources:**
|
|
6
6
|
|
|
7
7
|
- [dhanhq.co/docs/v2](https://dhanhq.co/docs/v2/) – main docs
|
|
8
8
|
- [dhanhq.co/docs/v2/edis](https://dhanhq.co/docs/v2/edis/) – EDIS
|
|
9
9
|
- [api.dhan.co/v2](https://api.dhan.co/v2/#/) – Developer Kit (when available)
|
|
10
|
-
- In-repo: `CODE_REVIEW_ISSUES.md` (Alert Orders, IP Setup paths)
|
|
11
10
|
|
|
12
11
|
---
|
|
13
12
|
|
|
@@ -32,7 +31,7 @@ This document records how the gem’s implementation aligns with the official Dh
|
|
|
32
31
|
|
|
33
32
|
| Doc path | Gem resource | Path used |
|
|
34
33
|
|---------------------------|---------------------------|------------------|
|
|
35
|
-
| `/alerts/orders` | `Resources::AlertOrders` | `HTTP_PATH = "/alerts/orders"` |
|
|
34
|
+
| `/alerts/orders` | `Resources::AlertOrders` | `HTTP_PATH = "/v2/alerts/orders"` |
|
|
36
35
|
| GET/POST `/alerts/orders` | `#all`, `#create` | BaseResource |
|
|
37
36
|
| GET/PUT/DELETE `/alerts/orders/{trigger-id}` | `#find`, `#update`, `#delete` | `/{id}` |
|
|
38
37
|
|
|
@@ -46,16 +45,19 @@ Model: `Models::AlertOrder`. Condition must include `exchange_segment`, `exp_dat
|
|
|
46
45
|
|
|
47
46
|
| Doc path | Gem method | Path / body |
|
|
48
47
|
|-----------------|----------------|-------------|
|
|
49
|
-
| GET /ip/getIP | `#current` | `get("/getIP")` |
|
|
50
|
-
| POST /ip/setIP | `#set(ip:, ip_flag: "PRIMARY", dhan_client_id: nil)` | `post("/setIP", params: { ip:, ip_flag:, dhan_client_id: })`; `dhan_client_id` defaults from config |
|
|
51
|
-
| PUT /ip/modifyIP| `#update(ip:, ip_flag: "PRIMARY", dhan_client_id: nil)` | `put("/modifyIP", params: { ip:, ip_flag:, dhan_client_id: })` |
|
|
48
|
+
| GET /v2/ip/getIP | `#current` | `get("/getIP")` (HTTP_PATH = "/v2/ip") |
|
|
49
|
+
| POST /v2/ip/setIP | `#set(ip:, ip_flag: "PRIMARY", dhan_client_id: nil)` | `post("/setIP", params: { ip:, ip_flag:, dhan_client_id: })`; `dhan_client_id` defaults from config |
|
|
50
|
+
| PUT /v2/ip/modifyIP| `#update(ip:, ip_flag: "PRIMARY", dhan_client_id: nil)` | `put("/modifyIP", params: { ip:, ip_flag:, dhan_client_id: })` |
|
|
52
51
|
|
|
53
52
|
---
|
|
54
53
|
|
|
55
54
|
## Trader Control / Kill Switch
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
**Doc:** [dhanhq.co/docs/v2](https://dhanhq.co/docs/v2/) → Trading APIs → Trader's Control.
|
|
57
|
+
|
|
58
|
+
- **Kill Switch:** `Resources::KillSwitch`, `Models::KillSwitch` – path `/v2/killswitch`. Manage (activate/deactivate) uses **query parameter**: `POST /v2/killswitch?killSwitchStatus=ACTIVATE` (or `DEACTIVATE`) with no body. `#status` is GET.
|
|
59
|
+
- **P&L Exit:** `Models::PnlExit` – path `/v2/pnlExit`. GET status, POST configure, DELETE stop.
|
|
60
|
+
- **TraderControl:** `Resources::TraderControl` – path `/trader-control` is **not** in the Dhan v2 API. The class is kept for backward compatibility but raises `DhanHQ::Error` when any method is called; use KillSwitch and PnlExit instead.
|
|
59
61
|
|
|
60
62
|
---
|
|
61
63
|
|
|
@@ -101,3 +101,15 @@ These are **not** sandbox-aware; they always use the URLs above.
|
|
|
101
101
|
- **REST:** When `sandbox` is true, all REST calls go to `https://sandbox.dhan.co/v2`. Only `GET /v2/profile` and `GET /v2/fundlimit` are verified working on sandbox; other REST endpoints are not verified — see "Sandbox: verified vs not working / unverified" above.
|
|
102
102
|
- **Auth:** Token generation and renewal always use production hosts.
|
|
103
103
|
- **WebSockets:** Sandbox does **not** support WebSocket. Order updates, market feed, and market depth always use production URLs; the gem does not publish or use any sandbox WebSocket URLs.
|
|
104
|
+
|
|
105
|
+
## Call-all-endpoints script
|
|
106
|
+
|
|
107
|
+
`bin/call_all_endpoints.rb` invokes every REST endpoint exposed by the gem (read-only by default; use `--all` to include write/destructive calls). Useful for connectivity checks or sandbox verification.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bin/call_all_endpoints.rb # read-only
|
|
111
|
+
bin/call_all_endpoints.rb --list # print endpoint list
|
|
112
|
+
bin/call_all_endpoints.rb --all # include POST/PUT/DELETE
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Requires `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN`. Optional: `DHAN_SANDBOX=true`, `DHAN_TEST_SECURITY_ID`, `DHAN_TEST_ORDER_ID`, `DHAN_TEST_ISIN`, `DHAN_TEST_EXPIRY`. When not using `--skip-unavailable`, the script creates a temporary alert for GET/PUT/DELETE alert endpoints and deletes it at exit.
|
data/lib/DhanHQ/auth.rb
CHANGED
|
@@ -27,8 +27,8 @@ module DhanHQ
|
|
|
27
27
|
# client_id: ENV["DHAN_CLIENT_ID"]
|
|
28
28
|
# )
|
|
29
29
|
module Auth
|
|
30
|
-
AUTH_BASE_URL =
|
|
31
|
-
API_BASE_URL =
|
|
30
|
+
AUTH_BASE_URL = Constants::Urls::AUTH_BASE
|
|
31
|
+
API_BASE_URL = Constants::Urls::REST_API_BASE
|
|
32
32
|
|
|
33
33
|
# Generates an access token using TOTP authentication.
|
|
34
34
|
#
|
data/lib/DhanHQ/client.rb
CHANGED
|
@@ -61,48 +61,56 @@ module DhanHQ
|
|
|
61
61
|
# @raise [DhanHQ::Error] If an HTTP error occurs.
|
|
62
62
|
def request(method, path, payload, retries: 3)
|
|
63
63
|
@token_manager&.ensure_valid_token!
|
|
64
|
-
@rate_limiter.throttle!
|
|
65
|
-
|
|
66
|
-
# Ensure connection matches current configuration (e.g. sandbox toggle)
|
|
64
|
+
@rate_limiter.throttle!
|
|
67
65
|
refresh_connection!
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
with_auth_retry do
|
|
68
|
+
with_transient_retry(retries: retries) do
|
|
69
|
+
response = connection.send(method, path) do |req|
|
|
70
|
+
req.headers.merge!(build_headers(path))
|
|
71
|
+
prepare_payload(req, payload, method, path)
|
|
72
|
+
end
|
|
73
|
+
handle_response(response)
|
|
75
74
|
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
def with_auth_retry
|
|
79
|
+
yield
|
|
80
|
+
rescue DhanHQ::InvalidAuthenticationError, DhanHQ::InvalidTokenError,
|
|
81
|
+
DhanHQ::TokenExpiredError, DhanHQ::AuthenticationFailedError => e
|
|
82
|
+
config = DhanHQ.configuration
|
|
83
|
+
raise unless config&.access_token_provider
|
|
84
|
+
|
|
85
|
+
config.on_token_expired&.call(e)
|
|
86
|
+
DhanHQ.logger&.warn("[DhanHQ::Client] Auth failure (#{e.class}), retrying once with fresh token")
|
|
87
|
+
yield
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def with_transient_retry(retries:)
|
|
91
|
+
attempt = 0
|
|
92
|
+
begin
|
|
93
|
+
yield
|
|
88
94
|
rescue DhanHQ::RateLimitError, DhanHQ::InternalServerError, DhanHQ::NetworkError => e
|
|
89
95
|
attempt += 1
|
|
90
|
-
if attempt
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
raise if attempt > retries
|
|
97
|
+
|
|
98
|
+
backoff = calculate_backoff(attempt)
|
|
99
|
+
DhanHQ.logger&.warn(
|
|
100
|
+
"[DhanHQ::Client] Transient error (#{e.class}), retrying in #{backoff}s (attempt #{attempt}/#{retries})"
|
|
101
|
+
)
|
|
102
|
+
sleep(backoff)
|
|
103
|
+
retry
|
|
97
104
|
rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
|
|
98
105
|
attempt += 1
|
|
99
|
-
if attempt
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
raise DhanHQ::NetworkError, "Request failed after #{retries} retries: #{e.message}" if attempt > retries
|
|
107
|
+
|
|
108
|
+
backoff = calculate_backoff(attempt)
|
|
109
|
+
DhanHQ.logger&.warn(
|
|
110
|
+
"[DhanHQ::Client] Network error (#{e.class}), retrying in #{backoff}s (attempt #{attempt}/#{retries})"
|
|
111
|
+
)
|
|
112
|
+
sleep(backoff)
|
|
113
|
+
retry
|
|
106
114
|
end
|
|
107
115
|
end
|
|
108
116
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../utils/network_inspector"
|
|
4
|
+
|
|
5
|
+
module DhanHQ
|
|
6
|
+
module Concerns
|
|
7
|
+
# Shared behavior for order audit logging and live trading safety.
|
|
8
|
+
#
|
|
9
|
+
# Include this module in any Resource that submits, modifies, or cancels
|
|
10
|
+
# orders on the Dhan API. It provides:
|
|
11
|
+
#
|
|
12
|
+
# - {#log_order_context}: emits a structured JSON log line (WARN level)
|
|
13
|
+
# capturing hostname, public IP, environment, security_id, correlation_id,
|
|
14
|
+
# and a UTC timestamp.
|
|
15
|
+
#
|
|
16
|
+
# - {#ensure_live_trading!}: raises {DhanHQ::LiveTradingDisabledError}
|
|
17
|
+
# unless +ENV["LIVE_TRADING"]+ is +"true"+, preventing accidental order
|
|
18
|
+
# placement from development machines.
|
|
19
|
+
#
|
|
20
|
+
# @example Including in a resource
|
|
21
|
+
# class MyOrders < BaseAPI
|
|
22
|
+
# include DhanHQ::Concerns::OrderAudit
|
|
23
|
+
#
|
|
24
|
+
# def create(params)
|
|
25
|
+
# ensure_live_trading!
|
|
26
|
+
# log_order_context("MY_ORDER_ATTEMPT", params)
|
|
27
|
+
# post("", params: params)
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
module OrderAudit
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Raises an error if LIVE_TRADING is not explicitly enabled.
|
|
34
|
+
# Set ENV["LIVE_TRADING"]="true" in production to allow order submission.
|
|
35
|
+
def ensure_live_trading!
|
|
36
|
+
return if ENV["LIVE_TRADING"] == "true"
|
|
37
|
+
|
|
38
|
+
raise DhanHQ::LiveTradingDisabledError,
|
|
39
|
+
"Live trading is disabled. Set ENV[\"LIVE_TRADING\"]=\"true\" to enable order placement."
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Emits a structured JSON log line with machine/network/correlation context.
|
|
43
|
+
# Uses WARN level so it appears even when INFO is silenced.
|
|
44
|
+
def log_order_context(event, params = {})
|
|
45
|
+
inspector = DhanHQ::Utils::NetworkInspector
|
|
46
|
+
entry = {
|
|
47
|
+
event: event,
|
|
48
|
+
hostname: inspector.hostname,
|
|
49
|
+
env: inspector.environment,
|
|
50
|
+
ipv4: inspector.public_ipv4,
|
|
51
|
+
ipv6: inspector.public_ipv6,
|
|
52
|
+
security_id: extract_param(params, :securityId, :security_id),
|
|
53
|
+
correlation_id: extract_param(params, :correlationId, :correlation_id),
|
|
54
|
+
order_id: extract_param(params, :orderId, :order_id),
|
|
55
|
+
timestamp: Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
56
|
+
}.compact
|
|
57
|
+
|
|
58
|
+
DhanHQ.logger&.warn(JSON.generate(entry))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Extracts a value from params trying both camelCase and snake_case keys,
|
|
62
|
+
# as well as both symbol and string key types.
|
|
63
|
+
def extract_param(params, camel_key, snake_key)
|
|
64
|
+
p = params || {}
|
|
65
|
+
p[camel_key] || p[camel_key.to_s] || p[snake_key] || p[snake_key.to_s]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/DhanHQ/configuration.rb
CHANGED
|
@@ -9,13 +9,12 @@ module DhanHQ
|
|
|
9
9
|
# @see https://dhanhq.co/docs/v2/ DhanHQ API Documentation
|
|
10
10
|
class Configuration
|
|
11
11
|
# Default REST API host used when the base URL is not overridden.
|
|
12
|
-
#
|
|
13
12
|
# @return [String]
|
|
14
|
-
BASE_URL =
|
|
13
|
+
BASE_URL = Constants::Urls::REST_API_BASE
|
|
15
14
|
|
|
16
15
|
# Default Sandbox API host.
|
|
17
16
|
# @return [String]
|
|
18
|
-
SANDBOX_URL =
|
|
17
|
+
SANDBOX_URL = Constants::Urls::SANDBOX_API_BASE
|
|
19
18
|
# The client ID for API authentication.
|
|
20
19
|
# @return [String, nil] The client ID or `nil` if not set.
|
|
21
20
|
attr_accessor :client_id
|
|
@@ -58,21 +57,21 @@ module DhanHQ
|
|
|
58
57
|
# Sandbox does not support WebSocket; always returns production URL unless overridden.
|
|
59
58
|
# @return [String]
|
|
60
59
|
def ws_order_url
|
|
61
|
-
@ws_order_url ||
|
|
60
|
+
@ws_order_url || Constants::Urls::WS_ORDER_UPDATE
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
# Websocket market feed endpoint.
|
|
65
64
|
# Sandbox does not support WebSocket; always returns production URL unless overridden.
|
|
66
65
|
# @return [String]
|
|
67
66
|
def ws_market_feed_url
|
|
68
|
-
@ws_market_feed_url ||
|
|
67
|
+
@ws_market_feed_url || Constants::Urls::WS_MARKET_FEED
|
|
69
68
|
end
|
|
70
69
|
|
|
71
70
|
# Websocket market depth endpoint.
|
|
72
71
|
# Sandbox does not support WebSocket; always returns production URL unless overridden.
|
|
73
72
|
# @return [String]
|
|
74
73
|
def ws_market_depth_url
|
|
75
|
-
@ws_market_depth_url ||
|
|
74
|
+
@ws_market_depth_url || Constants::Urls::WS_DEPTH_20
|
|
76
75
|
end
|
|
77
76
|
|
|
78
77
|
# Market depth level (20 or 200).
|