Class: DhanHQ::Models::Instrument

Inherits:
BaseModel
  • Object
show all
Includes:
InstrumentHelpers
Defined in:
lib/DhanHQ/models/instrument.rb

Overview

Model wrapper for fetching instruments by exchange segment.

Constant Summary

Constants included from ResponseHelper

ResponseHelper::STATUS_ERROR_FALLBACK

Instance Attribute Summary

Attributes inherited from BaseModel

#attributes, #errors

Class Method Summary collapse

Methods included from InstrumentHelpers

#daily, #expiry_list, #intraday, #ltp, #ohlc, #option_chain, #quote

Methods inherited from BaseModel

all, api, api_type, #assign_attributes, attributes, create, #delete, #destroy, #id, #initialize, #new_record?, #optionchain_api?, parse_collection_response, #persisted?, 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, #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

.by_segment(exchange_segment) ⇒ Array<Instrument>

Retrieve instruments for a given segment, returning an array of models.

Parameters:

  • exchange_segment (String)

Returns:



27
28
29
30
31
32
33
34
35
36
# File 'lib/DhanHQ/models/instrument.rb', line 27

def by_segment(exchange_segment)
  validate_params!({ exchange_segment: exchange_segment }, DhanHQ::Contracts::InstrumentListContract)

  csv_text = resource.by_segment(exchange_segment)
  return [] unless csv_text.is_a?(String) && !csv_text.empty?

  require "csv"
  rows = CSV.parse(csv_text, headers: true)
  rows.map { |r| new(normalize_csv_row(r), skip_validation: true) }
end

.find(exchange_segment, symbol, options = { exact_match: true, case_sensitive: false }) ⇒ Instrument?

Find a specific instrument by exchange segment and symbol.

Examples:

# Find RELIANCE in NSE_EQ (uses underlying_symbol for equity)
instrument = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
puts instrument.security_id  # => "2885"

# Find NIFTY in IDX_I (uses symbol_name for indices)
instrument = DhanHQ::Models::Instrument.find("IDX_I", "NIFTY")
puts instrument.security_id  # => "13"

# Exact match search
instrument = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE", exact_match: true)

# Case sensitive search
instrument = DhanHQ::Models::Instrument.find("NSE_EQ", "reliance", case_sensitive: true)

Parameters:

  • exchange_segment (String)

    The exchange segment (e.g., “NSE_EQ”, “IDX_I”)

  • symbol (String)

    The symbol name to search for

  • options (Hash) (defaults to: { exact_match: true, case_sensitive: false })

    Additional search options

Options Hash (options):

  • :exact_match (Boolean)

    Whether to perform exact symbol matching (default: false)

  • :case_sensitive (Boolean)

    Whether the search should be case sensitive (default: false)

Returns:

  • (Instrument, nil)

    The found instrument or nil if not found



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/DhanHQ/models/instrument.rb', line 59

def find(exchange_segment, symbol, options = { exact_match: true, case_sensitive: false })
  validate_params!({ exchange_segment: exchange_segment, symbol: symbol }, DhanHQ::Contracts::InstrumentListContract)

  exact_match = options[:exact_match] || false
  case_sensitive = options[:case_sensitive] || false

  instruments = by_segment(exchange_segment)
  return nil if instruments.empty?

  search_symbol = case_sensitive ? symbol : symbol.upcase

  instruments.find do |instrument|
    # For equity instruments, prefer underlying_symbol over symbol_name
    instrument_symbol = if instrument.instrument == DhanHQ::Constants::InstrumentType::EQUITY && instrument.underlying_symbol
                          case_sensitive ? instrument.underlying_symbol : instrument.underlying_symbol.upcase
                        else
                          case_sensitive ? instrument.symbol_name : instrument.symbol_name.upcase
                        end

    if exact_match
      instrument_symbol == search_symbol
    else
      instrument_symbol.include?(search_symbol)
    end
  end
end

.find_anywhere(symbol, options = {}) ⇒ Instrument?

