DhanHQ 2.1.8 → 2.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/lib/DhanHQ/contracts/expired_options_data_contract.rb +6 -6
- data/lib/DhanHQ/core/base_model.rb +9 -1
- data/lib/DhanHQ/models/edis.rb +150 -14
- data/lib/DhanHQ/models/expired_options_data.rb +307 -82
- data/lib/DhanHQ/models/forever_order.rb +261 -22
- data/lib/DhanHQ/models/funds.rb +76 -10
- data/lib/DhanHQ/models/historical_data.rb +148 -31
- data/lib/DhanHQ/models/holding.rb +82 -6
- data/lib/DhanHQ/models/kill_switch.rb +113 -11
- data/lib/DhanHQ/models/ledger_entry.rb +101 -13
- data/lib/DhanHQ/models/margin.rb +133 -8
- data/lib/DhanHQ/models/market_feed.rb +181 -17
- data/lib/DhanHQ/models/option_chain.rb +184 -12
- data/lib/DhanHQ/models/order.rb +399 -34
- data/lib/DhanHQ/models/position.rb +161 -10
- data/lib/DhanHQ/models/profile.rb +103 -7
- data/lib/DhanHQ/models/super_order.rb +275 -15
- data/lib/DhanHQ/models/trade.rb +279 -26
- data/lib/DhanHQ/resources/expired_options_data.rb +1 -1
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/orders/client.rb +2 -1
- data/lib/DhanHQ/ws/orders.rb +2 -1
- metadata +1 -1
data/lib/DhanHQ/models/margin.rb
CHANGED
|
@@ -2,7 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
module DhanHQ
|
|
4
4
|
module Models
|
|
5
|
-
|
|
5
|
+
##
|
|
6
|
+
# Model for fetching margin calculation results for orders.
|
|
7
|
+
#
|
|
8
|
+
# The Margin Calculator API provides span margin, exposure margin, VAR (variable margin),
|
|
9
|
+
# brokerage, leverage, and available margin values for any type of order and instrument
|
|
10
|
+
# you want to place. This helps you determine the margin requirements before placing an order.
|
|
11
|
+
#
|
|
12
|
+
# @example Calculate margin for a CNC order
|
|
13
|
+
# margin = DhanHQ::Models::Margin.calculate(
|
|
14
|
+
# dhan_client_id: "1000000132",
|
|
15
|
+
# exchange_segment: "NSE_EQ",
|
|
16
|
+
# transaction_type: "BUY",
|
|
17
|
+
# quantity: 5,
|
|
18
|
+
# product_type: "CNC",
|
|
19
|
+
# security_id: "1333",
|
|
20
|
+
# price: 1428.0
|
|
21
|
+
# )
|
|
22
|
+
# puts "Total margin required: ₹#{margin.total_margin}"
|
|
23
|
+
# puts "Available balance: ₹#{margin.available_balance}"
|
|
24
|
+
#
|
|
25
|
+
# @example Calculate margin for stop-loss order
|
|
26
|
+
# margin = DhanHQ::Models::Margin.calculate(
|
|
27
|
+
# dhan_client_id: "1000000132",
|
|
28
|
+
# exchange_segment: "NSE_EQ",
|
|
29
|
+
# transaction_type: "BUY",
|
|
30
|
+
# quantity: 10,
|
|
31
|
+
# product_type: "INTRADAY",
|
|
32
|
+
# security_id: "1333",
|
|
33
|
+
# price: 1428.0,
|
|
34
|
+
# trigger_price: 1427.0
|
|
35
|
+
# )
|
|
36
|
+
# puts "Leverage: #{margin.leverage}x"
|
|
37
|
+
#
|
|
38
|
+
# @example Check if sufficient margin is available
|
|
39
|
+
# margin = DhanHQ::Models::Margin.calculate(params)
|
|
40
|
+
# if margin.insufficient_balance > 0
|
|
41
|
+
# puts "Insufficient balance: ₹#{margin.insufficient_balance}"
|
|
42
|
+
# else
|
|
43
|
+
# puts "Sufficient margin available"
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
6
46
|
class Margin < BaseModel
|
|
7
47
|
# Base path used to invoke the calculator.
|
|
8
48
|
HTTP_PATH = "/v2/margincalculator"
|
|
@@ -12,18 +52,86 @@ module DhanHQ
|
|
|
12
52
|
|
|
13
53
|
class << self
|
|
14
54
|
##
|
|
15
|
-
# Provides a
|
|
55
|
+
# Provides a shared instance of the MarginCalculator resource.
|
|
16
56
|
#
|
|
17
|
-
# @return [DhanHQ::Resources::MarginCalculator]
|
|
57
|
+
# @return [DhanHQ::Resources::MarginCalculator] The MarginCalculator resource client instance
|
|
18
58
|
def resource
|
|
19
59
|
@resource ||= DhanHQ::Resources::MarginCalculator.new
|
|
20
60
|
end
|
|
21
61
|
|
|
22
62
|
##
|
|
23
|
-
#
|
|
63
|
+
# Calculates margin requirements for an order before placement.
|
|
24
64
|
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
65
|
+
# Fetches span margin, exposure margin, VAR (variable margin), brokerage, leverage,
|
|
66
|
+
# and available margin values for the specified order parameters. This allows you to
|
|
67
|
+
# check margin requirements and availability before actually placing the order.
|
|
68
|
+
#
|
|
69
|
+
# @param params [Hash{Symbol => String, Integer, Float}] Request parameters for margin calculation
|
|
70
|
+
# @option params [String] :dhan_client_id (required) User-specific identification generated by Dhan.
|
|
71
|
+
# Must be explicitly provided in the params hash
|
|
72
|
+
# @option params [String] :exchange_segment (required) Exchange and segment identifier.
|
|
73
|
+
# Valid values: "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", "MCX_COMM"
|
|
74
|
+
# @option params [String] :transaction_type (required) The trading side of transaction.
|
|
75
|
+
# Valid values: "BUY", "SELL"
|
|
76
|
+
# @option params [Integer] :quantity (required) Number of shares for the order. Must be greater than 0
|
|
77
|
+
# @option params [String] :product_type (required) Product type.
|
|
78
|
+
# Valid values: "CNC", "INTRADAY", "MARGIN", "MTF", "CO", "BO"
|
|
79
|
+
# @option params [String] :security_id (required) Exchange standard ID for each scrip
|
|
80
|
+
# @option params [Float] :price (required) Price at which order is placed. Must be greater than 0
|
|
81
|
+
# @option params [Float] :trigger_price (conditionally required) Price at which the order is triggered.
|
|
82
|
+
# Required for STOP_LOSS and STOP_LOSS_MARKET order types
|
|
83
|
+
#
|
|
84
|
+
# @return [Margin] Margin object containing margin calculation results.
|
|
85
|
+
# Response structure (keys normalized to snake_case):
|
|
86
|
+
# - **:total_margin** [Float] Total margin required for placing the order successfully
|
|
87
|
+
# - **:span_margin** [Float] SPAN margin required
|
|
88
|
+
# - **:exposure_margin** [Float] Exposure margin required
|
|
89
|
+
# - **:available_balance** [Float] Available amount in trading account
|
|
90
|
+
# - **:variable_margin** [Float] VAR or Variable margin required
|
|
91
|
+
# - **:insufficient_balance** [Float] Insufficient amount in trading account
|
|
92
|
+
# (Available Balance - Total Margin). Negative or zero indicates sufficient margin
|
|
93
|
+
# - **:brokerage** [Float] Brokerage charges for executing the order
|
|
94
|
+
# - **:leverage** [String] Margin leverage provided for the order as per product type
|
|
95
|
+
#
|
|
96
|
+
# @example Calculate margin for CNC equity order
|
|
97
|
+
# margin = DhanHQ::Models::Margin.calculate(
|
|
98
|
+
# dhan_client_id: "1000000132",
|
|
99
|
+
# exchange_segment: "NSE_EQ",
|
|
100
|
+
# transaction_type: "BUY",
|
|
101
|
+
# quantity: 5,
|
|
102
|
+
# product_type: "CNC",
|
|
103
|
+
# security_id: "1333",
|
|
104
|
+
# price: 1428.0
|
|
105
|
+
# )
|
|
106
|
+
# puts "Total Margin: ₹#{margin.total_margin}"
|
|
107
|
+
# puts "Brokerage: ₹#{margin.brokerage}"
|
|
108
|
+
#
|
|
109
|
+
# @example Calculate margin for intraday order
|
|
110
|
+
# margin = DhanHQ::Models::Margin.calculate(
|
|
111
|
+
# dhan_client_id: "1000000132",
|
|
112
|
+
# exchange_segment: "NSE_EQ",
|
|
113
|
+
# transaction_type: "SELL",
|
|
114
|
+
# quantity: 10,
|
|
115
|
+
# product_type: "INTRADAY",
|
|
116
|
+
# security_id: "1333",
|
|
117
|
+
# price: 1500.0
|
|
118
|
+
# )
|
|
119
|
+
# puts "Leverage: #{margin.leverage}x"
|
|
120
|
+
# puts "SPAN Margin: ₹#{margin.span_margin}"
|
|
121
|
+
#
|
|
122
|
+
# @example Calculate margin for stop-loss order
|
|
123
|
+
# margin = DhanHQ::Models::Margin.calculate(
|
|
124
|
+
# dhan_client_id: "1000000132",
|
|
125
|
+
# exchange_segment: "NSE_EQ",
|
|
126
|
+
# transaction_type: "BUY",
|
|
127
|
+
# quantity: 5,
|
|
128
|
+
# product_type: "INTRADAY",
|
|
129
|
+
# security_id: "1333",
|
|
130
|
+
# price: 1428.0,
|
|
131
|
+
# trigger_price: 1427.0
|
|
132
|
+
# )
|
|
133
|
+
#
|
|
134
|
+
# @raise [DhanHQ::ValidationError] If validation fails for any parameter
|
|
27
135
|
def calculate(params)
|
|
28
136
|
formatted_params = camelize_keys(params)
|
|
29
137
|
validate_params!(formatted_params, DhanHQ::Contracts::MarginCalculatorContract)
|
|
@@ -34,9 +142,26 @@ module DhanHQ
|
|
|
34
142
|
end
|
|
35
143
|
|
|
36
144
|
##
|
|
37
|
-
#
|
|
145
|
+
# Converts the Margin model attributes to a hash representation.
|
|
146
|
+
#
|
|
147
|
+
# Useful for serialization, logging, or passing margin data to other methods.
|
|
148
|
+
#
|
|
149
|
+
# @return [Hash{Symbol => Float, String}] Hash representation of the Margin model containing:
|
|
150
|
+
# - **:total_margin** [Float] Total margin required
|
|
151
|
+
# - **:span_margin** [Float] SPAN margin
|
|
152
|
+
# - **:exposure_margin** [Float] Exposure margin
|
|
153
|
+
# - **:available_balance** [Float] Available balance
|
|
154
|
+
# - **:variable_margin** [Float] Variable margin
|
|
155
|
+
# - **:insufficient_balance** [Float] Insufficient balance amount
|
|
156
|
+
# - **:brokerage** [Float] Brokerage charges
|
|
157
|
+
# - **:leverage** [String] Leverage as string
|
|
158
|
+
#
|
|
159
|
+
# @example Convert margin to hash
|
|
160
|
+
# margin = DhanHQ::Models::Margin.calculate(params)
|
|
161
|
+
# margin_hash = margin.to_h
|
|
162
|
+
# puts margin_hash[:total_margin] # => 2800.00
|
|
163
|
+
# puts margin_hash[:leverage] # => "4.00"
|
|
38
164
|
#
|
|
39
|
-
# @return [Hash] Hash representation of the Margin model.
|
|
40
165
|
def to_h
|
|
41
166
|
{
|
|
42
167
|
total_margin: total_margin,
|
|
@@ -2,39 +2,203 @@
|
|
|
2
2
|
|
|
3
3
|
module DhanHQ
|
|
4
4
|
module Models
|
|
5
|
-
|
|
5
|
+
##
|
|
6
|
+
# Model for fetching real-time market snapshots for multiple instruments.
|
|
7
|
+
#
|
|
8
|
+
# The Market Feed API provides snapshots of multiple instruments at once. You can fetch
|
|
9
|
+
# LTP (Last Traded Price), OHLC (Open, High, Low, Close), or Market Depth (quote) data
|
|
10
|
+
# for instruments via a single API request. Data is returned in real-time at the time
|
|
11
|
+
# of the API request.
|
|
12
|
+
#
|
|
13
|
+
# @note **Rate Limits**: You can fetch up to 1000 instruments in a single API request
|
|
14
|
+
# with a rate limit of 1 request per second. The client's internal rate limiter
|
|
15
|
+
# automatically throttles calls to prevent exceeding limits.
|
|
16
|
+
#
|
|
17
|
+
# @example Fetch LTP for multiple instruments
|
|
18
|
+
# payload = {
|
|
19
|
+
# "NSE_EQ" => [11536, 3456],
|
|
20
|
+
# "NSE_FNO" => [49081, 49082]
|
|
21
|
+
# }
|
|
22
|
+
# response = DhanHQ::Models::MarketFeed.ltp(payload)
|
|
23
|
+
# nse_eq_data = response[:data]["NSE_EQ"]
|
|
24
|
+
# puts "TCS LTP: ₹#{nse_eq_data["11536"][:last_price]}"
|
|
25
|
+
#
|
|
26
|
+
# @example Fetch OHLC data for equity instruments
|
|
27
|
+
# payload = {
|
|
28
|
+
# "NSE_EQ" => [11536]
|
|
29
|
+
# }
|
|
30
|
+
# response = DhanHQ::Models::MarketFeed.ohlc(payload)
|
|
31
|
+
# tcs_data = response[:data]["NSE_EQ"]["11536"]
|
|
32
|
+
# puts "Open: ₹#{tcs_data[:ohlc][:open]}"
|
|
33
|
+
# puts "High: ₹#{tcs_data[:ohlc][:high]}"
|
|
34
|
+
#
|
|
35
|
+
# @example Fetch full market depth quote
|
|
36
|
+
# payload = {
|
|
37
|
+
# "NSE_FNO" => [49081]
|
|
38
|
+
# }
|
|
39
|
+
# response = DhanHQ::Models::MarketFeed.quote(payload)
|
|
40
|
+
# quote_data = response[:data]["NSE_FNO"]["49081"]
|
|
41
|
+
# puts "Volume: #{quote_data[:volume]}"
|
|
42
|
+
# puts "Open Interest: #{quote_data[:oi]}"
|
|
43
|
+
#
|
|
6
44
|
class MarketFeed < BaseModel
|
|
7
45
|
class << self
|
|
8
|
-
|
|
46
|
+
##
|
|
47
|
+
# Provides a shared instance of the MarketFeed resource.
|
|
48
|
+
#
|
|
49
|
+
# @return [DhanHQ::Resources::MarketFeed] The MarketFeed resource client instance
|
|
50
|
+
def resource
|
|
51
|
+
@resource ||= DhanHQ::Resources::MarketFeed.new
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# Fetches Last Traded Price (LTP) snapshots for multiple instruments.
|
|
56
|
+
#
|
|
57
|
+
# Retrieves the last traded price for a list of instruments with a single API request.
|
|
58
|
+
# Supports up to 1000 instruments per request, organized by exchange segment.
|
|
59
|
+
#
|
|
60
|
+
# @param params [Hash{String => Array<Integer>}] Request payload mapping exchange segments
|
|
61
|
+
# to arrays of security IDs. Exchange segments are keys (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ").
|
|
62
|
+
# Values are arrays of security IDs (integer identifiers for each scrip).
|
|
63
|
+
# Valid exchange segment values: "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", etc.
|
|
64
|
+
#
|
|
65
|
+
# @return [HashWithIndifferentAccess] Response hash containing market data.
|
|
66
|
+
# Response structure:
|
|
67
|
+
# - **:data** [Hash{String => Hash{String => Hash}}] Market data organized by exchange segment
|
|
68
|
+
# and security ID. Each instrument's data contains:
|
|
69
|
+
# - **:last_price** [Float] Last traded price of the instrument
|
|
70
|
+
# - **:status** [String] Response status (typically "success")
|
|
71
|
+
#
|
|
72
|
+
# @example Fetch LTP for equity and F&O instruments
|
|
73
|
+
# payload = {
|
|
74
|
+
# "NSE_EQ" => [11536],
|
|
75
|
+
# "NSE_FNO" => [49081, 49082]
|
|
76
|
+
# }
|
|
77
|
+
# response = DhanHQ::Models::MarketFeed.ltp(payload)
|
|
78
|
+
# tcs_ltp = response[:data]["NSE_EQ"]["11536"][:last_price]
|
|
79
|
+
# puts "TCS LTP: ₹#{tcs_ltp}"
|
|
80
|
+
#
|
|
81
|
+
# @example Access data from response
|
|
82
|
+
# response = DhanHQ::Models::MarketFeed.ltp("NSE_EQ" => [11536])
|
|
83
|
+
# data = response[:data]["NSE_EQ"]["11536"]
|
|
84
|
+
# puts "Last Price: ₹#{data[:last_price]}"
|
|
9
85
|
#
|
|
10
|
-
# @param params [Hash]
|
|
11
|
-
# @return [Hash]
|
|
12
86
|
def ltp(params)
|
|
13
87
|
resource.ltp(params)
|
|
14
88
|
end
|
|
15
89
|
|
|
16
|
-
|
|
90
|
+
##
|
|
91
|
+
# Fetches OHLC (Open, High, Low, Close) data along with LTP for specified instruments.
|
|
92
|
+
#
|
|
93
|
+
# Retrieves the open, high, low, and close prices along with the last traded price
|
|
94
|
+
# for a list of instruments. Supports up to 1000 instruments per request.
|
|
95
|
+
#
|
|
96
|
+
# @param params [Hash{String => Array<Integer>}] Request payload mapping exchange segments
|
|
97
|
+
# to arrays of security IDs. Exchange segments are keys (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ").
|
|
98
|
+
# Values are arrays of security IDs (integer identifiers for each scrip).
|
|
99
|
+
# Valid exchange segment values: "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", etc.
|
|
100
|
+
#
|
|
101
|
+
# @return [HashWithIndifferentAccess] Response hash containing OHLC market data.
|
|
102
|
+
# Response structure:
|
|
103
|
+
# - **:data** [Hash{String => Hash{String => Hash}}] Market data organized by exchange segment
|
|
104
|
+
# and security ID. Each instrument's data contains:
|
|
105
|
+
# - **:last_price** [Float] Last traded price of the instrument
|
|
106
|
+
# - **:ohlc** [Hash{Symbol => Float}] OHLC data:
|
|
107
|
+
# - **:open** [Float] Market opening price of the day
|
|
108
|
+
# - **:close** [Float] Market closing price of the day (previous day close for current session)
|
|
109
|
+
# - **:high** [Float] Day high price
|
|
110
|
+
# - **:low** [Float] Day low price
|
|
111
|
+
# - **:status** [String] Response status (typically "success")
|
|
112
|
+
#
|
|
113
|
+
# @note For newly listed instruments or instruments without trading activity,
|
|
114
|
+
# OHLC values may be 0. The close price typically represents the previous day's
|
|
115
|
+
# closing price during the current trading session.
|
|
116
|
+
#
|
|
117
|
+
# @example Fetch OHLC for equity instruments
|
|
118
|
+
# payload = {
|
|
119
|
+
# "NSE_EQ" => [11536]
|
|
120
|
+
# }
|
|
121
|
+
# response = DhanHQ::Models::MarketFeed.ohlc(payload)
|
|
122
|
+
# tcs_data = response[:data]["NSE_EQ"]["11536"]
|
|
123
|
+
# puts "Open: ₹#{tcs_data[:ohlc][:open]}"
|
|
124
|
+
# puts "High: ₹#{tcs_data[:ohlc][:high]}"
|
|
125
|
+
# puts "Low: ₹#{tcs_data[:ohlc][:low]}"
|
|
126
|
+
# puts "Close: ₹#{tcs_data[:ohlc][:close]}"
|
|
127
|
+
# puts "LTP: ₹#{tcs_data[:last_price]}"
|
|
17
128
|
#
|
|
18
|
-
# @param params [Hash]
|
|
19
|
-
# @return [Hash]
|
|
20
129
|
def ohlc(params)
|
|
21
130
|
resource.ohlc(params)
|
|
22
131
|
end
|
|
23
132
|
|
|
24
|
-
|
|
133
|
+
##
|
|
134
|
+
# Fetches full market depth data including OHLC, Open Interest, Volume, and order book depth.
|
|
135
|
+
#
|
|
136
|
+
# Retrieves comprehensive market data including market depth (buy/sell orders), OHLC data,
|
|
137
|
+
# Open Interest (for derivatives), Volume, circuit limits, and other trading analytics
|
|
138
|
+
# for specified instruments. Supports up to 1000 instruments per request.
|
|
139
|
+
#
|
|
140
|
+
# @param params [Hash{String => Array<Integer>}] Request payload mapping exchange segments
|
|
141
|
+
# to arrays of security IDs. Exchange segments are keys (e.g., "NSE_EQ", "NSE_FNO", "BSE_EQ").
|
|
142
|
+
# Values are arrays of security IDs (integer identifiers for each scrip).
|
|
143
|
+
# Valid exchange segment values: "NSE_EQ", "NSE_FNO", "BSE_EQ", "BSE_FNO", etc.
|
|
144
|
+
#
|
|
145
|
+
# @return [HashWithIndifferentAccess] Response hash containing full quote market data.
|
|
146
|
+
# Response structure:
|
|
147
|
+
# - **:data** [Hash{String => Hash{String => Hash}}] Market data organized by exchange segment
|
|
148
|
+
# and security ID. Each instrument's data contains:
|
|
149
|
+
# - **:last_price** [Float] Last traded price of the instrument
|
|
150
|
+
# - **:last_quantity** [Integer] Last traded quantity
|
|
151
|
+
# - **:last_trade_time** [String] Timestamp of last trade in "DD/MM/YYYY HH:MM:SS" format
|
|
152
|
+
# - **:average_price** [Float] Volume weighted average price (VWAP) of the day
|
|
153
|
+
# - **:buy_quantity** [Integer] Total buy order quantity pending at the exchange
|
|
154
|
+
# - **:sell_quantity** [Integer] Total sell order quantity pending at the exchange
|
|
155
|
+
# - **:volume** [Integer] Total traded volume for the day
|
|
156
|
+
# - **:oi** [Integer] Open Interest in the contract (for derivatives)
|
|
157
|
+
# - **:oi_day_high** [Integer] Highest Open Interest for the day (only for NSE_FNO)
|
|
158
|
+
# - **:oi_day_low** [Integer] Lowest Open Interest for the day (only for NSE_FNO)
|
|
159
|
+
# - **:net_change** [Float] Absolute change in LTP from previous day closing price
|
|
160
|
+
# - **:upper_circuit_limit** [Float] Current upper circuit limit
|
|
161
|
+
# - **:lower_circuit_limit** [Float] Current lower circuit limit
|
|
162
|
+
# - **:ohlc** [Hash{Symbol => Float}] OHLC data:
|
|
163
|
+
# - **:open** [Float] Market opening price of the day
|
|
164
|
+
# - **:close** [Float] Market closing price of the day
|
|
165
|
+
# - **:high** [Float] Day high price
|
|
166
|
+
# - **:low** [Float] Day low price
|
|
167
|
+
# - **:depth** [Hash{Symbol => Array<Hash>}] Market depth (order book) data:
|
|
168
|
+
# - **:buy** [Array<Hash{Symbol => Integer, Float}>] Buy side depth levels (up to 5 levels):
|
|
169
|
+
# - **:quantity** [Integer] Number of quantity at this price depth
|
|
170
|
+
# - **:orders** [Integer] Number of open BUY orders at this price depth
|
|
171
|
+
# - **:price** [Float] Price at which the BUY depth stands
|
|
172
|
+
# - **:sell** [Array<Hash{Symbol => Integer, Float}>] Sell side depth levels (up to 5 levels):
|
|
173
|
+
# - **:quantity** [Integer] Number of quantity at this price depth
|
|
174
|
+
# - **:orders** [Integer] Number of open SELL orders at this price depth
|
|
175
|
+
# - **:price** [Float] Price at which the SELL depth stands
|
|
176
|
+
# - **:status** [String] Response status (typically "success")
|
|
177
|
+
#
|
|
178
|
+
# @note This endpoint uses a separate quote API with stricter rate limits (1 request per second).
|
|
179
|
+
# The client automatically handles rate limiting for quote requests.
|
|
180
|
+
#
|
|
181
|
+
# @example Fetch full quote for futures contract
|
|
182
|
+
# payload = {
|
|
183
|
+
# "NSE_FNO" => [49081]
|
|
184
|
+
# }
|
|
185
|
+
# response = DhanHQ::Models::MarketFeed.quote(payload)
|
|
186
|
+
# quote = response[:data]["NSE_FNO"]["49081"]
|
|
187
|
+
# puts "LTP: ₹#{quote[:last_price]}"
|
|
188
|
+
# puts "Volume: #{quote[:volume]}"
|
|
189
|
+
# puts "Open Interest: #{quote[:oi]}"
|
|
190
|
+
# puts "Day High OI: #{quote[:oi_day_high]}"
|
|
191
|
+
#
|
|
192
|
+
# @example Access market depth (order book)
|
|
193
|
+
# response = DhanHQ::Models::MarketFeed.quote("NSE_FNO" => [49081])
|
|
194
|
+
# quote = response[:data]["NSE_FNO"]["49081"]
|
|
195
|
+
# buy_depth = quote[:depth][:buy]
|
|
196
|
+
# puts "Best Buy Price: ₹#{buy_depth[0][:price]}"
|
|
197
|
+
# puts "Best Buy Quantity: #{buy_depth[0][:quantity]}"
|
|
25
198
|
#
|
|
26
|
-
# @param params [Hash]
|
|
27
|
-
# @return [Hash]
|
|
28
199
|
def quote(params)
|
|
29
200
|
resource.quote(params)
|
|
30
201
|
end
|
|
31
|
-
|
|
32
|
-
# Shared market feed resource instance.
|
|
33
|
-
#
|
|
34
|
-
# @return [DhanHQ::Resources::MarketFeed]
|
|
35
|
-
def resource
|
|
36
|
-
@resource ||= DhanHQ::Resources::MarketFeed.new
|
|
37
|
-
end
|
|
38
202
|
end
|
|
39
203
|
end
|
|
40
204
|
end
|
|
@@ -4,22 +4,151 @@ require_relative "../contracts/option_chain_contract"
|
|
|
4
4
|
|
|
5
5
|
module DhanHQ
|
|
6
6
|
module Models
|
|
7
|
-
|
|
7
|
+
##
|
|
8
|
+
# Model for fetching option chain data for any option instrument across exchanges.
|
|
9
|
+
#
|
|
10
|
+
# The Option Chain API provides the entire option chain for any underlying instrument
|
|
11
|
+
# across NSE, BSE, and MCX exchanges. For each strike price, you get Open Interest (OI),
|
|
12
|
+
# Greeks (Delta, Theta, Gamma, Vega), Volume, Last Traded Price, Best Bid/Ask prices,
|
|
13
|
+
# Implied Volatility (IV), and other option analytics.
|
|
14
|
+
#
|
|
15
|
+
# @note **Rate Limits**: You can call the Option Chain API once every 3 seconds.
|
|
16
|
+
# This rate limit is enforced because OI data updates slowly compared to LTP or
|
|
17
|
+
# other data parameters. The client's internal rate limiter automatically throttles
|
|
18
|
+
# calls to prevent exceeding limits.
|
|
19
|
+
#
|
|
20
|
+
# @note **Data Filtering**: The model automatically filters out strikes where both
|
|
21
|
+
# Call (CE) and Put (PE) options have zero `last_price`, keeping the payload compact
|
|
22
|
+
# and focused on actively traded strikes.
|
|
23
|
+
#
|
|
24
|
+
# @example Fetch option chain for NIFTY index options
|
|
25
|
+
# chain = DhanHQ::Models::OptionChain.fetch(
|
|
26
|
+
# underlying_scrip: 13,
|
|
27
|
+
# underlying_seg: "IDX_I",
|
|
28
|
+
# expiry: "2024-10-31"
|
|
29
|
+
# )
|
|
30
|
+
# puts "Underlying LTP: ₹#{chain[:last_price]}"
|
|
31
|
+
# nifty_25000 = chain[:oc]["25000.000000"]
|
|
32
|
+
# puts "CE LTP: ₹#{nifty_25000['ce'][:last_price]}"
|
|
33
|
+
# puts "CE OI: #{nifty_25000['ce'][:oi]}"
|
|
34
|
+
#
|
|
35
|
+
# @example Fetch expiry list for an underlying
|
|
36
|
+
# expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(
|
|
37
|
+
# underlying_scrip: 13,
|
|
38
|
+
# underlying_seg: "IDX_I"
|
|
39
|
+
# )
|
|
40
|
+
# expiries.each { |expiry| puts expiry }
|
|
41
|
+
#
|
|
42
|
+
# @example Access Greeks for a strike
|
|
43
|
+
# chain = DhanHQ::Models::OptionChain.fetch(
|
|
44
|
+
# underlying_scrip: 1333,
|
|
45
|
+
# underlying_seg: "NSE_FNO",
|
|
46
|
+
# expiry: "2024-12-26"
|
|
47
|
+
# )
|
|
48
|
+
# strike_data = chain[:oc]["25000.000000"]
|
|
49
|
+
# ce_greeks = strike_data['ce'][:greeks]
|
|
50
|
+
# puts "Delta: #{ce_greeks[:delta]}"
|
|
51
|
+
# puts "Gamma: #{ce_greeks[:gamma]}"
|
|
52
|
+
# puts "Theta: #{ce_greeks[:theta]}"
|
|
53
|
+
# puts "Vega: #{ce_greeks[:vega]}"
|
|
54
|
+
#
|
|
8
55
|
class OptionChain < BaseModel
|
|
9
56
|
attr_reader :underlying_scrip, :underlying_seg, :expiry, :last_price, :option_data
|
|
10
57
|
|
|
11
58
|
class << self
|
|
12
|
-
|
|
59
|
+
##
|
|
60
|
+
# Provides a shared instance of the OptionChain resource.
|
|
13
61
|
#
|
|
14
|
-
# @return [DhanHQ::Resources::OptionChain]
|
|
62
|
+
# @return [DhanHQ::Resources::OptionChain] The OptionChain resource client instance
|
|
15
63
|
def resource
|
|
16
64
|
@resource ||= DhanHQ::Resources::OptionChain.new
|
|
17
65
|
end
|
|
18
66
|
|
|
19
|
-
|
|
67
|
+
##
|
|
68
|
+
# Fetches the entire option chain for a specified underlying instrument and expiry.
|
|
20
69
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
70
|
+
# Retrieves real-time option chain data across all strikes for the given underlying.
|
|
71
|
+
# The response includes Open Interest (OI), Greeks, Volume, Last Traded Price,
|
|
72
|
+
# Best Bid/Ask prices, Implied Volatility (IV), and other option analytics for
|
|
73
|
+
# both Call (CE) and Put (PE) options at each strike price.
|
|
74
|
+
#
|
|
75
|
+
# @param params [Hash{Symbol => Integer, String}] Request parameters for option chain
|
|
76
|
+
# @option params [Integer] :underlying_scrip (required) Security ID of the underlying
|
|
77
|
+
# instrument. Can be found via the Instruments API.
|
|
78
|
+
# @option params [String] :underlying_seg (required) Exchange and segment of underlying
|
|
79
|
+
# for which data is to be fetched.
|
|
80
|
+
# Valid values: "IDX_I" (Index), "NSE_FNO" (NSE F&O), "BSE_FNO" (BSE F&O), "MCX_FO" (MCX)
|
|
81
|
+
# @option params [String] :expiry (required) Expiry date of the option contract for
|
|
82
|
+
# which the option chain is requested. Must be in "YYYY-MM-DD" format.
|
|
83
|
+
# List of active expiries can be fetched using {fetch_expiry_list}.
|
|
84
|
+
#
|
|
85
|
+
# @return [HashWithIndifferentAccess] Filtered option chain data.
|
|
86
|
+
# Response structure:
|
|
87
|
+
# - **:last_price** [Float] Last Traded Price (LTP) of the underlying instrument
|
|
88
|
+
# - **:oc** [Hash{String => Hash}] Option chain data organized by strike price.
|
|
89
|
+
# Strike prices are stored as string keys (e.g., "25000.000000").
|
|
90
|
+
# Each strike contains:
|
|
91
|
+
# - **"ce"** [Hash{Symbol => Float, Integer, Hash}] Call Option data for this strike:
|
|
92
|
+
# - **:greeks** [Hash{Symbol => Float}] Option Greeks:
|
|
93
|
+
# - **:delta** [Float] Measures the change of option's premium based on
|
|
94
|
+
# every 1 rupee change in underlying
|
|
95
|
+
# - **:theta** [Float] Measures how quickly an option's value decreases over time
|
|
96
|
+
# - **:gamma** [Float] Rate of change in an option's delta in relation to the
|
|
97
|
+
# price of the underlying asset
|
|
98
|
+
# - **:vega** [Float] Measures the change of option's premium in response to
|
|
99
|
+
# a 1% change in implied volatility
|
|
100
|
+
# - **:implied_volatility** [Float] Value of expected volatility of a stock
|
|
101
|
+
# over the life of the option
|
|
102
|
+
# - **:last_price** [Float] Last Traded Price of the Call Option Instrument
|
|
103
|
+
# - **:oi** [Integer] Open Interest of the Call Option Instrument
|
|
104
|
+
# - **:previous_close_price** [Float] Previous day close price
|
|
105
|
+
# - **:previous_oi** [Integer] Previous day Open Interest
|
|
106
|
+
# - **:previous_volume** [Integer] Previous day volume
|
|
107
|
+
# - **:top_ask_price** [Float] Current best ask price available
|
|
108
|
+
# - **:top_ask_quantity** [Integer] Quantity available at current best ask price
|
|
109
|
+
# - **:top_bid_price** [Float] Current best bid price available
|
|
110
|
+
# - **:top_bid_quantity** [Integer] Quantity available at current best bid price
|
|
111
|
+
# - **:volume** [Integer] Day volume for Call Option Instrument
|
|
112
|
+
# - **"pe"** [Hash{Symbol => Float, Integer, Hash}] Put Option data for this strike.
|
|
113
|
+
# Contains the same fields as "ce" (Call Option data).
|
|
114
|
+
#
|
|
115
|
+
# @note Strikes where both CE and PE have zero `last_price` are automatically filtered out.
|
|
116
|
+
# This keeps the payload compact and focused on actively traded strikes.
|
|
117
|
+
#
|
|
118
|
+
# @example Fetch option chain for NIFTY index options
|
|
119
|
+
# chain = DhanHQ::Models::OptionChain.fetch(
|
|
120
|
+
# underlying_scrip: 13,
|
|
121
|
+
# underlying_seg: "IDX_I",
|
|
122
|
+
# expiry: "2024-10-31"
|
|
123
|
+
# )
|
|
124
|
+
# puts "NIFTY LTP: ₹#{chain[:last_price]}"
|
|
125
|
+
#
|
|
126
|
+
# @example Access Call and Put data for a specific strike
|
|
127
|
+
# chain = DhanHQ::Models::OptionChain.fetch(
|
|
128
|
+
# underlying_scrip: 13,
|
|
129
|
+
# underlying_seg: "IDX_I",
|
|
130
|
+
# expiry: "2024-10-31"
|
|
131
|
+
# )
|
|
132
|
+
# strike_25000 = chain[:oc]["25000.000000"]
|
|
133
|
+
# ce_data = strike_25000["ce"]
|
|
134
|
+
# pe_data = strike_25000["pe"]
|
|
135
|
+
# puts "CE LTP: ₹#{ce_data[:last_price]}, OI: #{ce_data[:oi]}"
|
|
136
|
+
# puts "PE LTP: ₹#{pe_data[:last_price]}, OI: #{pe_data[:oi]}"
|
|
137
|
+
#
|
|
138
|
+
# @example Calculate OI change and analyze Greeks
|
|
139
|
+
# chain = DhanHQ::Models::OptionChain.fetch(
|
|
140
|
+
# underlying_scrip: 1333,
|
|
141
|
+
# underlying_seg: "NSE_FNO",
|
|
142
|
+
# expiry: "2024-12-26"
|
|
143
|
+
# )
|
|
144
|
+
# strike_data = chain[:oc]["25000.000000"]
|
|
145
|
+
# ce = strike_data["ce"]
|
|
146
|
+
# oi_change = ce[:oi] - ce[:previous_oi]
|
|
147
|
+
# puts "OI Change: #{oi_change}"
|
|
148
|
+
# puts "Delta: #{ce[:greeks][:delta]}"
|
|
149
|
+
# puts "IV: #{ce[:implied_volatility]}%"
|
|
150
|
+
#
|
|
151
|
+
# @raise [DhanHQ::ValidationError] If validation fails for any parameter or date format
|
|
23
152
|
def fetch(params)
|
|
24
153
|
validate_params!(params, DhanHQ::Contracts::OptionChainContract)
|
|
25
154
|
|
|
@@ -29,10 +158,44 @@ module DhanHQ
|
|
|
29
158
|
filter_valid_strikes(response[:data]).with_indifferent_access
|
|
30
159
|
end
|
|
31
160
|
|
|
32
|
-
|
|
161
|
+
##
|
|
162
|
+
# Fetches the list of active expiry dates for an underlying instrument.
|
|
33
163
|
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
164
|
+
# Retrieves all expiry dates for which option instruments are active for the given
|
|
165
|
+
# underlying. This list is useful for selecting valid expiry dates when fetching
|
|
166
|
+
# option chains.
|
|
167
|
+
#
|
|
168
|
+
# @param params [Hash{Symbol => Integer, String}] Request parameters for expiry list
|
|
169
|
+
# @option params [Integer] :underlying_scrip (required) Security ID of the underlying
|
|
170
|
+
# instrument. Can be found via the Instruments API.
|
|
171
|
+
# @option params [String] :underlying_seg (required) Exchange and segment of underlying
|
|
172
|
+
# for which expiry list is to be fetched.
|
|
173
|
+
# Valid values: "IDX_I" (Index), "NSE_FNO" (NSE F&O), "BSE_FNO" (BSE F&O), "MCX_FO" (MCX)
|
|
174
|
+
#
|
|
175
|
+
# @return [Array<String>] Array of expiry dates in "YYYY-MM-DD" format.
|
|
176
|
+
# Returns empty array if the API response status is not "success" or if no expiries are found.
|
|
177
|
+
#
|
|
178
|
+
# @example Fetch expiry list for NIFTY index
|
|
179
|
+
# expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(
|
|
180
|
+
# underlying_scrip: 13,
|
|
181
|
+
# underlying_seg: "IDX_I"
|
|
182
|
+
# )
|
|
183
|
+
# puts "Available expiries:"
|
|
184
|
+
# expiries.each { |expiry| puts " #{expiry}" }
|
|
185
|
+
#
|
|
186
|
+
# @example Use expiry list to fetch option chains
|
|
187
|
+
# expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(
|
|
188
|
+
# underlying_scrip: 1333,
|
|
189
|
+
# underlying_seg: "NSE_FNO"
|
|
190
|
+
# )
|
|
191
|
+
# nearest_expiry = expiries.first
|
|
192
|
+
# chain = DhanHQ::Models::OptionChain.fetch(
|
|
193
|
+
# underlying_scrip: 1333,
|
|
194
|
+
# underlying_seg: "NSE_FNO",
|
|
195
|
+
# expiry: nearest_expiry
|
|
196
|
+
# )
|
|
197
|
+
#
|
|
198
|
+
# @raise [DhanHQ::ValidationError] If validation fails for any parameter
|
|
36
199
|
def fetch_expiry_list(params)
|
|
37
200
|
validate_params!(params, DhanHQ::Contracts::OptionChainExpiryListContract)
|
|
38
201
|
|
|
@@ -42,10 +205,17 @@ module DhanHQ
|
|
|
42
205
|
|
|
43
206
|
private
|
|
44
207
|
|
|
45
|
-
|
|
208
|
+
##
|
|
209
|
+
# Filters valid strikes where at least one of CE or PE has a non-zero last_price.
|
|
210
|
+
#
|
|
211
|
+
# Removes strikes from the option chain where both Call (CE) and Put (PE) options
|
|
212
|
+
# have zero `last_price`, keeping only actively traded strikes. This keeps the
|
|
213
|
+
# payload compact and focused on relevant data.
|
|
214
|
+
#
|
|
215
|
+
# @param data [Hash] The API response data containing option chain information
|
|
216
|
+
# @return [Hash] The filtered option chain data with original strike price keys preserved
|
|
46
217
|
#
|
|
47
|
-
# @
|
|
48
|
-
# @return [Hash] The filtered option chain data with original strike price keys
|
|
218
|
+
# @api private
|
|
49
219
|
def filter_valid_strikes(data)
|
|
50
220
|
return {} unless data.is_a?(Hash) && data.key?(:oc)
|
|
51
221
|
|
|
@@ -63,6 +233,7 @@ module DhanHQ
|
|
|
63
233
|
# Validation contract for option chain
|
|
64
234
|
#
|
|
65
235
|
# @return [DhanHQ::Contracts::OptionChainContract]
|
|
236
|
+
# @api private
|
|
66
237
|
def validation_contract
|
|
67
238
|
DhanHQ::Contracts::OptionChainContract.new
|
|
68
239
|
end
|
|
@@ -73,6 +244,7 @@ module DhanHQ
|
|
|
73
244
|
# Validation contract for option chain
|
|
74
245
|
#
|
|
75
246
|
# @return [DhanHQ::Contracts::OptionChainContract]
|
|
247
|
+
# @api private
|
|
76
248
|
def validation_contract
|
|
77
249
|
DhanHQ::Contracts::OptionChainContract.new
|
|
78
250
|
end
|