DhanHQ 2.1.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +26 -0
- data/CHANGELOG.md +20 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/GUIDE.md +555 -0
- data/LICENSE.txt +21 -0
- data/README.md +463 -0
- data/README1.md +521 -0
- data/Rakefile +12 -0
- data/TAGS +10 -0
- data/TODO-1.md +14 -0
- data/TODO.md +127 -0
- data/app/services/live/order_update_guard_support.rb +75 -0
- data/app/services/live/order_update_hub.rb +76 -0
- data/app/services/live/order_update_persistence_support.rb +68 -0
- data/config/initializers/order_update_hub.rb +16 -0
- data/diagram.html +184 -0
- data/diagram.md +34 -0
- data/docs/rails_integration.md +304 -0
- data/exe/DhanHQ +4 -0
- data/lib/DhanHQ/client.rb +116 -0
- data/lib/DhanHQ/config.rb +32 -0
- data/lib/DhanHQ/configuration.rb +72 -0
- data/lib/DhanHQ/constants.rb +170 -0
- data/lib/DhanHQ/contracts/base_contract.rb +15 -0
- data/lib/DhanHQ/contracts/historical_data_contract.rb +28 -0
- data/lib/DhanHQ/contracts/margin_calculator_contract.rb +19 -0
- data/lib/DhanHQ/contracts/modify_order_contract copy.rb +100 -0
- data/lib/DhanHQ/contracts/modify_order_contract.rb +22 -0
- data/lib/DhanHQ/contracts/option_chain_contract.rb +31 -0
- data/lib/DhanHQ/contracts/order_contract.rb +102 -0
- data/lib/DhanHQ/contracts/place_order_contract.rb +119 -0
- data/lib/DhanHQ/contracts/position_conversion_contract.rb +24 -0
- data/lib/DhanHQ/contracts/slice_order_contract.rb +111 -0
- data/lib/DhanHQ/core/base_api.rb +105 -0
- data/lib/DhanHQ/core/base_model.rb +266 -0
- data/lib/DhanHQ/core/base_resource.rb +50 -0
- data/lib/DhanHQ/core/error_handler.rb +19 -0
- data/lib/DhanHQ/error_object.rb +49 -0
- data/lib/DhanHQ/errors.rb +45 -0
- data/lib/DhanHQ/helpers/api_helper.rb +17 -0
- data/lib/DhanHQ/helpers/attribute_helper.rb +72 -0
- data/lib/DhanHQ/helpers/model_helper.rb +7 -0
- data/lib/DhanHQ/helpers/request_helper.rb +69 -0
- data/lib/DhanHQ/helpers/response_helper.rb +98 -0
- data/lib/DhanHQ/helpers/validation_helper.rb +36 -0
- data/lib/DhanHQ/json_loader.rb +23 -0
- data/lib/DhanHQ/models/edis.rb +58 -0
- data/lib/DhanHQ/models/forever_order.rb +85 -0
- data/lib/DhanHQ/models/funds.rb +50 -0
- data/lib/DhanHQ/models/historical_data.rb +77 -0
- data/lib/DhanHQ/models/holding.rb +56 -0
- data/lib/DhanHQ/models/kill_switch.rb +49 -0
- data/lib/DhanHQ/models/ledger_entry.rb +60 -0
- data/lib/DhanHQ/models/margin.rb +54 -0
- data/lib/DhanHQ/models/market_feed.rb +41 -0
- data/lib/DhanHQ/models/option_chain.rb +79 -0
- data/lib/DhanHQ/models/order.rb +239 -0
- data/lib/DhanHQ/models/position.rb +60 -0
- data/lib/DhanHQ/models/profile.rb +44 -0
- data/lib/DhanHQ/models/super_order.rb +69 -0
- data/lib/DhanHQ/models/trade.rb +79 -0
- data/lib/DhanHQ/rate_limiter.rb +107 -0
- data/lib/DhanHQ/requests/optionchain/nifty.json +5 -0
- data/lib/DhanHQ/requests/optionchain/nifty_expiries.json +4 -0
- data/lib/DhanHQ/requests/orders/create.json +0 -0
- data/lib/DhanHQ/resources/edis.rb +44 -0
- data/lib/DhanHQ/resources/forever_orders.rb +53 -0
- data/lib/DhanHQ/resources/funds.rb +21 -0
- data/lib/DhanHQ/resources/historical_data.rb +34 -0
- data/lib/DhanHQ/resources/holdings.rb +21 -0
- data/lib/DhanHQ/resources/kill_switch.rb +21 -0
- data/lib/DhanHQ/resources/margin_calculator.rb +22 -0
- data/lib/DhanHQ/resources/market_feed.rb +56 -0
- data/lib/DhanHQ/resources/option_chain.rb +31 -0
- data/lib/DhanHQ/resources/orders.rb +70 -0
- data/lib/DhanHQ/resources/positions.rb +29 -0
- data/lib/DhanHQ/resources/profile.rb +25 -0
- data/lib/DhanHQ/resources/statements.rb +42 -0
- data/lib/DhanHQ/resources/super_orders.rb +46 -0
- data/lib/DhanHQ/resources/trades.rb +23 -0
- data/lib/DhanHQ/version.rb +6 -0
- data/lib/DhanHQ/ws/client.rb +182 -0
- data/lib/DhanHQ/ws/cmd_bus.rb +38 -0
- data/lib/DhanHQ/ws/connection.rb +240 -0
- data/lib/DhanHQ/ws/decoder.rb +83 -0
- data/lib/DhanHQ/ws/errors.rb +0 -0
- data/lib/DhanHQ/ws/orders/client.rb +59 -0
- data/lib/DhanHQ/ws/orders/connection.rb +148 -0
- data/lib/DhanHQ/ws/orders.rb +13 -0
- data/lib/DhanHQ/ws/packets/depth_delta_packet.rb +20 -0
- data/lib/DhanHQ/ws/packets/disconnect_packet.rb +15 -0
- data/lib/DhanHQ/ws/packets/full_packet.rb +40 -0
- data/lib/DhanHQ/ws/packets/header.rb +23 -0
- data/lib/DhanHQ/ws/packets/index_packet.rb +14 -0
- data/lib/DhanHQ/ws/packets/market_depth_level.rb +21 -0
- data/lib/DhanHQ/ws/packets/market_status_packet.rb +14 -0
- data/lib/DhanHQ/ws/packets/oi_packet.rb +15 -0
- data/lib/DhanHQ/ws/packets/prev_close_packet.rb +16 -0
- data/lib/DhanHQ/ws/packets/quote_packet.rb +26 -0
- data/lib/DhanHQ/ws/packets/ticker_packet.rb +16 -0
- data/lib/DhanHQ/ws/registry.rb +46 -0
- data/lib/DhanHQ/ws/segments.rb +75 -0
- data/lib/DhanHQ/ws/singleton_lock.rb +54 -0
- data/lib/DhanHQ/ws/sub_state.rb +59 -0
- data/lib/DhanHQ/ws/websocket_packet_parser.rb +165 -0
- data/lib/DhanHQ/ws.rb +37 -0
- data/lib/DhanHQ.rb +135 -0
- data/lib/ta/technical_analysis.rb +405 -0
- data/sig/DhanHQ.rbs +4 -0
- data/watchlist.csv +3 -0
- metadata +283 -0
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Models
|
5
|
+
##
|
6
|
+
# Represents a single trade.
|
7
|
+
# The API docs show an array of trades from GET /v2/trades/{from-date}/{to-date}/{page}
|
8
|
+
class Trade < BaseModel
|
9
|
+
# No explicit HTTP_PATH if we rely on the statements resource
|
10
|
+
# but we can define it if needed
|
11
|
+
HTTP_PATH = "/v2/trades"
|
12
|
+
|
13
|
+
attributes :dhan_client_id, :order_id, :exchange_order_id, :exchange_trade_id,
|
14
|
+
:transaction_type, :exchange_segment, :product_type, :order_type,
|
15
|
+
:trading_symbol, :custom_symbol, :security_id, :traded_quantity,
|
16
|
+
:traded_price, :isin, :instrument, :sebi_tax, :stt, :brokerage_charges,
|
17
|
+
:service_tax, :exchange_transaction_charges, :stamp_duty,
|
18
|
+
:create_time, :update_time, :exchange_time, :drv_expiry_date,
|
19
|
+
:drv_option_type, :drv_strike_price
|
20
|
+
|
21
|
+
class << self
|
22
|
+
##
|
23
|
+
# Provide a **shared instance** of the `Statements` resource,
|
24
|
+
# where we have `trade_history(from_date:, to_date:, page:)`.
|
25
|
+
# used for fetching historical trades.
|
26
|
+
def resource
|
27
|
+
@resource ||= DhanHQ::Resources::Statements.new
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Resource for current day tradebook APIs
|
32
|
+
def tradebook_resource
|
33
|
+
@tradebook_resource ||= DhanHQ::Resources::Trades.new
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Fetch trades within the given date range and page.
|
38
|
+
# GET /v2/trades/{from-date}/{to-date}/{page}
|
39
|
+
#
|
40
|
+
# @param from_date [String]
|
41
|
+
# @param to_date [String]
|
42
|
+
# @param page [Integer] Default 0
|
43
|
+
# @return [Array<Trade>]
|
44
|
+
# Retrieve historical trades
|
45
|
+
def history(from_date:, to_date:, page: 0)
|
46
|
+
# The resource call returns an Array<Hash>.
|
47
|
+
response = resource.trade_history(from_date: from_date, to_date: to_date, page: page)
|
48
|
+
return [] unless response.is_a?(Array)
|
49
|
+
|
50
|
+
response.map { |t| new(t, skip_validation: true) }
|
51
|
+
end
|
52
|
+
|
53
|
+
alias all history
|
54
|
+
|
55
|
+
# Retrieve current day trades
|
56
|
+
def today
|
57
|
+
response = tradebook_resource.all
|
58
|
+
return [] unless response.is_a?(Array)
|
59
|
+
|
60
|
+
response.map { |t| new(t, skip_validation: true) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fetch trades for a specific order id for the current day
|
64
|
+
def find_by_order_id(order_id)
|
65
|
+
response = tradebook_resource.find(order_id)
|
66
|
+
return nil unless response.is_a?(Hash) || (response.is_a?(Array) && response.any?)
|
67
|
+
|
68
|
+
data = response.is_a?(Array) ? response.first : response
|
69
|
+
new(data, skip_validation: true)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# If you want custom validations, you'd set a contract or skip for read-only
|
74
|
+
def validation_contract
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent"
|
4
|
+
|
5
|
+
module DhanHQ
|
6
|
+
# Coarse-grained in-memory throttler matching the platform rate limits.
|
7
|
+
class RateLimiter
|
8
|
+
# Per-interval thresholds keyed by API type.
|
9
|
+
RATE_LIMITS = {
|
10
|
+
order_api: { per_second: 25, per_minute: 250, per_hour: 1000, per_day: 7000 },
|
11
|
+
data_api: { per_second: 5, per_minute: Float::INFINITY, per_hour: Float::INFINITY, per_day: 100_000 },
|
12
|
+
quote_api: { per_second: 1, per_minute: Float::INFINITY, per_hour: Float::INFINITY, per_day: Float::INFINITY },
|
13
|
+
option_chain: { per_second: 1.0 / 3, per_minute: 20, per_hour: 600, per_day: 4800 },
|
14
|
+
non_trading_api: { per_second: 20, per_minute: Float::INFINITY, per_hour: Float::INFINITY,
|
15
|
+
per_day: Float::INFINITY }
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
# Creates a rate limiter for a given API type.
|
19
|
+
#
|
20
|
+
# @param api_type [Symbol] One of the keys from {RATE_LIMITS}.
|
21
|
+
def initialize(api_type)
|
22
|
+
@api_type = api_type
|
23
|
+
@buckets = Concurrent::Hash.new
|
24
|
+
@buckets[:last_request_time] = Time.at(0) if api_type == :option_chain
|
25
|
+
initialize_buckets
|
26
|
+
start_cleanup_threads
|
27
|
+
end
|
28
|
+
|
29
|
+
# Blocks until the current request is allowed by the configured limits.
|
30
|
+
#
|
31
|
+
# @return [void]
|
32
|
+
def throttle!
|
33
|
+
if @api_type == :option_chain
|
34
|
+
last_request_time = @buckets[:last_request_time]
|
35
|
+
|
36
|
+
sleep_time = 4 - (Time.now - last_request_time)
|
37
|
+
if sleep_time.positive?
|
38
|
+
puts "Sleeping for #{sleep_time.round(2)} seconds due to option_chain rate limit"
|
39
|
+
sleep(sleep_time)
|
40
|
+
end
|
41
|
+
|
42
|
+
@buckets[:last_request_time] = Time.now
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
loop do
|
47
|
+
break if allow_request?
|
48
|
+
|
49
|
+
sleep(0.1)
|
50
|
+
end
|
51
|
+
record_request
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Prepares the counters used for each interval in {RATE_LIMITS}.
|
57
|
+
def initialize_buckets
|
58
|
+
RATE_LIMITS[@api_type].each_key do |interval|
|
59
|
+
@buckets[interval] = Concurrent::AtomicFixnum.new(0)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Determines whether a request can be made without exceeding limits.
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
def allow_request?
|
67
|
+
RATE_LIMITS[@api_type].all? do |interval, limit|
|
68
|
+
@buckets[interval].value < limit
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Increments the counters for each time window once a request is made.
|
73
|
+
def record_request
|
74
|
+
RATE_LIMITS[@api_type].each_key do |interval|
|
75
|
+
@buckets[interval].increment
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Spawns background threads to reset counters after each interval elapses.
|
80
|
+
def start_cleanup_threads
|
81
|
+
Thread.new do
|
82
|
+
loop do
|
83
|
+
sleep(1)
|
84
|
+
@buckets[:per_second].value = 0
|
85
|
+
end
|
86
|
+
end
|
87
|
+
Thread.new do
|
88
|
+
loop do
|
89
|
+
sleep(60)
|
90
|
+
@buckets[:per_minute].value = 0
|
91
|
+
end
|
92
|
+
end
|
93
|
+
Thread.new do
|
94
|
+
loop do
|
95
|
+
sleep(3600)
|
96
|
+
@buckets[:per_hour].value = 0
|
97
|
+
end
|
98
|
+
end
|
99
|
+
Thread.new do
|
100
|
+
loop do
|
101
|
+
sleep(86_400)
|
102
|
+
@buckets[:per_day].value = 0
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
File without changes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client for electronic DIS flows.
|
6
|
+
class Edis < BaseAPI
|
7
|
+
# EDIS endpoints are served from the trading API.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Base path for EDIS endpoints.
|
10
|
+
HTTP_PATH = "/v2/edis"
|
11
|
+
|
12
|
+
# Creates a TPIN request form.
|
13
|
+
#
|
14
|
+
# @param params [Hash]
|
15
|
+
# @return [Hash]
|
16
|
+
def form(params)
|
17
|
+
post("/form", params: params)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Bulk EDIS form submission.
|
21
|
+
#
|
22
|
+
# @param params [Hash]
|
23
|
+
# @return [Hash]
|
24
|
+
def bulk_form(params)
|
25
|
+
post("/bulkform", params: params)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Generates a TPIN for the client.
|
29
|
+
#
|
30
|
+
# @return [Hash]
|
31
|
+
def tpin
|
32
|
+
get("/tpin")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Checks the EDIS status for a given ISIN.
|
36
|
+
#
|
37
|
+
# @param isin [String]
|
38
|
+
# @return [Hash]
|
39
|
+
def inquire(isin)
|
40
|
+
get("/inquire/#{isin}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client for GTT/forever order management.
|
6
|
+
class ForeverOrders < BaseAPI
|
7
|
+
# Uses the trading API tier.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Root path for forever order operations.
|
10
|
+
HTTP_PATH = "/v2/forever"
|
11
|
+
|
12
|
+
# Lists all forever orders for the account.
|
13
|
+
#
|
14
|
+
# @return [Array<Hash>]
|
15
|
+
def all
|
16
|
+
get("/orders")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates a new forever order configuration.
|
20
|
+
#
|
21
|
+
# @param params [Hash]
|
22
|
+
# @return [Hash]
|
23
|
+
def create(params)
|
24
|
+
post("/orders", params: params)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Fetches a forever order by identifier.
|
28
|
+
#
|
29
|
+
# @param order_id [String]
|
30
|
+
# @return [Hash]
|
31
|
+
def find(order_id)
|
32
|
+
get("/orders/#{order_id}")
|
33
|
+
end
|
34
|
+
|
35
|
+
# Updates a forever order.
|
36
|
+
#
|
37
|
+
# @param order_id [String]
|
38
|
+
# @param params [Hash]
|
39
|
+
# @return [Hash]
|
40
|
+
def update(order_id, params)
|
41
|
+
put("/orders/#{order_id}", params: params)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Cancels a forever order.
|
45
|
+
#
|
46
|
+
# @param order_id [String]
|
47
|
+
# @return [Hash]
|
48
|
+
def cancel(order_id)
|
49
|
+
delete("/orders/#{order_id}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client that exposes funds and limits endpoints.
|
6
|
+
class Funds < BaseAPI
|
7
|
+
# Funds data comes from the trading API tier.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Base path for fund limit endpoints.
|
10
|
+
HTTP_PATH = "/v2/fundlimit"
|
11
|
+
|
12
|
+
##
|
13
|
+
# Fetch fund limit details.
|
14
|
+
#
|
15
|
+
# @return [Hash] API response containing fund details.
|
16
|
+
def fetch
|
17
|
+
get("")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
##
|
6
|
+
# Resource class for fetching Daily & Intraday historical data.
|
7
|
+
# Based on the official docs:
|
8
|
+
# - POST /v2/charts/historical (daily timeframe)
|
9
|
+
# - POST /v2/charts/intraday (minute timeframe)
|
10
|
+
#
|
11
|
+
class HistoricalData < BaseAPI
|
12
|
+
API_TYPE = :data_api # Because we are fetching market data
|
13
|
+
HTTP_PATH = "/v2/charts" # The base path for historical endpoints
|
14
|
+
|
15
|
+
##
|
16
|
+
# POST /v2/charts/historical
|
17
|
+
# "Daily Historical Data"
|
18
|
+
# @param params [Hash]
|
19
|
+
# @return [Hash] The parsed API response containing arrays of open, high, low, close, volume, timestamp
|
20
|
+
def daily(params)
|
21
|
+
post("/historical", params: params)
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# POST /v2/charts/intraday
|
26
|
+
# "Intraday Historical Data"
|
27
|
+
# @param params [Hash]
|
28
|
+
# @return [Hash] The parsed API response containing arrays for candle data
|
29
|
+
def intraday(params)
|
30
|
+
post("/intraday", params: params)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client exposing portfolio holdings.
|
6
|
+
class Holdings < BaseAPI
|
7
|
+
# Holdings are exposed via the trading API.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Base path for holdings queries.
|
10
|
+
HTTP_PATH = "/v2/holdings"
|
11
|
+
|
12
|
+
##
|
13
|
+
# Fetch all holdings.
|
14
|
+
#
|
15
|
+
# @return [Array<Hash>] API response containing holdings data.
|
16
|
+
def all
|
17
|
+
get("")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client to control the trading kill switch feature.
|
6
|
+
class KillSwitch < BaseAPI
|
7
|
+
# Kill switch operations execute on the trading API tier.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Base path for kill switch operations.
|
10
|
+
HTTP_PATH = "/v2/killswitch"
|
11
|
+
|
12
|
+
# Enables or disables the kill switch.
|
13
|
+
#
|
14
|
+
# @param params [Hash]
|
15
|
+
# @return [Hash]
|
16
|
+
def update(params)
|
17
|
+
post("", params: params)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client for invoking the margin calculator endpoint.
|
6
|
+
class MarginCalculator < BaseAPI
|
7
|
+
# Calculator results are served via the trading API.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Base path for the calculator endpoint.
|
10
|
+
HTTP_PATH = "/v2/margincalculator"
|
11
|
+
|
12
|
+
##
|
13
|
+
# Calculate margin requirements for an order.
|
14
|
+
#
|
15
|
+
# @param params [Hash] Request parameters for margin calculation.
|
16
|
+
# @return [Hash] API response containing margin details.
|
17
|
+
def calculate(params)
|
18
|
+
post("", params: params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client for fetching on-demand market data snapshots.
|
6
|
+
class MarketFeed < BaseAPI
|
7
|
+
# Market feed requests hit the data API tier.
|
8
|
+
API_TYPE = :data_api
|
9
|
+
# Root path for market feed endpoints.
|
10
|
+
HTTP_PATH = "/v2"
|
11
|
+
|
12
|
+
##
|
13
|
+
# POST /v2/marketfeed/ltp
|
14
|
+
# Returns the LTP (Last Traded Price) of up to 1000 instruments.
|
15
|
+
#
|
16
|
+
# @param params [Hash] Example:
|
17
|
+
# {
|
18
|
+
# "NSE_EQ": [11536, 3456],
|
19
|
+
# "NSE_FNO": [49081, 49082]
|
20
|
+
# }
|
21
|
+
# @return [HashWithIndifferentAccess]
|
22
|
+
def ltp(params)
|
23
|
+
post("/marketfeed/ltp", params: params)
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# POST /v2/marketfeed/ohlc
|
28
|
+
# Returns open-high-low-close for up to 1000 instruments.
|
29
|
+
#
|
30
|
+
# @param params [Hash]
|
31
|
+
# @return [HashWithIndifferentAccess]
|
32
|
+
def ohlc(params)
|
33
|
+
post("/marketfeed/ohlc", params: params)
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# POST /v2/marketfeed/quote
|
38
|
+
# Returns market depth, OI, and other details for up to 1000 instruments.
|
39
|
+
#
|
40
|
+
# @param params [Hash]
|
41
|
+
# @return [HashWithIndifferentAccess]
|
42
|
+
def quote(params)
|
43
|
+
quote_resource.post("/marketfeed/quote", params: params)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Lazily builds a `:quote_api` scoped client for quote depth requests.
|
49
|
+
#
|
50
|
+
# @return [MarketFeed]
|
51
|
+
def quote_resource
|
52
|
+
@quote_resource ||= self.class.new(api_type: :quote_api)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client for querying option chain data APIs.
|
6
|
+
class OptionChain < BaseAPI
|
7
|
+
# Option chain queries have bespoke rate limits, hence their own API type.
|
8
|
+
API_TYPE = :option_chain
|
9
|
+
# Base path for option chain endpoints.
|
10
|
+
HTTP_PATH = "/v2/optionchain"
|
11
|
+
|
12
|
+
##
|
13
|
+
# Fetch option chain data based on provided parameters.
|
14
|
+
#
|
15
|
+
# @param params [Hash] Query parameters for the request.
|
16
|
+
# @return [Hash] API response containing option chain data.
|
17
|
+
def fetch(params)
|
18
|
+
post("", params: params)
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Fetch expiry dates list based on provided parameters.
|
23
|
+
#
|
24
|
+
# @param params [Hash] Query parameters for the request.
|
25
|
+
# @return [Hash] API response containing option chain data.
|
26
|
+
def expirylist(params)
|
27
|
+
post("/expirylist", params: params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
# REST API wrappers grouped by resource type.
|
5
|
+
module Resources
|
6
|
+
# Resource client for managing equity and F&O orders.
|
7
|
+
class Orders < BaseAPI
|
8
|
+
# Orders are routed through the trading API tier.
|
9
|
+
API_TYPE = :order_api
|
10
|
+
# Base path for order endpoints.
|
11
|
+
HTTP_PATH = "/v2/orders"
|
12
|
+
|
13
|
+
# Retrieve all orders for the current trading day.
|
14
|
+
#
|
15
|
+
# @return [Array<Hash>]
|
16
|
+
def all
|
17
|
+
get("")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Places a new order using the provided payload.
|
21
|
+
#
|
22
|
+
# @param params [Hash]
|
23
|
+
# @return [Hash]
|
24
|
+
def create(params)
|
25
|
+
post("", params: params)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fetches a single order by broker order id.
|
29
|
+
#
|
30
|
+
# @param order_id [String]
|
31
|
+
# @return [Hash]
|
32
|
+
def find(order_id)
|
33
|
+
get("/#{order_id}")
|
34
|
+
end
|
35
|
+
|
36
|
+
# Modifies an existing order.
|
37
|
+
#
|
38
|
+
# @param order_id [String]
|
39
|
+
# @param params [Hash]
|
40
|
+
# @return [Hash]
|
41
|
+
def update(order_id, params)
|
42
|
+
put("/#{order_id}", params: params)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Cancels an existing order.
|
46
|
+
#
|
47
|
+
# @param order_id [String]
|
48
|
+
# @return [Hash]
|
49
|
+
def cancel(order_id)
|
50
|
+
delete("/#{order_id}")
|
51
|
+
end
|
52
|
+
|
53
|
+
# Places a slicing order request.
|
54
|
+
#
|
55
|
+
# @param params [Hash]
|
56
|
+
# @return [Hash]
|
57
|
+
def slicing(params)
|
58
|
+
post("/slicing", params: params)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Retrieve an order by client-supplied correlation id.
|
62
|
+
#
|
63
|
+
# @param correlation_id [String]
|
64
|
+
# @return [Hash]
|
65
|
+
def by_correlation(correlation_id)
|
66
|
+
get("/external/#{correlation_id}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
# Resource client managing intraday and carry-forward positions.
|
6
|
+
class Positions < BaseAPI
|
7
|
+
# Position endpoints are exposed via the trading API.
|
8
|
+
API_TYPE = :order_api
|
9
|
+
# Base path for position management endpoints.
|
10
|
+
HTTP_PATH = "/v2/positions"
|
11
|
+
|
12
|
+
##
|
13
|
+
# Fetch all open positions for the day.
|
14
|
+
#
|
15
|
+
# @return [Array<Hash>] API response containing position data.
|
16
|
+
def all
|
17
|
+
get("")
|
18
|
+
end
|
19
|
+
|
20
|
+
# Converts a position between eligible product types.
|
21
|
+
#
|
22
|
+
# @param params [Hash]
|
23
|
+
# @return [Hash]
|
24
|
+
def convert(params)
|
25
|
+
post("/convert", params: params)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
##
|
6
|
+
# Provides access to the user profile endpoint.
|
7
|
+
#
|
8
|
+
# The endpoint is a simple GET request to `/v2/profile` that returns
|
9
|
+
# account level metadata (token validity, active segments, etc.).
|
10
|
+
class Profile < BaseAPI
|
11
|
+
# Profile metadata is served from the non-trading API tier.
|
12
|
+
API_TYPE = :non_trading_api
|
13
|
+
# Base path for profile lookups.
|
14
|
+
HTTP_PATH = "/v2/profile"
|
15
|
+
|
16
|
+
##
|
17
|
+
# Fetch the authenticated user's profile information.
|
18
|
+
#
|
19
|
+
# @return [Hash] Parsed response from the profile endpoint.
|
20
|
+
def fetch
|
21
|
+
get("")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
##
|
6
|
+
# Provides methods to retrieve Ledger and Trade History.
|
7
|
+
#
|
8
|
+
# GET /v2/ledger?from-date=YYYY-MM-DD&to-date=YYYY-MM-DD
|
9
|
+
# GET /v2/trades/{from-date}/{to-date}/{page}
|
10
|
+
#
|
11
|
+
class Statements < BaseAPI
|
12
|
+
# Statement history is fetched from the non-trading API tier.
|
13
|
+
API_TYPE = :non_trading_api
|
14
|
+
# Base path for ledger and trade history.
|
15
|
+
HTTP_PATH = "/v2"
|
16
|
+
|
17
|
+
##
|
18
|
+
# GET /v2/ledger?from-date=YYYY-MM-DD&to-date=YYYY-MM-DD
|
19
|
+
# @param from_date [String] e.g. "2023-01-01"
|
20
|
+
# @param to_date [String] e.g. "2023-01-31"
|
21
|
+
# @return [Array<Hash>] An array of ledger entries
|
22
|
+
def ledger(from_date:, to_date:)
|
23
|
+
# Because the docs say "from-date" & "to-date" (with dashes),
|
24
|
+
# pass them as snake case or match them exactly:
|
25
|
+
get("/ledger", params: { "from-date": from_date, "to-date": to_date })
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# GET /v2/trades/{from-date}/{to-date}/{page}
|
30
|
+
# @param from_date [String]
|
31
|
+
# @param to_date [String]
|
32
|
+
# @param page [Integer] Defaults to 0
|
33
|
+
# @return [Array<Hash>] An array of trades
|
34
|
+
#
|
35
|
+
def trade_history(from_date:, to_date:, page: 0)
|
36
|
+
# docs show this path style:
|
37
|
+
# /v2/trades/{from-date}/{to-date}/{page}
|
38
|
+
get("/trades/#{from_date}/#{to_date}/#{page}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|