Improve convert-to-trade security selection with search-first UX (#703)

* Enhance security handling logic:
- Prioritize user's country in sorting securities and country codes.
- Add comprehensive mapping for MIC codes to user-friendly exchange names.
- Revamp combobox to consistently pull from a provider when available.
- Improve handling of custom ticker and exchange input fields.

* Localize securities combobox display and exchange labels.

---------

Co-authored-by: luckyPipewrench <luckypipewrench@proton.me>
This commit is contained in:
LPW
2026-01-19 09:49:51 -05:00
committed by GitHub
parent bf9bcae600
commit 237035c8d4
9 changed files with 250 additions and 79 deletions

View File

@@ -1,6 +1,132 @@
class Security < ApplicationRecord
include Provided
# ISO 10383 MIC codes mapped to user-friendly exchange names
# Source: https://www.iso20022.org/market-identifier-codes
EXCHANGE_NAMES = {
# United States - NASDAQ family (Operating MIC: XNAS)
"XNAS" => "NASDAQ",
"XNGS" => "NASDAQ", # Global Select Market
"XNMS" => "NASDAQ", # Global Market
"XNCM" => "NASDAQ", # Capital Market
"XBOS" => "NASDAQ BX",
"XPSX" => "NASDAQ PSX",
"XNDQ" => "NASDAQ Options",
# United States - NYSE family (Operating MIC: XNYS)
"XNYS" => "NYSE",
"ARCX" => "NYSE Arca",
"XASE" => "NYSE American", # Formerly AMEX
"XCHI" => "NYSE Chicago",
"XCIS" => "NYSE National",
"AMXO" => "NYSE American Options",
"ARCO" => "NYSE Arca Options",
# United States - OTC Markets (Operating MIC: OTCM)
"OTCM" => "OTC Markets",
"PINX" => "OTC Pink",
"OTCQ" => "OTCQX",
"OTCB" => "OTCQB",
"PSGM" => "OTC Grey",
# United States - Other
"XCBO" => "CBOE",
"XCME" => "CME",
"XCBT" => "CBOT",
"XNYM" => "NYMEX",
"BATS" => "CBOE BZX",
"EDGX" => "CBOE EDGX",
"IEXG" => "IEX",
"MEMX" => "MEMX",
# United Kingdom
"XLON" => "London Stock Exchange",
"XLME" => "London Metal Exchange",
# Germany
"XETR" => "Xetra",
"XFRA" => "Frankfurt",
"XSTU" => "Stuttgart",
"XMUN" => "Munich",
"XBER" => "Berlin",
"XHAM" => "Hamburg",
"XDUS" => "Düsseldorf",
"XHAN" => "Hannover",
# Euronext
"XPAR" => "Euronext Paris",
"XAMS" => "Euronext Amsterdam",
"XBRU" => "Euronext Brussels",
"XLIS" => "Euronext Lisbon",
"XDUB" => "Euronext Dublin",
"XOSL" => "Euronext Oslo",
"XMIL" => "Euronext Milan",
# Other Europe
"XSWX" => "SIX Swiss",
"XVTX" => "SIX Swiss",
"XMAD" => "BME Madrid",
"XWBO" => "Vienna",
"XCSE" => "Copenhagen",
"XHEL" => "Helsinki",
"XSTO" => "Stockholm",
"XICE" => "Iceland",
"XPRA" => "Prague",
"XWAR" => "Warsaw",
"XATH" => "Athens",
"XIST" => "Istanbul",
# Canada
"XTSE" => "Toronto",
"XTSX" => "TSX Venture",
"XCNQ" => "CSE",
"NEOE" => "NEO",
# Australia & New Zealand
"XASX" => "ASX",
"XNZE" => "NZX",
# Asia - Japan
"XTKS" => "Tokyo",
"XJPX" => "Japan Exchange",
"XOSE" => "Osaka",
"XNGO" => "Nagoya",
"XSAP" => "Sapporo",
"XFKA" => "Fukuoka",
# Asia - China
"XSHG" => "Shanghai",
"XSHE" => "Shenzhen",
"XHKG" => "Hong Kong",
# Asia - Other
"XKRX" => "Korea Exchange",
"XKOS" => "KOSDAQ",
"XTAI" => "Taiwan",
"XSES" => "Singapore",
"XBKK" => "Thailand",
"XIDX" => "Indonesia",
"XKLS" => "Malaysia",
"XPHS" => "Philippines",
"XBOM" => "BSE India",
"XNSE" => "NSE India",
# Latin America
"XMEX" => "Mexico",
"XBUE" => "Buenos Aires",
"XBOG" => "Colombia",
"XSGO" => "Santiago",
"BVMF" => "B3 Brazil",
"XLIM" => "Lima",
# Middle East & Africa
"XTAE" => "Tel Aviv",
"XDFM" => "Dubai",
"XADS" => "Abu Dhabi",
"XSAU" => "Saudi (Tadawul)",
"XJSE" => "Johannesburg"
}.freeze
before_validation :upcase_symbols
has_many :trades, dependent: :nullify, class_name: "Trade"
@@ -11,6 +137,16 @@ class Security < ApplicationRecord
scope :online, -> { where(offline: false) }
# Returns user-friendly exchange name for a MIC code
def self.exchange_name_for(mic)
return nil if mic.blank?
EXCHANGE_NAMES[mic.upcase] || mic.upcase
end
def exchange_name
self.class.exchange_name_for(exchange_operating_mic)
end
def current_price
@current_price ||= find_or_fetch_price
return nil if @current_price.nil?