Class: DhanHQ::Models::ExpiredOptionsData

Inherits:
BaseModel
  • Object
show all
Defined in:
lib/DhanHQ/models/expired_options_data.rb

Overview

Model for fetching expired options contract data on a rolling basis.

This API provides pre-processed expired options data for up to the last 5 years. Data is available on a minute-level basis, organized by strike price relative to spot (e.g., ATM, ATM+1, ATM-1, etc.). You can fetch up to 31 days of data in a single API call.

Available data includes:

  • OHLC (Open, High, Low, Close) prices
  • Volume and Open Interest
  • Implied Volatility (IV)
  • Strike prices
  • Spot prices
  • Timestamps

Strike ranges:

  • Index Options (near expiry): Up to ATM+10 / ATM-10
  • All other contracts: Up to ATM+3 / ATM-3

Examples:

Fetch expired options data for NIFTY

data = DhanHQ::Models::ExpiredOptionsData.fetch(
  exchange_segment: "NSE_FNO",
  interval: "1",
  security_id: 13,
  instrument: "OPTIDX",
  expiry_flag: "MONTH",
  expiry_code: 1,
  strike: "ATM",
  drv_option_type: "CALL",
  required_data: ["open", "high", "low", "close", "volume"],
  from_date: "2021-08-01",
  to_date: "2021-09-01"
)
ohlc = data.ohlc_data
volumes = data.volume_data

Access call option data

call_data = data.call_data
put_data = data.put_data

Normalize to candles

candles = data.to_candles

Constant Summary collapse

OHLC_FIELDS =
i[open high low close iv volume strike spot oi open_interest].freeze

Constants included from ResponseHelper

ResponseHelper::STATUS_ERROR_FALLBACK

Instance Attribute Summary

Attributes inherited from BaseModel

#attributes, #errors

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseModel

all, api, api_type, #assign_attributes, attributes, create, #delete, #destroy, find, #id, #initialize, #new_record?, #optionchain_api?, parse_collection_response, #persisted?, resource, resource_path, #save, #save!, #to_request_params, #update, #valid?, validate_attributes, #validation_contract, validation_contract, where

Methods included from APIHelper

#handle_response

Methods included from AttributeHelper

#camelize_keys, #deep_camelize_keys, #inspect, #normalize_keys, #snake_case, #titleize_keys

Methods included from ValidationHelper

#valid?, #validate!, #validate_params!

Methods included from RequestHelper

#build_from_response

Constructor Details

This class inherits a constructor from DhanHQ::BaseModel

Class Method Details

.fetch(params) ⇒ ExpiredOptionsData Also known as: rolling

Fetches expired options data for rolling contracts on a minute-level basis.

Data is organized by strike price relative to spot and can be fetched for up to 31 days in a single request. Historical data is available for up to the last 5 years.

Raises:



83
84
85
86
87
88
89
90
91
92
# File 'lib/DhanHQ/models/expired_options_data.rb', line 83

def fetch(params)
  # Map option_type to drv_option_type if provided
  params[:drv_option_type] ||= params[:option_type] if params.key?(:option_type)

  normalized = normalize_params(params)
  validate_params(normalized)

  response = expired_options_resource.fetch(normalized)
  new(response.merge(normalized), skip_validation: true)
end

Instance Method Details

#at_the_money?Boolean

Checks if the strike is at the money (ATM).



520
521
522
# File 'lib/DhanHQ/models/expired_options_data.rb', line 520

def at_the_money?
  strike == "ATM"
end

#average_implied_volatility(option_type = nil) ⇒ Float

Calculates the average implied volatility for the specified option type.



409
410
411
412
413
414
# File 'lib/DhanHQ/models/expired_options_data.rb', line 409

def average_implied_volatility(option_type = nil)
  iv_data = implied_volatility_data(option_type)
  return 0.0 if iv_data.empty?

  iv_data.sum.to_f / iv_data.size
end

#average_open_interest(option_type = nil) ⇒ Float

Calculates the average open interest for the specified option type.



396
397
398
399
400
401
# File 'lib/DhanHQ/models/expired_options_data.rb', line 396

def average_open_interest(option_type = nil)
  oi_data = open_interest_data(option_type)
  return 0.0 if oi_data.empty?

  oi_data.sum.to_f / oi_data.size
end

#average_volume(option_type = nil) ⇒ Float

Calculates the average volume for the specified option type.