Find a specific instrument across all exchange segments.

Examples:

# Find RELIANCE across all segments
instrument = DhanHQ::Models::Instrument.find_anywhere("RELIANCE")
puts "#{instrument.exchange_segment}:#{instrument.security_id}"  # => "NSE_EQ:2885"

# Find NIFTY across all segments
instrument = DhanHQ::Models::Instrument.find_anywhere("NIFTY")
puts "#{instrument.exchange_segment}:#{instrument.security_id}"  # => "IDX_I:13"

# Search only in specific segments
instrument = DhanHQ::Models::Instrument.find_anywhere("RELIANCE", segments: ["NSE_EQ", "BSE_EQ"])

Parameters:

  • symbol (String)

    The symbol name to search for

  • options (Hash) (defaults to: {})

    Additional search options

Options Hash (options):

  • :exact_match (Boolean)

    Whether to perform exact symbol matching (default: false)

  • :case_sensitive (Boolean)

    Whether the search should be case sensitive (default: false)

  • :segments (Array<String>)

    Specific segments to search in (default: all common segments)

Returns:

  • (Instrument, nil)

    The found instrument or nil if not found



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/DhanHQ/models/instrument.rb', line 104

def find_anywhere(symbol, options = {})
  exact_match = options[:exact_match] || false
  case_sensitive = options[:case_sensitive] || false
  segments = options[:segments] || %w[NSE_EQ BSE_EQ IDX_I NSE_FNO NSE_CURRENCY]

  segments.each do |segment|
    instrument = find(segment, symbol, exact_match: exact_match, case_sensitive: case_sensitive)
    return instrument if instrument
  end

  nil
end

.normalize_csv_row(row) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/DhanHQ/models/instrument.rb', line 117

def normalize_csv_row(row)
  # Extract exchange and segment from CSV
  exchange_id = row["EXCH_ID"] || row["EXCHANGE"]
  segment_code = row["SEGMENT"]

  # Calculate exchange_segment using SEGMENT_MAP from Constants
  exchange_segment = if exchange_id && segment_code
                       DhanHQ::Constants::SEGMENT_MAP[[exchange_id, segment_code]]
                     else
                       row["EXCH_ID"] # Fallback to original value
                     end

  {
    security_id: row["SECURITY_ID"].to_s,
    symbol_name: row["SYMBOL_NAME"],
    display_name: row["DISPLAY_NAME"],
    exchange: exchange_id,
    segment: segment_code,
    exchange_segment: exchange_segment,
    instrument: row["INSTRUMENT"],
    series: row["SERIES"],
    lot_size: row["LOT_SIZE"]&.to_f,
    tick_size: row["TICK_SIZE"]&.to_f,
    expiry_date: row["SM_EXPIRY_DATE"],
    strike_price: row["STRIKE_PRICE"]&.to_f,
    option_type: row["OPTION_TYPE"],
    underlying_symbol: row["UNDERLYING_SYMBOL"],
    isin: row["ISIN"],
    instrument_type: row["INSTRUMENT_TYPE"],
    expiry_flag: row["EXPIRY_FLAG"],
    bracket_flag: row["BRACKET_FLAG"],
    cover_flag: row["COVER_FLAG"],
    asm_gsm_flag: row["ASM_GSM_FLAG"],
    asm_gsm_category: row["ASM_GSM_CATEGORY"],
    buy_sell_indicator: row["BUY_SELL_INDICATOR"],
    buy_co_min_margin_per: row["BUY_CO_MIN_MARGIN_PER"]&.to_f,
    sell_co_min_margin_per: row["SELL_CO_MIN_MARGIN_PER"]&.to_f,
    mtf_leverage: row["MTF_LEVERAGE"]&.to_f
  }
end

.resourceDhanHQ::Resources::Instruments



20
21
22
# File 'lib/DhanHQ/models/instrument.rb', line 20

def resource
  @resource ||= DhanHQ::Resources::Instruments.new
end