DhanHQ 2.1.5 → 2.1.6
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 +7 -0
- data/GUIDE.md +215 -73
- data/README.md +416 -132
- data/README1.md +267 -26
- data/docs/live_order_updates.md +319 -0
- data/docs/rails_websocket_integration.md +847 -0
- data/docs/standalone_ruby_websocket_integration.md +1588 -0
- data/docs/websocket_integration.md +871 -0
- data/examples/comprehensive_websocket_examples.rb +148 -0
- data/examples/instrument_finder_test.rb +195 -0
- data/examples/live_order_updates.rb +118 -0
- data/examples/market_depth_example.rb +144 -0
- data/examples/market_feed_example.rb +81 -0
- data/examples/order_update_example.rb +105 -0
- data/examples/trading_fields_example.rb +215 -0
- data/lib/DhanHQ/configuration.rb +16 -1
- data/lib/DhanHQ/contracts/expired_options_data_contract.rb +103 -0
- data/lib/DhanHQ/contracts/trade_contract.rb +70 -0
- data/lib/DhanHQ/errors.rb +2 -0
- data/lib/DhanHQ/models/expired_options_data.rb +331 -0
- data/lib/DhanHQ/models/instrument.rb +96 -2
- data/lib/DhanHQ/models/order_update.rb +235 -0
- data/lib/DhanHQ/models/trade.rb +118 -31
- data/lib/DhanHQ/resources/expired_options_data.rb +22 -0
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/base_connection.rb +249 -0
- data/lib/DhanHQ/ws/connection.rb +2 -2
- data/lib/DhanHQ/ws/decoder.rb +3 -3
- data/lib/DhanHQ/ws/market_depth/client.rb +376 -0
- data/lib/DhanHQ/ws/market_depth/decoder.rb +131 -0
- data/lib/DhanHQ/ws/market_depth.rb +74 -0
- data/lib/DhanHQ/ws/orders/client.rb +175 -11
- data/lib/DhanHQ/ws/orders/connection.rb +40 -81
- data/lib/DhanHQ/ws/orders.rb +28 -0
- data/lib/DhanHQ/ws/segments.rb +18 -2
- data/lib/DhanHQ/ws.rb +3 -2
- data/lib/dhan_hq.rb +5 -0
- metadata +35 -1
data/lib/DhanHQ/models/trade.rb
CHANGED
@@ -4,12 +4,14 @@ module DhanHQ
|
|
4
4
|
module Models
|
5
5
|
##
|
6
6
|
# Represents a single trade.
|
7
|
-
#
|
7
|
+
# Supports three main API endpoints:
|
8
|
+
# 1. GET /v2/trades - Current day trades
|
9
|
+
# 2. GET /v2/trades/{order-id} - Trades for specific order
|
10
|
+
# 3. GET /v2/trades/{from-date}/{to-date}/{page} - Historical trades
|
8
11
|
class Trade < BaseModel
|
9
|
-
# No explicit HTTP_PATH if we rely on the statements resource
|
10
|
-
# but we can define it if needed
|
11
12
|
HTTP_PATH = "/v2/trades"
|
12
13
|
|
14
|
+
# All trade attributes as per API documentation
|
13
15
|
attributes :dhan_client_id, :order_id, :exchange_order_id, :exchange_trade_id,
|
14
16
|
:transaction_type, :exchange_segment, :product_type, :order_type,
|
15
17
|
:trading_symbol, :custom_symbol, :security_id, :traded_quantity,
|
@@ -19,14 +21,6 @@ module DhanHQ
|
|
19
21
|
:drv_option_type, :drv_strike_price
|
20
22
|
|
21
23
|
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
24
|
##
|
31
25
|
# Resource for current day tradebook APIs
|
32
26
|
def tradebook_resource
|
@@ -34,46 +28,139 @@ module DhanHQ
|
|
34
28
|
end
|
35
29
|
|
36
30
|
##
|
37
|
-
#
|
38
|
-
|
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) }
|
31
|
+
# Resource for historical trade data
|
32
|
+
def statements_resource
|
33
|
+
@statements_resource ||= DhanHQ::Resources::Statements.new
|
51
34
|
end
|
52
35
|
|
53
|
-
|
54
|
-
|
55
|
-
#
|
36
|
+
##
|
37
|
+
# Fetch current day trades
|
38
|
+
# GET /v2/trades
|
39
|
+
#
|
40
|
+
# @return [Array<Trade>] Array of trades executed today
|
56
41
|
def today
|
57
42
|
response = tradebook_resource.all
|
58
43
|
return [] unless response.is_a?(Array)
|
59
44
|
|
60
|
-
response.map { |
|
45
|
+
response.map { |trade_data| new(trade_data, skip_validation: true) }
|
61
46
|
end
|
62
47
|
|
63
|
-
|
48
|
+
##
|
49
|
+
# Fetch trades for a specific order ID (current day)
|
50
|
+
# GET /v2/trades/{order-id}
|
51
|
+
#
|
52
|
+
# @param order_id [String] The order ID to fetch trades for
|
53
|
+
# @return [Trade, nil] Trade object or nil if not found
|
64
54
|
def find_by_order_id(order_id)
|
55
|
+
# Validate input
|
56
|
+
contract = DhanHQ::Contracts::TradeByOrderIdContract.new
|
57
|
+
validation_result = contract.call(order_id: order_id)
|
58
|
+
|
59
|
+
unless validation_result.success?
|
60
|
+
raise DhanHQ::ValidationError, "Invalid order_id: #{validation_result.errors.to_h}"
|
61
|
+
end
|
62
|
+
|
65
63
|
response = tradebook_resource.find(order_id)
|
66
64
|
return nil unless response.is_a?(Hash) || (response.is_a?(Array) && response.any?)
|
67
65
|
|
68
66
|
data = response.is_a?(Array) ? response.first : response
|
69
67
|
new(data, skip_validation: true)
|
70
68
|
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Fetch historical trades within the given date range and page
|
72
|
+
# GET /v2/trades/{from-date}/{to-date}/{page}
|
73
|
+
#
|
74
|
+
# @param from_date [String] Start date in YYYY-MM-DD format
|
75
|
+
# @param to_date [String] End date in YYYY-MM-DD format
|
76
|
+
# @param page [Integer] Page number (default: 0)
|
77
|
+
# @return [Array<Trade>] Array of historical trades
|
78
|
+
def history(from_date:, to_date:, page: 0)
|
79
|
+
validate_history_params(from_date, to_date, page)
|
80
|
+
|
81
|
+
response = statements_resource.trade_history(
|
82
|
+
from_date: from_date,
|
83
|
+
to_date: to_date,
|
84
|
+
page: page
|
85
|
+
)
|
86
|
+
|
87
|
+
return [] unless response.is_a?(Array)
|
88
|
+
|
89
|
+
response.map { |trade_data| new(trade_data, skip_validation: true) }
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def validate_history_params(from_date, to_date, page)
|
95
|
+
contract = DhanHQ::Contracts::TradeHistoryContract.new
|
96
|
+
validation_result = contract.call(from_date: from_date, to_date: to_date, page: page)
|
97
|
+
|
98
|
+
return if validation_result.success?
|
99
|
+
|
100
|
+
raise DhanHQ::ValidationError, "Invalid parameters: #{validation_result.errors.to_h}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# Alias for backward compatibility
|
104
|
+
alias all history
|
71
105
|
end
|
72
106
|
|
73
|
-
|
107
|
+
##
|
108
|
+
# Trade objects are read-only, so no validation contract needed
|
74
109
|
def validation_contract
|
75
110
|
nil
|
76
111
|
end
|
112
|
+
|
113
|
+
##
|
114
|
+
# Helper methods for trade data
|
115
|
+
def buy?
|
116
|
+
transaction_type == "BUY"
|
117
|
+
end
|
118
|
+
|
119
|
+
def sell?
|
120
|
+
transaction_type == "SELL"
|
121
|
+
end
|
122
|
+
|
123
|
+
def equity?
|
124
|
+
instrument == "EQUITY"
|
125
|
+
end
|
126
|
+
|
127
|
+
def derivative?
|
128
|
+
instrument == "DERIVATIVES"
|
129
|
+
end
|
130
|
+
|
131
|
+
def option?
|
132
|
+
%w[CALL PUT].include?(drv_option_type)
|
133
|
+
end
|
134
|
+
|
135
|
+
def call_option?
|
136
|
+
drv_option_type == "CALL"
|
137
|
+
end
|
138
|
+
|
139
|
+
def put_option?
|
140
|
+
drv_option_type == "PUT"
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Calculate total trade value
|
145
|
+
def total_value
|
146
|
+
return 0 unless traded_quantity && traded_price
|
147
|
+
|
148
|
+
traded_quantity * traded_price
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Calculate total charges
|
153
|
+
def total_charges
|
154
|
+
charges = [sebi_tax, stt, brokerage_charges, service_tax,
|
155
|
+
exchange_transaction_charges, stamp_duty].compact
|
156
|
+
charges.sum(&:to_f)
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Net trade value after charges
|
161
|
+
def net_value
|
162
|
+
total_value - total_charges
|
163
|
+
end
|
77
164
|
end
|
78
165
|
end
|
79
166
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DhanHQ
|
4
|
+
module Resources
|
5
|
+
##
|
6
|
+
# Resource for expired options data API endpoints
|
7
|
+
class ExpiredOptionsData < BaseAPI
|
8
|
+
API_TYPE = :data_api
|
9
|
+
HTTP_PATH = "/charts"
|
10
|
+
|
11
|
+
##
|
12
|
+
# Fetch expired options data for rolling contracts
|
13
|
+
# POST /charts/rollingoption
|
14
|
+
#
|
15
|
+
# @param params [Hash] Parameters for the request
|
16
|
+
# @return [Hash] API response with expired options data
|
17
|
+
def fetch(params)
|
18
|
+
post("/rollingoption", params: params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/DhanHQ/version.rb
CHANGED
@@ -0,0 +1,249 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "eventmachine"
|
4
|
+
require "faye/websocket"
|
5
|
+
require "json"
|
6
|
+
require "concurrent"
|
7
|
+
require "uri"
|
8
|
+
|
9
|
+
module DhanHQ
|
10
|
+
module WS
|
11
|
+
##
|
12
|
+
# Base WebSocket connection class providing common functionality
|
13
|
+
# for all DhanHQ WebSocket connections (Orders, Market Feed, Market Depth)
|
14
|
+
class BaseConnection
|
15
|
+
COOL_OFF_429 = 60 # seconds to cool off on 429
|
16
|
+
MAX_BACKOFF = 90 # cap exponential backoff
|
17
|
+
|
18
|
+
attr_reader :stopping, :url, :callbacks, :started
|
19
|
+
|
20
|
+
##
|
21
|
+
# Initialize base connection
|
22
|
+
# @param url [String] WebSocket endpoint URL
|
23
|
+
# @param options [Hash] Connection options
|
24
|
+
# @option options [Integer] :max_backoff Maximum backoff time (default: 90)
|
25
|
+
# @option options [Integer] :cool_off_429 Cool off time for 429 errors (default: 60)
|
26
|
+
def initialize(url:, **options)
|
27
|
+
@url = url
|
28
|
+
@callbacks = Concurrent::Map.new { |h, k| h[k] = [] }
|
29
|
+
@started = Concurrent::AtomicBoolean.new(false)
|
30
|
+
@stop = false
|
31
|
+
@stopping = false
|
32
|
+
@ws = nil
|
33
|
+
@timer = nil
|
34
|
+
@cooloff_until = nil
|
35
|
+
@thr = nil
|
36
|
+
@max_backoff = options[:max_backoff] || MAX_BACKOFF
|
37
|
+
@cool_off_429 = options[:cool_off_429] || COOL_OFF_429
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Start the WebSocket connection
|
42
|
+
# @return [BaseConnection] self for method chaining
|
43
|
+
def start
|
44
|
+
return self if @started.true?
|
45
|
+
|
46
|
+
@started.make_true
|
47
|
+
@thr = Thread.new { loop_run }
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Stop the WebSocket connection gracefully
|
53
|
+
# @return [BaseConnection] self for method chaining
|
54
|
+
def stop
|
55
|
+
return self unless @started.true?
|
56
|
+
|
57
|
+
@started.make_false
|
58
|
+
@stop = true
|
59
|
+
@stopping = true
|
60
|
+
@ws&.close
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Force disconnect the WebSocket
|
66
|
+
# @return [BaseConnection] self for method chaining
|
67
|
+
def disconnect!
|
68
|
+
@stop = true
|
69
|
+
@stopping = true
|
70
|
+
@ws&.close
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# Check if connection is open
|
76
|
+
# @return [Boolean] true if connected
|
77
|
+
def open?
|
78
|
+
@ws && @ws.instance_variable_get(:@driver)&.ready_state == 1
|
79
|
+
rescue StandardError
|
80
|
+
false
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Check if connection is connected (alias for open?)
|
85
|
+
# @return [Boolean] true if connected
|
86
|
+
def connected?
|
87
|
+
open?
|
88
|
+
end
|
89
|
+
|
90
|
+
##
|
91
|
+
# Register event handler
|
92
|
+
# @param event [Symbol] Event type
|
93
|
+
# @param block [Proc] Event handler
|
94
|
+
# @return [BaseConnection] self for method chaining
|
95
|
+
def on(event, &block)
|
96
|
+
@callbacks[event] << block
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# Emit event to registered callbacks
|
102
|
+
# @param event [Symbol] Event type
|
103
|
+
# @param payload [Object] Event payload
|
104
|
+
def emit(event, payload = nil)
|
105
|
+
list = @callbacks[event] || []
|
106
|
+
list.each { |cb| cb.call(payload) }
|
107
|
+
rescue StandardError => e
|
108
|
+
DhanHQ.logger&.error("[DhanHQ::WS::BaseConnection] Error in event handler: #{e.class} #{e.message}")
|
109
|
+
end
|
110
|
+
|
111
|
+
##
|
112
|
+
# Send message over WebSocket
|
113
|
+
# @param message [String, Hash] Message to send
|
114
|
+
def send_message(message)
|
115
|
+
return unless @ws && open?
|
116
|
+
|
117
|
+
data = message.is_a?(Hash) ? message.to_json : message
|
118
|
+
@ws.send(data)
|
119
|
+
rescue StandardError => e
|
120
|
+
DhanHQ.logger&.error("[DhanHQ::WS::BaseConnection] Error sending message: #{e.class} #{e.message}")
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
##
|
126
|
+
# Main connection loop with reconnection logic
|
127
|
+
def loop_run
|
128
|
+
backoff = 2.0
|
129
|
+
until @stop
|
130
|
+
failed = false
|
131
|
+
got_429 = false
|
132
|
+
|
133
|
+
# Respect any active cool-off window
|
134
|
+
sleep (@cooloff_until - Time.now).ceil if @cooloff_until && Time.now < @cooloff_until
|
135
|
+
|
136
|
+
begin
|
137
|
+
failed, got_429 = run_session
|
138
|
+
rescue StandardError => e
|
139
|
+
DhanHQ.logger&.error("[DhanHQ::WS::BaseConnection] Connection crashed: #{e.class} #{e.message}")
|
140
|
+
failed = true
|
141
|
+
ensure
|
142
|
+
break if @stop
|
143
|
+
|
144
|
+
if got_429
|
145
|
+
@cooloff_until = Time.now + @cool_off_429
|
146
|
+
DhanHQ.logger&.warn("[DhanHQ::WS::BaseConnection] Cooling off #{@cool_off_429}s due to 429")
|
147
|
+
end
|
148
|
+
|
149
|
+
if failed
|
150
|
+
sleep_time = [backoff, @max_backoff].min
|
151
|
+
jitter = rand(0.2 * sleep_time)
|
152
|
+
DhanHQ.logger&.warn("[DhanHQ::WS::BaseConnection] Reconnecting in #{(sleep_time + jitter).round(1)}s")
|
153
|
+
sleep(sleep_time + jitter)
|
154
|
+
backoff *= 2.0
|
155
|
+
else
|
156
|
+
backoff = 2.0
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
##
|
163
|
+
# Run a single WebSocket session
|
164
|
+
# Must be implemented by subclasses
|
165
|
+
# @return [Array<Boolean>] [failed, got_429]
|
166
|
+
def run_session
|
167
|
+
raise NotImplementedError, "Subclasses must implement run_session"
|
168
|
+
end
|
169
|
+
|
170
|
+
##
|
171
|
+
# Get default headers for WebSocket connection
|
172
|
+
# @return [Hash] Default headers
|
173
|
+
def default_headers
|
174
|
+
{
|
175
|
+
"User-Agent" => "DhanHQ-Ruby-Client/#{DhanHQ::VERSION}",
|
176
|
+
"Origin" => "https://dhanhq.co"
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Sanitize URL for logging by removing sensitive parameters
|
182
|
+
# @param url [String] Original URL
|
183
|
+
# @return [String] Sanitized URL safe for logging
|
184
|
+
def sanitize_url(url)
|
185
|
+
return url if url.nil? || url.empty?
|
186
|
+
|
187
|
+
begin
|
188
|
+
uri = URI.parse(url)
|
189
|
+
# Remove sensitive query parameters
|
190
|
+
if uri.query
|
191
|
+
params = URI.decode_www_form(uri.query).reject do |key, _|
|
192
|
+
%w[token clientId client_id access_token].include?(key.downcase)
|
193
|
+
end
|
194
|
+
uri.query = URI.encode_www_form(params) unless params.empty?
|
195
|
+
end
|
196
|
+
uri.to_s
|
197
|
+
rescue StandardError
|
198
|
+
# If URL parsing fails, return a generic message
|
199
|
+
"wss://[sanitized-url]"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
##
|
204
|
+
# Handle WebSocket open event
|
205
|
+
def handle_open
|
206
|
+
DhanHQ.logger&.info("[DhanHQ::WS::BaseConnection] Connected to #{sanitize_url(@url)}")
|
207
|
+
emit(:open)
|
208
|
+
authenticate if respond_to?(:authenticate, true)
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Handle WebSocket message event
|
213
|
+
# @param ev [Event] WebSocket message event
|
214
|
+
def handle_message(ev)
|
215
|
+
emit(:raw, ev.data)
|
216
|
+
process_message(ev.data) if respond_to?(:process_message, true)
|
217
|
+
end
|
218
|
+
|
219
|
+
##
|
220
|
+
# Handle WebSocket close event
|
221
|
+
# @param ev [Event] WebSocket close event
|
222
|
+
def handle_close(ev)
|
223
|
+
@timer&.cancel
|
224
|
+
@timer = nil
|
225
|
+
msg = "[DhanHQ::WS::BaseConnection] Connection closed: #{ev.code} #{ev.reason}"
|
226
|
+
DhanHQ.logger&.warn(msg)
|
227
|
+
|
228
|
+
emit(:close, { code: ev.code, reason: ev.reason })
|
229
|
+
|
230
|
+
if @stopping
|
231
|
+
[false, false]
|
232
|
+
else
|
233
|
+
failed = (ev.code != 1000)
|
234
|
+
got_429 = ev.reason.to_s.include?("429")
|
235
|
+
[failed, got_429]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# Handle WebSocket error event
|
241
|
+
# @param ev [Event] WebSocket error event
|
242
|
+
def handle_error(ev)
|
243
|
+
DhanHQ.logger&.error("[DhanHQ::WS::BaseConnection] WebSocket error: #{ev.message}")
|
244
|
+
emit(:error, ev.message)
|
245
|
+
[true, false]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
data/lib/DhanHQ/ws/connection.rb
CHANGED
@@ -9,9 +9,9 @@ module DhanHQ
|
|
9
9
|
# Low-level wrapper responsible for establishing and maintaining the raw
|
10
10
|
# WebSocket connection to the streaming API.
|
11
11
|
class Connection
|
12
|
-
SUB_CODES = { ticker: 15, quote:
|
12
|
+
SUB_CODES = { ticker: 15, quote: 15, full: 15 }.freeze # All use RequestCode 15 per official API
|
13
13
|
# Request codes used when unsubscribing from feeds.
|
14
|
-
UNSUB_CODES = { ticker:
|
14
|
+
UNSUB_CODES = { ticker: 12, quote: 12, full: 12 }.freeze # Use disconnect code 12 for unsubscribe
|
15
15
|
|
16
16
|
COOL_OFF_429 = 60 # seconds to cool off on 429
|
17
17
|
MAX_BACKOFF = 90 # cap exponential backoff
|
data/lib/DhanHQ/ws/decoder.rb
CHANGED
@@ -31,19 +31,19 @@ module DhanHQ
|
|
31
31
|
when :ticker
|
32
32
|
{
|
33
33
|
kind: :ticker, segment: segstr, security_id: sid,
|
34
|
-
ltp: pkt[:ltp].to_f, ts: pkt[:ltt]
|
34
|
+
ltp: pkt[:ltp].to_f, ts: pkt[:ltt]&.to_i
|
35
35
|
}
|
36
36
|
when :quote
|
37
37
|
{
|
38
38
|
kind: :quote, segment: segstr, security_id: sid,
|
39
|
-
ltp: pkt[:ltp].to_f, ts: pkt[:ltt]
|
39
|
+
ltp: pkt[:ltp].to_f, ts: pkt[:ltt]&.to_i, atp: pkt[:atp].to_f,
|
40
40
|
vol: pkt[:volume].to_i, ts_buy_qty: pkt[:total_buy_qty].to_i, ts_sell_qty: pkt[:total_sell_qty].to_i,
|
41
41
|
day_open: pkt[:day_open]&.to_f, day_high: pkt[:day_high]&.to_f, day_low: pkt[:day_low]&.to_f, day_close: pkt[:day_close]&.to_f
|
42
42
|
}
|
43
43
|
when :full
|
44
44
|
out = {
|
45
45
|
kind: :full, segment: segstr, security_id: sid,
|
46
|
-
ltp: pkt[:ltp].to_f, ts: pkt[:ltt]
|
46
|
+
ltp: pkt[:ltp].to_f, ts: pkt[:ltt]&.to_i, atp: pkt[:atp].to_f,
|
47
47
|
vol: pkt[:volume].to_i, ts_buy_qty: pkt[:total_buy_qty].to_i, ts_sell_qty: pkt[:total_sell_qty].to_i,
|
48
48
|
oi: pkt[:open_interest]&.to_i, oi_high: pkt[:highest_open_interest]&.to_i, oi_low: pkt[:lowest_open_interest]&.to_i,
|
49
49
|
day_open: pkt[:day_open]&.to_f, day_high: pkt[:day_high]&.to_f, day_low: pkt[:day_low]&.to_f, day_close: pkt[:day_close]&.to_f
|