383
384
385
386
387
388
# File 'lib/DhanHQ/models/expired_options_data.rb', line 383

def average_volume(option_type = nil)
  volumes = volume_data(option_type)
  return 0.0 if volumes.empty?

  volumes.sum.to_f / volumes.size
end

#call_dataHash{Symbol => Array<Float, Integer>}?

Gets call option data from the response.



209
210
211
212
213
# File 'lib/DhanHQ/models/expired_options_data.rb', line 209

def call_data
  return nil unless data.is_a?(Hash)

  data["ce"] || data[:ce]
end

#call_option?Boolean

Checks if this is call option data.



504
505
506
# File 'lib/DhanHQ/models/expired_options_data.rb', line 504

def call_option?
  drv_option_type == DhanHQ::Constants::OptionType::CALL
end

#data_for_type(option_type) ⇒ Hash{Symbol => Array<Float, Integer>}?

Gets data for the specified option type.



243
244
245
246
247
248
249
250
# File 'lib/DhanHQ/models/expired_options_data.rb', line 243

def data_for_type(option_type)
  case option_type.upcase
  when DhanHQ::Constants::OptionType::CALL
    call_data
  when DhanHQ::Constants::OptionType::PUT
    put_data
  end
end

#data_points_count(option_type = nil) ⇒ Integer

Gets the number of data points available for the specified option type.



372
373
374
375
# File 'lib/DhanHQ/models/expired_options_data.rb', line 372

def data_points_count(option_type = nil)
  timestamps = timestamp_data(option_type)
  timestamps.size
end

#implied_volatility_data(option_type = nil) ⇒ Array<Float>

Gets implied volatility (IV) data for the specified option type.



313
314
315
316
317
318
319
# File 'lib/DhanHQ/models/expired_options_data.rb', line 313

def implied_volatility_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return [] unless option_data

  option_data["iv"] || option_data[:iv] || []
end

#index_options?Boolean

Checks if this is index options data.



472
473
474
# File 'lib/DhanHQ/models/expired_options_data.rb', line 472

def index_options?
  instrument == DhanHQ::Constants::InstrumentType::OPTIDX
end

#monthly_expiry?Boolean

Checks if this is monthly expiry data.



496
497
498
# File 'lib/DhanHQ/models/expired_options_data.rb', line 496

def monthly_expiry?
  expiry_flag == "MONTH"
end

#ohlc_data(option_type = nil) ⇒ Hash{Symbol => Array<Float>}, Hash{Symbol => Array}

Gets OHLC (Open, High, Low, Close) data for the specified option type.



263
264
265
266
267
268
269
270
271
272
273
274
# File 'lib/DhanHQ/models/expired_options_data.rb', line 263

def ohlc_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return {} unless option_data

  {
    open: option_data["open"] || option_data[:open] || [],
    high: option_data["high"] || option_data[:high] || [],
    low: option_data["low"] || option_data[:low] || [],
    close: option_data["close"] || option_data[:close] || []
  }
end

#open_interest_data(option_type = nil) ⇒ Array<Float>

Gets open interest (OI) data for the specified option type.



298
299
300
301
302
303
304
# File 'lib/DhanHQ/models/expired_options_data.rb', line 298

def open_interest_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return [] unless option_data

  option_data["oi"] || option_data[:oi] || []
end

#price_ranges(option_type = nil) ⇒ Array<Float>

Calculates price range (high - low) for each timeframe of the specified option type.



423
424
425
426
427
428
429
430
431
# File 'lib/DhanHQ/models/expired_options_data.rb', line 423

def price_ranges(option_type = nil)
  ohlc = ohlc_data(option_type)
  highs = ohlc[:high]
  lows = ohlc[:low]

  return [] if highs.empty? || lows.empty?

  highs.zip(lows).map { |high, low| high - low }
end

#put_dataHash{Symbol => Array<Float, Integer>}?

Gets put option data from the response.



231
232
233
234
235
# File 'lib/DhanHQ/models/expired_options_data.rb', line 231

def put_data
  return nil unless data.is_a?(Hash)

  data["pe"] || data[:pe]
end

#put_option?Boolean

Checks if this is put option data.



512
513
514
# File 'lib/DhanHQ/models/expired_options_data.rb', line 512

def put_option?
  drv_option_type == DhanHQ::Constants::OptionType::PUT
end

#spot_data(option_type = nil) ⇒ Array<Float>

Gets spot price data for the specified option type.



343
344
345
346
347
348
349
# File 'lib/DhanHQ/models/expired_options_data.rb', line 343

def spot_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return [] unless option_data

  option_data["spot"] || option_data[:spot] || []
end

#stock_options?Boolean

Checks if this is stock options data.



480
481
482
# File 'lib/DhanHQ/models/expired_options_data.rb', line 480

def stock_options?
  instrument == DhanHQ::Constants::InstrumentType::OPTSTK
end

#strike_data(option_type = nil) ⇒ Array<Float>

Gets strike price data for the specified option type.



328
329
330
331
332
333
334
# File 'lib/DhanHQ/models/expired_options_data.rb', line 328

def strike_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return [] unless option_data

  option_data["strike"] || option_data[:strike] || []
end

#strike_offsetInteger

Calculates the strike offset from ATM (At The Money).

Examples:

data.strike = "ATM+5"
data.strike_offset # => 5

data.strike = "ATM-3"
data.strike_offset # => -3

data.strike = "ATM"
data.strike_offset # => 0


542
543
544
545
546
547
548
549
550
551
# File 'lib/DhanHQ/models/expired_options_data.rb', line 542

def strike_offset
  return 0 if at_the_money?

  match = strike.match(/\AATM(\+|-)?(\d+)\z/)
  return 0 unless match

  sign = match[1] == "-" ? -1 : 1
  offset = match[2].to_i
  sign * offset
end

#summary_stats(option_type = nil) ⇒ Hash{Symbol => Integer, Float, Array, Boolean}

Gets comprehensive summary statistics for the specified option type.



448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/DhanHQ/models/expired_options_data.rb', line 448

def summary_stats(option_type = nil)
  option_type ||= drv_option_type
  ohlc = ohlc_data(option_type)
  volumes = volume_data(option_type)
  oi_data = open_interest_data(option_type)
  iv_data = implied_volatility_data(option_type)

  {
    data_points: data_points_count(option_type),
    avg_volume: average_volume(option_type),
    avg_open_interest: average_open_interest(option_type),
    avg_implied_volatility: average_implied_volatility(option_type),
    price_ranges: price_ranges(option_type),
    has_ohlc: !ohlc[:open].empty?,
    has_volume: !volumes.empty?,
    has_open_interest: !oi_data.empty?,
    has_implied_volatility: !iv_data.empty?
  }
end

#timestamp_data(option_type = nil) ⇒ Array<Integer>

Gets timestamp data for the specified option type.



358
359
360
361
362
363
364
# File 'lib/DhanHQ/models/expired_options_data.rb', line 358

def timestamp_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return [] unless option_data

  option_data["timestamp"] || option_data[:timestamp] || []
end

#to_candles(option_type = nil) ⇒ Array<Hash>

Normalizes the columnar response into an array of candle hashes.



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/DhanHQ/models/expired_options_data.rb', line 162

def to_candles(option_type = nil)
  option_type ||= drv_option_type
  opt_data = data_for_type(option_type)
  return [] unless opt_data.is_a?(Hash)

  # Standardize keys to symbols
  opt_data = opt_data.transform_keys(&:to_sym)
  ts_arr = opt_data[:timestamp]
  return [] unless ts_arr.is_a?(Array)

  type_sym = option_type.to_s.downcase.to_sym

  ts_arr.each_with_index.map do |ts, i|
    candle = {
      option_type: type_sym,
      timestamp: ts.is_a?(Numeric) ? Time.at(ts) : ts
    }

    # Map requested fields
    OHLC_FIELDS.each do |field|
      val_arr = opt_data[field]
      next unless val_arr.is_a?(Array)

      # Map 'oi' to 'open_interest' if requested
      target_field = field == :oi ? :open_interest : field
      candle[target_field] = val_arr[i]
    end
    candle
  end
end

#volume_data(option_type = nil) ⇒ Array<Integer>

Gets volume data for the specified option type.



283
284
285
286
287
288
289
# File 'lib/DhanHQ/models/expired_options_data.rb', line 283

def volume_data(option_type = nil)
  option_type ||= drv_option_type
  option_data = data_for_type(option_type)
  return [] unless option_data

  option_data["volume"] || option_data[:volume] || []
end

#weekly_expiry?Boolean

Checks if this is weekly expiry data.



488
489
490
# File 'lib/DhanHQ/models/expired_options_data.rb', line 488

def weekly_expiry?
  expiry_flag == "WEEK"
end