diff --git a/openbb_platform/core/openbb/package/__init__.py b/openbb_platform/core/openbb/package/__init__.py index 6d065b4f42f..6eec957f14b 100644 --- a/openbb_platform/core/openbb/package/__init__.py +++ b/openbb_platform/core/openbb/package/__init__.py @@ -1,2 +1,3 @@ -""" Autogenerated OpenBB module.""" +"""Autogenerated OpenBB module.""" + ### THIS FILE IS AUTO-GENERATED. DO NOT EDIT. ### diff --git a/openbb_platform/core/openbb_core/provider/utils/country_data.json b/openbb_platform/core/openbb_core/provider/utils/country_data.json index 3d506fd85d2..512cd2d42e5 100644 --- a/openbb_platform/core/openbb_core/provider/utils/country_data.json +++ b/openbb_platform/core/openbb_core/provider/utils/country_data.json @@ -1,253 +1,1788 @@ { + "_last_updated": "2026-02-21", + "_sources": { + "iso_3166": "pycountry library", + "G7": "https://en.wikipedia.org/wiki/G7", + "G20": "https://en.wikipedia.org/wiki/G20", + "EU": "https://european-union.europa.eu/principles-countries-history/eu-countries_en", + "NATO": "https://en.wikipedia.org/wiki/Member_states_of_NATO", + "OECD": "https://en.wikipedia.org/wiki/OECD", + "OPEC": "https://en.wikipedia.org/wiki/OPEC", + "BRICS": "https://en.wikipedia.org/wiki/BRICS" + }, "countries": [ - {"alpha_2": "AF", "alpha_3": "AFG", "name": "Afghanistan", "numeric": "004"}, - {"alpha_2": "AL", "alpha_3": "ALB", "name": "Albania", "numeric": "008"}, - {"alpha_2": "DZ", "alpha_3": "DZA", "name": "Algeria", "numeric": "012"}, - {"alpha_2": "AS", "alpha_3": "ASM", "name": "American Samoa", "numeric": "016"}, - {"alpha_2": "AD", "alpha_3": "AND", "name": "Andorra", "numeric": "020"}, - {"alpha_2": "AO", "alpha_3": "AGO", "name": "Angola", "numeric": "024"}, - {"alpha_2": "AI", "alpha_3": "AIA", "name": "Anguilla", "numeric": "660"}, - {"alpha_2": "AQ", "alpha_3": "ATA", "name": "Antarctica", "numeric": "010"}, - {"alpha_2": "AG", "alpha_3": "ATG", "name": "Antigua and Barbuda", "numeric": "028"}, - {"alpha_2": "AR", "alpha_3": "ARG", "name": "Argentina", "numeric": "032"}, - {"alpha_2": "AM", "alpha_3": "ARM", "name": "Armenia", "numeric": "051"}, - {"alpha_2": "AW", "alpha_3": "ABW", "name": "Aruba", "numeric": "533"}, - {"alpha_2": "AU", "alpha_3": "AUS", "name": "Australia", "numeric": "036"}, - {"alpha_2": "AT", "alpha_3": "AUT", "name": "Austria", "numeric": "040"}, - {"alpha_2": "AZ", "alpha_3": "AZE", "name": "Azerbaijan", "numeric": "031"}, - {"alpha_2": "BS", "alpha_3": "BHS", "name": "Bahamas", "numeric": "044"}, - {"alpha_2": "BH", "alpha_3": "BHR", "name": "Bahrain", "numeric": "048"}, - {"alpha_2": "BD", "alpha_3": "BGD", "name": "Bangladesh", "numeric": "050"}, - {"alpha_2": "BB", "alpha_3": "BRB", "name": "Barbados", "numeric": "052"}, - {"alpha_2": "BY", "alpha_3": "BLR", "name": "Belarus", "numeric": "112"}, - {"alpha_2": "BE", "alpha_3": "BEL", "name": "Belgium", "numeric": "056"}, - {"alpha_2": "BZ", "alpha_3": "BLZ", "name": "Belize", "numeric": "084"}, - {"alpha_2": "BJ", "alpha_3": "BEN", "name": "Benin", "numeric": "204"}, - {"alpha_2": "BM", "alpha_3": "BMU", "name": "Bermuda", "numeric": "060"}, - {"alpha_2": "BT", "alpha_3": "BTN", "name": "Bhutan", "numeric": "064"}, - {"alpha_2": "BO", "alpha_3": "BOL", "name": "Bolivia, Plurinational State of", "numeric": "068"}, - {"alpha_2": "BQ", "alpha_3": "BES", "name": "Bonaire, Sint Eustatius and Saba", "numeric": "535"}, - {"alpha_2": "BA", "alpha_3": "BIH", "name": "Bosnia and Herzegovina", "numeric": "070"}, - {"alpha_2": "BW", "alpha_3": "BWA", "name": "Botswana", "numeric": "072"}, - {"alpha_2": "BV", "alpha_3": "BVT", "name": "Bouvet Island", "numeric": "074"}, - {"alpha_2": "BR", "alpha_3": "BRA", "name": "Brazil", "numeric": "076"}, - {"alpha_2": "IO", "alpha_3": "IOT", "name": "British Indian Ocean Territory", "numeric": "086"}, - {"alpha_2": "BN", "alpha_3": "BRN", "name": "Brunei Darussalam", "numeric": "096"}, - {"alpha_2": "BG", "alpha_3": "BGR", "name": "Bulgaria", "numeric": "100"}, - {"alpha_2": "BF", "alpha_3": "BFA", "name": "Burkina Faso", "numeric": "854"}, - {"alpha_2": "BI", "alpha_3": "BDI", "name": "Burundi", "numeric": "108"}, - {"alpha_2": "CV", "alpha_3": "CPV", "name": "Cabo Verde", "numeric": "132"}, - {"alpha_2": "KH", "alpha_3": "KHM", "name": "Cambodia", "numeric": "116"}, - {"alpha_2": "CM", "alpha_3": "CMR", "name": "Cameroon", "numeric": "120"}, - {"alpha_2": "CA", "alpha_3": "CAN", "name": "Canada", "numeric": "124"}, - {"alpha_2": "KY", "alpha_3": "CYM", "name": "Cayman Islands", "numeric": "136"}, - {"alpha_2": "CF", "alpha_3": "CAF", "name": "Central African Republic", "numeric": "140"}, - {"alpha_2": "TD", "alpha_3": "TCD", "name": "Chad", "numeric": "148"}, - {"alpha_2": "CL", "alpha_3": "CHL", "name": "Chile", "numeric": "152"}, - {"alpha_2": "CN", "alpha_3": "CHN", "name": "China", "numeric": "156"}, - {"alpha_2": "CX", "alpha_3": "CXR", "name": "Christmas Island", "numeric": "162"}, - {"alpha_2": "CC", "alpha_3": "CCK", "name": "Cocos (Keeling) Islands", "numeric": "166"}, - {"alpha_2": "CO", "alpha_3": "COL", "name": "Colombia", "numeric": "170"}, - {"alpha_2": "KM", "alpha_3": "COM", "name": "Comoros", "numeric": "174"}, - {"alpha_2": "CG", "alpha_3": "COG", "name": "Congo", "numeric": "178"}, - {"alpha_2": "CD", "alpha_3": "COD", "name": "Congo, Democratic Republic of the", "numeric": "180"}, - {"alpha_2": "CK", "alpha_3": "COK", "name": "Cook Islands", "numeric": "184"}, - {"alpha_2": "CR", "alpha_3": "CRI", "name": "Costa Rica", "numeric": "188"}, - {"alpha_2": "HR", "alpha_3": "HRV", "name": "Croatia", "numeric": "191"}, - {"alpha_2": "CU", "alpha_3": "CUB", "name": "Cuba", "numeric": "192"}, - {"alpha_2": "CW", "alpha_3": "CUW", "name": "Curaçao", "numeric": "531"}, - {"alpha_2": "CY", "alpha_3": "CYP", "name": "Cyprus", "numeric": "196"}, - {"alpha_2": "CZ", "alpha_3": "CZE", "name": "Czechia", "numeric": "203"}, - {"alpha_2": "CI", "alpha_3": "CIV", "name": "Côte d'Ivoire", "numeric": "384"}, - {"alpha_2": "DK", "alpha_3": "DNK", "name": "Denmark", "numeric": "208"}, - {"alpha_2": "DJ", "alpha_3": "DJI", "name": "Djibouti", "numeric": "262"}, - {"alpha_2": "DM", "alpha_3": "DMA", "name": "Dominica", "numeric": "212"}, - {"alpha_2": "DO", "alpha_3": "DOM", "name": "Dominican Republic", "numeric": "214"}, - {"alpha_2": "EC", "alpha_3": "ECU", "name": "Ecuador", "numeric": "218"}, - {"alpha_2": "EG", "alpha_3": "EGY", "name": "Egypt", "numeric": "818"}, - {"alpha_2": "SV", "alpha_3": "SLV", "name": "El Salvador", "numeric": "222"}, - {"alpha_2": "GQ", "alpha_3": "GNQ", "name": "Equatorial Guinea", "numeric": "226"}, - {"alpha_2": "ER", "alpha_3": "ERI", "name": "Eritrea", "numeric": "232"}, - {"alpha_2": "EE", "alpha_3": "EST", "name": "Estonia", "numeric": "233"}, - {"alpha_2": "SZ", "alpha_3": "SWZ", "name": "Eswatini", "numeric": "748"}, - {"alpha_2": "ET", "alpha_3": "ETH", "name": "Ethiopia", "numeric": "231"}, - {"alpha_2": "FK", "alpha_3": "FLK", "name": "Falkland Islands (Malvinas)", "numeric": "238"}, - {"alpha_2": "FO", "alpha_3": "FRO", "name": "Faroe Islands", "numeric": "234"}, - {"alpha_2": "FJ", "alpha_3": "FJI", "name": "Fiji", "numeric": "242"}, - {"alpha_2": "FI", "alpha_3": "FIN", "name": "Finland", "numeric": "246"}, - {"alpha_2": "FR", "alpha_3": "FRA", "name": "France", "numeric": "250"}, - {"alpha_2": "GF", "alpha_3": "GUF", "name": "French Guiana", "numeric": "254"}, - {"alpha_2": "PF", "alpha_3": "PYF", "name": "French Polynesia", "numeric": "258"}, - {"alpha_2": "TF", "alpha_3": "ATF", "name": "French Southern Territories", "numeric": "260"}, - {"alpha_2": "GA", "alpha_3": "GAB", "name": "Gabon", "numeric": "266"}, - {"alpha_2": "GM", "alpha_3": "GMB", "name": "Gambia", "numeric": "270"}, - {"alpha_2": "GE", "alpha_3": "GEO", "name": "Georgia", "numeric": "268"}, - {"alpha_2": "DE", "alpha_3": "DEU", "name": "Germany", "numeric": "276"}, - {"alpha_2": "GH", "alpha_3": "GHA", "name": "Ghana", "numeric": "288"}, - {"alpha_2": "GI", "alpha_3": "GIB", "name": "Gibraltar", "numeric": "292"}, - {"alpha_2": "GR", "alpha_3": "GRC", "name": "Greece", "numeric": "300"}, - {"alpha_2": "GL", "alpha_3": "GRL", "name": "Greenland", "numeric": "304"}, - {"alpha_2": "GD", "alpha_3": "GRD", "name": "Grenada", "numeric": "308"}, - {"alpha_2": "GP", "alpha_3": "GLP", "name": "Guadeloupe", "numeric": "312"}, - {"alpha_2": "GU", "alpha_3": "GUM", "name": "Guam", "numeric": "316"}, - {"alpha_2": "GT", "alpha_3": "GTM", "name": "Guatemala", "numeric": "320"}, - {"alpha_2": "GG", "alpha_3": "GGY", "name": "Guernsey", "numeric": "831"}, - {"alpha_2": "GN", "alpha_3": "GIN", "name": "Guinea", "numeric": "324"}, - {"alpha_2": "GW", "alpha_3": "GNB", "name": "Guinea-Bissau", "numeric": "624"}, - {"alpha_2": "GY", "alpha_3": "GUY", "name": "Guyana", "numeric": "328"}, - {"alpha_2": "HT", "alpha_3": "HTI", "name": "Haiti", "numeric": "332"}, - {"alpha_2": "HM", "alpha_3": "HMD", "name": "Heard Island and McDonald Islands", "numeric": "334"}, - {"alpha_2": "VA", "alpha_3": "VAT", "name": "Holy See", "numeric": "336"}, - {"alpha_2": "HN", "alpha_3": "HND", "name": "Honduras", "numeric": "340"}, - {"alpha_2": "HK", "alpha_3": "HKG", "name": "Hong Kong", "numeric": "344"}, - {"alpha_2": "HU", "alpha_3": "HUN", "name": "Hungary", "numeric": "348"}, - {"alpha_2": "IS", "alpha_3": "ISL", "name": "Iceland", "numeric": "352"}, - {"alpha_2": "IN", "alpha_3": "IND", "name": "India", "numeric": "356"}, - {"alpha_2": "ID", "alpha_3": "IDN", "name": "Indonesia", "numeric": "360"}, - {"alpha_2": "IR", "alpha_3": "IRN", "name": "Iran, Islamic Republic of", "numeric": "364"}, - {"alpha_2": "IQ", "alpha_3": "IRQ", "name": "Iraq", "numeric": "368"}, - {"alpha_2": "IE", "alpha_3": "IRL", "name": "Ireland", "numeric": "372"}, - {"alpha_2": "IM", "alpha_3": "IMN", "name": "Isle of Man", "numeric": "833"}, - {"alpha_2": "IL", "alpha_3": "ISR", "name": "Israel", "numeric": "376"}, - {"alpha_2": "IT", "alpha_3": "ITA", "name": "Italy", "numeric": "380"}, - {"alpha_2": "JM", "alpha_3": "JAM", "name": "Jamaica", "numeric": "388"}, - {"alpha_2": "JP", "alpha_3": "JPN", "name": "Japan", "numeric": "392"}, - {"alpha_2": "JE", "alpha_3": "JEY", "name": "Jersey", "numeric": "832"}, - {"alpha_2": "JO", "alpha_3": "JOR", "name": "Jordan", "numeric": "400"}, - {"alpha_2": "KZ", "alpha_3": "KAZ", "name": "Kazakhstan", "numeric": "398"}, - {"alpha_2": "KE", "alpha_3": "KEN", "name": "Kenya", "numeric": "404"}, - {"alpha_2": "KI", "alpha_3": "KIR", "name": "Kiribati", "numeric": "296"}, - {"alpha_2": "KP", "alpha_3": "PRK", "name": "Korea, Democratic People's Republic of", "numeric": "408"}, - {"alpha_2": "KR", "alpha_3": "KOR", "name": "Korea, Republic of", "numeric": "410"}, - {"alpha_2": "KW", "alpha_3": "KWT", "name": "Kuwait", "numeric": "414"}, - {"alpha_2": "KG", "alpha_3": "KGZ", "name": "Kyrgyzstan", "numeric": "417"}, - {"alpha_2": "LA", "alpha_3": "LAO", "name": "Lao People's Democratic Republic", "numeric": "418"}, - {"alpha_2": "LV", "alpha_3": "LVA", "name": "Latvia", "numeric": "428"}, - {"alpha_2": "LB", "alpha_3": "LBN", "name": "Lebanon", "numeric": "422"}, - {"alpha_2": "LS", "alpha_3": "LSO", "name": "Lesotho", "numeric": "426"}, - {"alpha_2": "LR", "alpha_3": "LBR", "name": "Liberia", "numeric": "430"}, - {"alpha_2": "LY", "alpha_3": "LBY", "name": "Libya", "numeric": "434"}, - {"alpha_2": "LI", "alpha_3": "LIE", "name": "Liechtenstein", "numeric": "438"}, - {"alpha_2": "LT", "alpha_3": "LTU", "name": "Lithuania", "numeric": "440"}, - {"alpha_2": "LU", "alpha_3": "LUX", "name": "Luxembourg", "numeric": "442"}, - {"alpha_2": "MO", "alpha_3": "MAC", "name": "Macao", "numeric": "446"}, - {"alpha_2": "MG", "alpha_3": "MDG", "name": "Madagascar", "numeric": "450"}, - {"alpha_2": "MW", "alpha_3": "MWI", "name": "Malawi", "numeric": "454"}, - {"alpha_2": "MY", "alpha_3": "MYS", "name": "Malaysia", "numeric": "458"}, - {"alpha_2": "MV", "alpha_3": "MDV", "name": "Maldives", "numeric": "462"}, - {"alpha_2": "ML", "alpha_3": "MLI", "name": "Mali", "numeric": "466"}, - {"alpha_2": "MT", "alpha_3": "MLT", "name": "Malta", "numeric": "470"}, - {"alpha_2": "MH", "alpha_3": "MHL", "name": "Marshall Islands", "numeric": "584"}, - {"alpha_2": "MQ", "alpha_3": "MTQ", "name": "Martinique", "numeric": "474"}, - {"alpha_2": "MR", "alpha_3": "MRT", "name": "Mauritania", "numeric": "478"}, - {"alpha_2": "MU", "alpha_3": "MUS", "name": "Mauritius", "numeric": "480"}, - {"alpha_2": "YT", "alpha_3": "MYT", "name": "Mayotte", "numeric": "175"}, - {"alpha_2": "MX", "alpha_3": "MEX", "name": "Mexico", "numeric": "484"}, - {"alpha_2": "FM", "alpha_3": "FSM", "name": "Micronesia, Federated States of", "numeric": "583"}, - {"alpha_2": "MD", "alpha_3": "MDA", "name": "Moldova, Republic of", "numeric": "498"}, - {"alpha_2": "MC", "alpha_3": "MCO", "name": "Monaco", "numeric": "492"}, - {"alpha_2": "MN", "alpha_3": "MNG", "name": "Mongolia", "numeric": "496"}, - {"alpha_2": "ME", "alpha_3": "MNE", "name": "Montenegro", "numeric": "499"}, - {"alpha_2": "MS", "alpha_3": "MSR", "name": "Montserrat", "numeric": "500"}, - {"alpha_2": "MA", "alpha_3": "MAR", "name": "Morocco", "numeric": "504"}, - {"alpha_2": "MZ", "alpha_3": "MOZ", "name": "Mozambique", "numeric": "508"}, - {"alpha_2": "MM", "alpha_3": "MMR", "name": "Myanmar", "numeric": "104"}, - {"alpha_2": "NA", "alpha_3": "NAM", "name": "Namibia", "numeric": "516"}, - {"alpha_2": "NR", "alpha_3": "NRU", "name": "Nauru", "numeric": "520"}, - {"alpha_2": "NP", "alpha_3": "NPL", "name": "Nepal", "numeric": "524"}, - {"alpha_2": "NL", "alpha_3": "NLD", "name": "Netherlands", "numeric": "528"}, - {"alpha_2": "NC", "alpha_3": "NCL", "name": "New Caledonia", "numeric": "540"}, - {"alpha_2": "NZ", "alpha_3": "NZL", "name": "New Zealand", "numeric": "554"}, - {"alpha_2": "NI", "alpha_3": "NIC", "name": "Nicaragua", "numeric": "558"}, - {"alpha_2": "NE", "alpha_3": "NER", "name": "Niger", "numeric": "562"}, - {"alpha_2": "NG", "alpha_3": "NGA", "name": "Nigeria", "numeric": "566"}, - {"alpha_2": "NU", "alpha_3": "NIU", "name": "Niue", "numeric": "570"}, - {"alpha_2": "NF", "alpha_3": "NFK", "name": "Norfolk Island", "numeric": "574"}, - {"alpha_2": "MK", "alpha_3": "MKD", "name": "North Macedonia", "numeric": "807"}, - {"alpha_2": "MP", "alpha_3": "MNP", "name": "Northern Mariana Islands", "numeric": "580"}, - {"alpha_2": "NO", "alpha_3": "NOR", "name": "Norway", "numeric": "578"}, - {"alpha_2": "OM", "alpha_3": "OMN", "name": "Oman", "numeric": "512"}, - {"alpha_2": "PK", "alpha_3": "PAK", "name": "Pakistan", "numeric": "586"}, - {"alpha_2": "PW", "alpha_3": "PLW", "name": "Palau", "numeric": "585"}, - {"alpha_2": "PS", "alpha_3": "PSE", "name": "Palestine, State of", "numeric": "275"}, - {"alpha_2": "PA", "alpha_3": "PAN", "name": "Panama", "numeric": "591"}, - {"alpha_2": "PG", "alpha_3": "PNG", "name": "Papua New Guinea", "numeric": "598"}, - {"alpha_2": "PY", "alpha_3": "PRY", "name": "Paraguay", "numeric": "600"}, - {"alpha_2": "PE", "alpha_3": "PER", "name": "Peru", "numeric": "604"}, - {"alpha_2": "PH", "alpha_3": "PHL", "name": "Philippines", "numeric": "608"}, - {"alpha_2": "PN", "alpha_3": "PCN", "name": "Pitcairn", "numeric": "612"}, - {"alpha_2": "PL", "alpha_3": "POL", "name": "Poland", "numeric": "616"}, - {"alpha_2": "PT", "alpha_3": "PRT", "name": "Portugal", "numeric": "620"}, - {"alpha_2": "PR", "alpha_3": "PRI", "name": "Puerto Rico", "numeric": "630"}, - {"alpha_2": "QA", "alpha_3": "QAT", "name": "Qatar", "numeric": "634"}, - {"alpha_2": "RO", "alpha_3": "ROU", "name": "Romania", "numeric": "642"}, - {"alpha_2": "RU", "alpha_3": "RUS", "name": "Russian Federation", "numeric": "643"}, - {"alpha_2": "RW", "alpha_3": "RWA", "name": "Rwanda", "numeric": "646"}, - {"alpha_2": "RE", "alpha_3": "REU", "name": "Réunion", "numeric": "638"}, - {"alpha_2": "BL", "alpha_3": "BLM", "name": "Saint Barthélemy", "numeric": "652"}, - {"alpha_2": "SH", "alpha_3": "SHN", "name": "Saint Helena, Ascension and Tristan da Cunha", "numeric": "654"}, - {"alpha_2": "KN", "alpha_3": "KNA", "name": "Saint Kitts and Nevis", "numeric": "659"}, - {"alpha_2": "LC", "alpha_3": "LCA", "name": "Saint Lucia", "numeric": "662"}, - {"alpha_2": "MF", "alpha_3": "MAF", "name": "Saint Martin (French part)", "numeric": "663"}, - {"alpha_2": "PM", "alpha_3": "SPM", "name": "Saint Pierre and Miquelon", "numeric": "666"}, - {"alpha_2": "VC", "alpha_3": "VCT", "name": "Saint Vincent and the Grenadines", "numeric": "670"}, - {"alpha_2": "WS", "alpha_3": "WSM", "name": "Samoa", "numeric": "882"}, - {"alpha_2": "SM", "alpha_3": "SMR", "name": "San Marino", "numeric": "674"}, - {"alpha_2": "ST", "alpha_3": "STP", "name": "Sao Tome and Principe", "numeric": "678"}, - {"alpha_2": "SA", "alpha_3": "SAU", "name": "Saudi Arabia", "numeric": "682"}, - {"alpha_2": "SN", "alpha_3": "SEN", "name": "Senegal", "numeric": "686"}, - {"alpha_2": "RS", "alpha_3": "SRB", "name": "Serbia", "numeric": "688"}, - {"alpha_2": "SC", "alpha_3": "SYC", "name": "Seychelles", "numeric": "690"}, - {"alpha_2": "SL", "alpha_3": "SLE", "name": "Sierra Leone", "numeric": "694"}, - {"alpha_2": "SG", "alpha_3": "SGP", "name": "Singapore", "numeric": "702"}, - {"alpha_2": "SX", "alpha_3": "SXM", "name": "Sint Maarten (Dutch part)", "numeric": "534"}, - {"alpha_2": "SK", "alpha_3": "SVK", "name": "Slovakia", "numeric": "703"}, - {"alpha_2": "SI", "alpha_3": "SVN", "name": "Slovenia", "numeric": "705"}, - {"alpha_2": "SB", "alpha_3": "SLB", "name": "Solomon Islands", "numeric": "090"}, - {"alpha_2": "SO", "alpha_3": "SOM", "name": "Somalia", "numeric": "706"}, - {"alpha_2": "ZA", "alpha_3": "ZAF", "name": "South Africa", "numeric": "710"}, - {"alpha_2": "GS", "alpha_3": "SGS", "name": "South Georgia and the South Sandwich Islands", "numeric": "239"}, - {"alpha_2": "SS", "alpha_3": "SSD", "name": "South Sudan", "numeric": "728"}, - {"alpha_2": "ES", "alpha_3": "ESP", "name": "Spain", "numeric": "724"}, - {"alpha_2": "LK", "alpha_3": "LKA", "name": "Sri Lanka", "numeric": "144"}, - {"alpha_2": "SD", "alpha_3": "SDN", "name": "Sudan", "numeric": "729"}, - {"alpha_2": "SR", "alpha_3": "SUR", "name": "Suriname", "numeric": "740"}, - {"alpha_2": "SJ", "alpha_3": "SJM", "name": "Svalbard and Jan Mayen", "numeric": "744"}, - {"alpha_2": "SE", "alpha_3": "SWE", "name": "Sweden", "numeric": "752"}, - {"alpha_2": "CH", "alpha_3": "CHE", "name": "Switzerland", "numeric": "756"}, - {"alpha_2": "SY", "alpha_3": "SYR", "name": "Syrian Arab Republic", "numeric": "760"}, - {"alpha_2": "TW", "alpha_3": "TWN", "name": "Taiwan, Province of China", "numeric": "158"}, - {"alpha_2": "TJ", "alpha_3": "TJK", "name": "Tajikistan", "numeric": "762"}, - {"alpha_2": "TZ", "alpha_3": "TZA", "name": "Tanzania, United Republic of", "numeric": "834"}, - {"alpha_2": "TH", "alpha_3": "THA", "name": "Thailand", "numeric": "764"}, - {"alpha_2": "TL", "alpha_3": "TLS", "name": "Timor-Leste", "numeric": "626"}, - {"alpha_2": "TG", "alpha_3": "TGO", "name": "Togo", "numeric": "768"}, - {"alpha_2": "TK", "alpha_3": "TKL", "name": "Tokelau", "numeric": "772"}, - {"alpha_2": "TO", "alpha_3": "TON", "name": "Tonga", "numeric": "776"}, - {"alpha_2": "TT", "alpha_3": "TTO", "name": "Trinidad and Tobago", "numeric": "780"}, - {"alpha_2": "TN", "alpha_3": "TUN", "name": "Tunisia", "numeric": "788"}, - {"alpha_2": "TR", "alpha_3": "TUR", "name": "Turkey", "numeric": "792"}, - {"alpha_2": "TM", "alpha_3": "TKM", "name": "Turkmenistan", "numeric": "795"}, - {"alpha_2": "TC", "alpha_3": "TCA", "name": "Turks and Caicos Islands", "numeric": "796"}, - {"alpha_2": "TV", "alpha_3": "TUV", "name": "Tuvalu", "numeric": "798"}, - {"alpha_2": "UG", "alpha_3": "UGA", "name": "Uganda", "numeric": "800"}, - {"alpha_2": "UA", "alpha_3": "UKR", "name": "Ukraine", "numeric": "804"}, - {"alpha_2": "AE", "alpha_3": "ARE", "name": "United Arab Emirates", "numeric": "784"}, - {"alpha_2": "GB", "alpha_3": "GBR", "name": "United Kingdom", "numeric": "826"}, - {"alpha_2": "US", "alpha_3": "USA", "name": "United States", "numeric": "840"}, - {"alpha_2": "UM", "alpha_3": "UMI", "name": "United States Minor Outlying Islands", "numeric": "581"}, - {"alpha_2": "UY", "alpha_3": "URY", "name": "Uruguay", "numeric": "858"}, - {"alpha_2": "UZ", "alpha_3": "UZB", "name": "Uzbekistan", "numeric": "860"}, - {"alpha_2": "VU", "alpha_3": "VUT", "name": "Vanuatu", "numeric": "548"}, - {"alpha_2": "VE", "alpha_3": "VEN", "name": "Venezuela, Bolivarian Republic of", "numeric": "862"}, - {"alpha_2": "VN", "alpha_3": "VNM", "name": "Viet Nam", "numeric": "704"}, - {"alpha_2": "VG", "alpha_3": "VGB", "name": "Virgin Islands, British", "numeric": "092"}, - {"alpha_2": "VI", "alpha_3": "VIR", "name": "Virgin Islands, U.S.", "numeric": "850"}, - {"alpha_2": "WF", "alpha_3": "WLF", "name": "Wallis and Futuna", "numeric": "876"}, - {"alpha_2": "EH", "alpha_3": "ESH", "name": "Western Sahara", "numeric": "732"}, - {"alpha_2": "YE", "alpha_3": "YEM", "name": "Yemen", "numeric": "887"}, - {"alpha_2": "ZM", "alpha_3": "ZMB", "name": "Zambia", "numeric": "894"}, - {"alpha_2": "ZW", "alpha_3": "ZWE", "name": "Zimbabwe", "numeric": "716"}, - {"alpha_2": "AX", "alpha_3": "ALA", "name": "Åland Islands", "numeric": "248"} + { + "alpha_2": "AD", + "alpha_3": "AND", + "name": "Andorra", + "numeric": "020" + }, + { + "alpha_2": "AE", + "alpha_3": "ARE", + "name": "United Arab Emirates", + "numeric": "784", + "groups": [ + "BRICS", + "OPEC" + ] + }, + { + "alpha_2": "AF", + "alpha_3": "AFG", + "name": "Afghanistan", + "numeric": "004" + }, + { + "alpha_2": "AG", + "alpha_3": "ATG", + "name": "Antigua and Barbuda", + "numeric": "028" + }, + { + "alpha_2": "AI", + "alpha_3": "AIA", + "name": "Anguilla", + "numeric": "660" + }, + { + "alpha_2": "AL", + "alpha_3": "ALB", + "name": "Albania", + "numeric": "008", + "groups": [ + "NATO" + ] + }, + { + "alpha_2": "AM", + "alpha_3": "ARM", + "name": "Armenia", + "numeric": "051" + }, + { + "alpha_2": "AO", + "alpha_3": "AGO", + "name": "Angola", + "numeric": "024" + }, + { + "alpha_2": "AQ", + "alpha_3": "ATA", + "name": "Antarctica", + "numeric": "010" + }, + { + "alpha_2": "AR", + "alpha_3": "ARG", + "name": "Argentina", + "numeric": "032", + "groups": [ + "G20" + ] + }, + { + "alpha_2": "AS", + "alpha_3": "ASM", + "name": "American Samoa", + "numeric": "016" + }, + { + "alpha_2": "AT", + "alpha_3": "AUT", + "name": "Austria", + "numeric": "040", + "groups": [ + "EU", + "OECD" + ] + }, + { + "alpha_2": "AU", + "alpha_3": "AUS", + "name": "Australia", + "numeric": "036", + "groups": [ + "G20", + "OECD" + ] + }, + { + "alpha_2": "AW", + "alpha_3": "ABW", + "name": "Aruba", + "numeric": "533" + }, + { + "alpha_2": "AX", + "alpha_3": "ALA", + "name": "Aland Islands", + "numeric": "248" + }, + { + "alpha_2": "AZ", + "alpha_3": "AZE", + "name": "Azerbaijan", + "numeric": "031" + }, + { + "alpha_2": "BA", + "alpha_3": "BIH", + "name": "Bosnia and Herzegovina", + "numeric": "070" + }, + { + "alpha_2": "BB", + "alpha_3": "BRB", + "name": "Barbados", + "numeric": "052" + }, + { + "alpha_2": "BD", + "alpha_3": "BGD", + "name": "Bangladesh", + "numeric": "050" + }, + { + "alpha_2": "BE", + "alpha_3": "BEL", + "name": "Belgium", + "numeric": "056", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "BF", + "alpha_3": "BFA", + "name": "Burkina Faso", + "numeric": "854" + }, + { + "alpha_2": "BG", + "alpha_3": "BGR", + "name": "Bulgaria", + "numeric": "100", + "groups": [ + "EU", + "NATO" + ] + }, + { + "alpha_2": "BH", + "alpha_3": "BHR", + "name": "Bahrain", + "numeric": "048" + }, + { + "alpha_2": "BI", + "alpha_3": "BDI", + "name": "Burundi", + "numeric": "108" + }, + { + "alpha_2": "BJ", + "alpha_3": "BEN", + "name": "Benin", + "numeric": "204" + }, + { + "alpha_2": "BL", + "alpha_3": "BLM", + "name": "Saint Barthelemy", + "numeric": "652" + }, + { + "alpha_2": "BM", + "alpha_3": "BMU", + "name": "Bermuda", + "numeric": "060" + }, + { + "alpha_2": "BN", + "alpha_3": "BRN", + "name": "Brunei Darussalam", + "numeric": "096" + }, + { + "alpha_2": "BO", + "alpha_3": "BOL", + "name": "Bolivia, Plurinational State of", + "numeric": "068" + }, + { + "alpha_2": "BQ", + "alpha_3": "BES", + "name": "Bonaire, Sint Eustatius and Saba", + "numeric": "535" + }, + { + "alpha_2": "BR", + "alpha_3": "BRA", + "name": "Brazil", + "numeric": "076", + "groups": [ + "BRICS", + "G20" + ] + }, + { + "alpha_2": "BS", + "alpha_3": "BHS", + "name": "Bahamas", + "numeric": "044" + }, + { + "alpha_2": "BT", + "alpha_3": "BTN", + "name": "Bhutan", + "numeric": "064" + }, + { + "alpha_2": "BV", + "alpha_3": "BVT", + "name": "Bouvet Island", + "numeric": "074" + }, + { + "alpha_2": "BW", + "alpha_3": "BWA", + "name": "Botswana", + "numeric": "072" + }, + { + "alpha_2": "BY", + "alpha_3": "BLR", + "name": "Belarus", + "numeric": "112" + }, + { + "alpha_2": "BZ", + "alpha_3": "BLZ", + "name": "Belize", + "numeric": "084" + }, + { + "alpha_2": "CA", + "alpha_3": "CAN", + "name": "Canada", + "numeric": "124", + "groups": [ + "G20", + "G7", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "CC", + "alpha_3": "CCK", + "name": "Cocos (Keeling) Islands", + "numeric": "166" + }, + { + "alpha_2": "CD", + "alpha_3": "COD", + "name": "Congo, The Democratic Republic of the", + "numeric": "180" + }, + { + "alpha_2": "CF", + "alpha_3": "CAF", + "name": "Central African Republic", + "numeric": "140" + }, + { + "alpha_2": "CG", + "alpha_3": "COG", + "name": "Congo", + "numeric": "178", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "CH", + "alpha_3": "CHE", + "name": "Switzerland", + "numeric": "756", + "groups": [ + "OECD" + ] + }, + { + "alpha_2": "CI", + "alpha_3": "CIV", + "name": "Cote d'Ivoire", + "numeric": "384" + }, + { + "alpha_2": "CK", + "alpha_3": "COK", + "name": "Cook Islands", + "numeric": "184" + }, + { + "alpha_2": "CL", + "alpha_3": "CHL", + "name": "Chile", + "numeric": "152", + "groups": [ + "OECD" + ] + }, + { + "alpha_2": "CM", + "alpha_3": "CMR", + "name": "Cameroon", + "numeric": "120" + }, + { + "alpha_2": "CN", + "alpha_3": "CHN", + "name": "China", + "numeric": "156", + "groups": [ + "BRICS", + "G20" + ] + }, + { + "alpha_2": "CO", + "alpha_3": "COL", + "name": "Colombia", + "numeric": "170", + "groups": [ + "OECD" + ] + }, + { + "alpha_2": "CR", + "alpha_3": "CRI", + "name": "Costa Rica", + "numeric": "188", + "groups": [ + "OECD" + ] + }, + { + "alpha_2": "CU", + "alpha_3": "CUB", + "name": "Cuba", + "numeric": "192" + }, + { + "alpha_2": "CV", + "alpha_3": "CPV", + "name": "Cabo Verde", + "numeric": "132" + }, + { + "alpha_2": "CW", + "alpha_3": "CUW", + "name": "Curacao", + "numeric": "531" + }, + { + "alpha_2": "CX", + "alpha_3": "CXR", + "name": "Christmas Island", + "numeric": "162" + }, + { + "alpha_2": "CY", + "alpha_3": "CYP", + "name": "Cyprus", + "numeric": "196", + "groups": [ + "EU" + ] + }, + { + "alpha_2": "CZ", + "alpha_3": "CZE", + "name": "Czechia", + "numeric": "203", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "DE", + "alpha_3": "DEU", + "name": "Germany", + "numeric": "276", + "groups": [ + "EU", + "G20", + "G7", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "DJ", + "alpha_3": "DJI", + "name": "Djibouti", + "numeric": "262" + }, + { + "alpha_2": "DK", + "alpha_3": "DNK", + "name": "Denmark", + "numeric": "208", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "DM", + "alpha_3": "DMA", + "name": "Dominica", + "numeric": "212" + }, + { + "alpha_2": "DO", + "alpha_3": "DOM", + "name": "Dominican Republic", + "numeric": "214" + }, + { + "alpha_2": "DZ", + "alpha_3": "DZA", + "name": "Algeria", + "numeric": "012", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "EC", + "alpha_3": "ECU", + "name": "Ecuador", + "numeric": "218" + }, + { + "alpha_2": "EE", + "alpha_3": "EST", + "name": "Estonia", + "numeric": "233", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "EG", + "alpha_3": "EGY", + "name": "Egypt", + "numeric": "818", + "groups": [ + "BRICS" + ] + }, + { + "alpha_2": "EH", + "alpha_3": "ESH", + "name": "Western Sahara", + "numeric": "732" + }, + { + "alpha_2": "ER", + "alpha_3": "ERI", + "name": "Eritrea", + "numeric": "232" + }, + { + "alpha_2": "ES", + "alpha_3": "ESP", + "name": "Spain", + "numeric": "724", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "ET", + "alpha_3": "ETH", + "name": "Ethiopia", + "numeric": "231", + "groups": [ + "BRICS" + ] + }, + { + "alpha_2": "FI", + "alpha_3": "FIN", + "name": "Finland", + "numeric": "246", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "FJ", + "alpha_3": "FJI", + "name": "Fiji", + "numeric": "242" + }, + { + "alpha_2": "FK", + "alpha_3": "FLK", + "name": "Falkland Islands (Malvinas)", + "numeric": "238" + }, + { + "alpha_2": "FM", + "alpha_3": "FSM", + "name": "Micronesia, Federated States of", + "numeric": "583" + }, + { + "alpha_2": "FO", + "alpha_3": "FRO", + "name": "Faroe Islands", + "numeric": "234" + }, + { + "alpha_2": "FR", + "alpha_3": "FRA", + "name": "France", + "numeric": "250", + "groups": [ + "EU", + "G20", + "G7", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "GA", + "alpha_3": "GAB", + "name": "Gabon", + "numeric": "266", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "GB", + "alpha_3": "GBR", + "name": "United Kingdom", + "numeric": "826", + "groups": [ + "G20", + "G7", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "GD", + "alpha_3": "GRD", + "name": "Grenada", + "numeric": "308" + }, + { + "alpha_2": "GE", + "alpha_3": "GEO", + "name": "Georgia", + "numeric": "268" + }, + { + "alpha_2": "GF", + "alpha_3": "GUF", + "name": "French Guiana", + "numeric": "254" + }, + { + "alpha_2": "GG", + "alpha_3": "GGY", + "name": "Guernsey", + "numeric": "831" + }, + { + "alpha_2": "GH", + "alpha_3": "GHA", + "name": "Ghana", + "numeric": "288" + }, + { + "alpha_2": "GI", + "alpha_3": "GIB", + "name": "Gibraltar", + "numeric": "292" + }, + { + "alpha_2": "GL", + "alpha_3": "GRL", + "name": "Greenland", + "numeric": "304" + }, + { + "alpha_2": "GM", + "alpha_3": "GMB", + "name": "Gambia", + "numeric": "270" + }, + { + "alpha_2": "GN", + "alpha_3": "GIN", + "name": "Guinea", + "numeric": "324" + }, + { + "alpha_2": "GP", + "alpha_3": "GLP", + "name": "Guadeloupe", + "numeric": "312" + }, + { + "alpha_2": "GQ", + "alpha_3": "GNQ", + "name": "Equatorial Guinea", + "numeric": "226", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "GR", + "alpha_3": "GRC", + "name": "Greece", + "numeric": "300", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "GS", + "alpha_3": "SGS", + "name": "South Georgia and the South Sandwich Islands", + "numeric": "239" + }, + { + "alpha_2": "GT", + "alpha_3": "GTM", + "name": "Guatemala", + "numeric": "320" + }, + { + "alpha_2": "GU", + "alpha_3": "GUM", + "name": "Guam", + "numeric": "316" + }, + { + "alpha_2": "GW", + "alpha_3": "GNB", + "name": "Guinea-Bissau", + "numeric": "624" + }, + { + "alpha_2": "GY", + "alpha_3": "GUY", + "name": "Guyana", + "numeric": "328" + }, + { + "alpha_2": "HK", + "alpha_3": "HKG", + "name": "Hong Kong", + "numeric": "344" + }, + { + "alpha_2": "HM", + "alpha_3": "HMD", + "name": "Heard Island and McDonald Islands", + "numeric": "334" + }, + { + "alpha_2": "HN", + "alpha_3": "HND", + "name": "Honduras", + "numeric": "340" + }, + { + "alpha_2": "HR", + "alpha_3": "HRV", + "name": "Croatia", + "numeric": "191", + "groups": [ + "EU", + "NATO" + ] + }, + { + "alpha_2": "HT", + "alpha_3": "HTI", + "name": "Haiti", + "numeric": "332" + }, + { + "alpha_2": "HU", + "alpha_3": "HUN", + "name": "Hungary", + "numeric": "348", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "ID", + "alpha_3": "IDN", + "name": "Indonesia", + "numeric": "360", + "groups": [ + "BRICS", + "G20" + ] + }, + { + "alpha_2": "IE", + "alpha_3": "IRL", + "name": "Ireland", + "numeric": "372", + "groups": [ + "EU", + "OECD" + ] + }, + { + "alpha_2": "IL", + "alpha_3": "ISR", + "name": "Israel", + "numeric": "376", + "groups": [ + "OECD" + ] + }, + { + "alpha_2": "IM", + "alpha_3": "IMN", + "name": "Isle of Man", + "numeric": "833" + }, + { + "alpha_2": "IN", + "alpha_3": "IND", + "name": "India", + "numeric": "356", + "groups": [ + "BRICS", + "G20" + ] + }, + { + "alpha_2": "IO", + "alpha_3": "IOT", + "name": "British Indian Ocean Territory", + "numeric": "086" + }, + { + "alpha_2": "IQ", + "alpha_3": "IRQ", + "name": "Iraq", + "numeric": "368", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "IR", + "alpha_3": "IRN", + "name": "Iran, Islamic Republic of", + "numeric": "364", + "groups": [ + "BRICS", + "OPEC" + ] + }, + { + "alpha_2": "IS", + "alpha_3": "ISL", + "name": "Iceland", + "numeric": "352", + "groups": [ + "NATO", + "OECD" + ] + }, + { + "alpha_2": "IT", + "alpha_3": "ITA", + "name": "Italy", + "numeric": "380", + "groups": [ + "EU", + "G20", + "G7", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "JE", + "alpha_3": "JEY", + "name": "Jersey", + "numeric": "832" + }, + { + "alpha_2": "JM", + "alpha_3": "JAM", + "name": "Jamaica", + "numeric": "388" + }, + { + "alpha_2": "JO", + "alpha_3": "JOR", + "name": "Jordan", + "numeric": "400" + }, + { + "alpha_2": "JP", + "alpha_3": "JPN", + "name": "Japan", + "numeric": "392", + "groups": [ + "G20", + "G7", + "OECD" + ] + }, + { + "alpha_2": "KE", + "alpha_3": "KEN", + "name": "Kenya", + "numeric": "404" + }, + { + "alpha_2": "KG", + "alpha_3": "KGZ", + "name": "Kyrgyzstan", + "numeric": "417" + }, + { + "alpha_2": "KH", + "alpha_3": "KHM", + "name": "Cambodia", + "numeric": "116" + }, + { + "alpha_2": "KI", + "alpha_3": "KIR", + "name": "Kiribati", + "numeric": "296" + }, + { + "alpha_2": "KM", + "alpha_3": "COM", + "name": "Comoros", + "numeric": "174" + }, + { + "alpha_2": "KN", + "alpha_3": "KNA", + "name": "Saint Kitts and Nevis", + "numeric": "659" + }, + { + "alpha_2": "KP", + "alpha_3": "PRK", + "name": "Korea, Democratic People's Republic of", + "numeric": "408" + }, + { + "alpha_2": "KR", + "alpha_3": "KOR", + "name": "Korea, Republic of", + "numeric": "410", + "groups": [ + "G20", + "OECD" + ] + }, + { + "alpha_2": "KW", + "alpha_3": "KWT", + "name": "Kuwait", + "numeric": "414", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "KY", + "alpha_3": "CYM", + "name": "Cayman Islands", + "numeric": "136" + }, + { + "alpha_2": "KZ", + "alpha_3": "KAZ", + "name": "Kazakhstan", + "numeric": "398" + }, + { + "alpha_2": "LA", + "alpha_3": "LAO", + "name": "Lao People's Democratic Republic", + "numeric": "418" + }, + { + "alpha_2": "LB", + "alpha_3": "LBN", + "name": "Lebanon", + "numeric": "422" + }, + { + "alpha_2": "LC", + "alpha_3": "LCA", + "name": "Saint Lucia", + "numeric": "662" + }, + { + "alpha_2": "LI", + "alpha_3": "LIE", + "name": "Liechtenstein", + "numeric": "438" + }, + { + "alpha_2": "LK", + "alpha_3": "LKA", + "name": "Sri Lanka", + "numeric": "144" + }, + { + "alpha_2": "LR", + "alpha_3": "LBR", + "name": "Liberia", + "numeric": "430" + }, + { + "alpha_2": "LS", + "alpha_3": "LSO", + "name": "Lesotho", + "numeric": "426" + }, + { + "alpha_2": "LT", + "alpha_3": "LTU", + "name": "Lithuania", + "numeric": "440", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "LU", + "alpha_3": "LUX", + "name": "Luxembourg", + "numeric": "442", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "LV", + "alpha_3": "LVA", + "name": "Latvia", + "numeric": "428", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "LY", + "alpha_3": "LBY", + "name": "Libya", + "numeric": "434", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "MA", + "alpha_3": "MAR", + "name": "Morocco", + "numeric": "504" + }, + { + "alpha_2": "MC", + "alpha_3": "MCO", + "name": "Monaco", + "numeric": "492" + }, + { + "alpha_2": "MD", + "alpha_3": "MDA", + "name": "Moldova, Republic of", + "numeric": "498" + }, + { + "alpha_2": "ME", + "alpha_3": "MNE", + "name": "Montenegro", + "numeric": "499", + "groups": [ + "NATO" + ] + }, + { + "alpha_2": "MF", + "alpha_3": "MAF", + "name": "Saint Martin (French part)", + "numeric": "663" + }, + { + "alpha_2": "MG", + "alpha_3": "MDG", + "name": "Madagascar", + "numeric": "450" + }, + { + "alpha_2": "MH", + "alpha_3": "MHL", + "name": "Marshall Islands", + "numeric": "584" + }, + { + "alpha_2": "MK", + "alpha_3": "MKD", + "name": "North Macedonia", + "numeric": "807", + "groups": [ + "NATO" + ] + }, + { + "alpha_2": "ML", + "alpha_3": "MLI", + "name": "Mali", + "numeric": "466" + }, + { + "alpha_2": "MM", + "alpha_3": "MMR", + "name": "Myanmar", + "numeric": "104" + }, + { + "alpha_2": "MN", + "alpha_3": "MNG", + "name": "Mongolia", + "numeric": "496" + }, + { + "alpha_2": "MO", + "alpha_3": "MAC", + "name": "Macao", + "numeric": "446" + }, + { + "alpha_2": "MP", + "alpha_3": "MNP", + "name": "Northern Mariana Islands", + "numeric": "580" + }, + { + "alpha_2": "MQ", + "alpha_3": "MTQ", + "name": "Martinique", + "numeric": "474" + }, + { + "alpha_2": "MR", + "alpha_3": "MRT", + "name": "Mauritania", + "numeric": "478" + }, + { + "alpha_2": "MS", + "alpha_3": "MSR", + "name": "Montserrat", + "numeric": "500" + }, + { + "alpha_2": "MT", + "alpha_3": "MLT", + "name": "Malta", + "numeric": "470", + "groups": [ + "EU" + ] + }, + { + "alpha_2": "MU", + "alpha_3": "MUS", + "name": "Mauritius", + "numeric": "480" + }, + { + "alpha_2": "MV", + "alpha_3": "MDV", + "name": "Maldives", + "numeric": "462" + }, + { + "alpha_2": "MW", + "alpha_3": "MWI", + "name": "Malawi", + "numeric": "454" + }, + { + "alpha_2": "MX", + "alpha_3": "MEX", + "name": "Mexico", + "numeric": "484", + "groups": [ + "G20", + "OECD" + ] + }, + { + "alpha_2": "MY", + "alpha_3": "MYS", + "name": "Malaysia", + "numeric": "458" + }, + { + "alpha_2": "MZ", + "alpha_3": "MOZ", + "name": "Mozambique", + "numeric": "508" + }, + { + "alpha_2": "NA", + "alpha_3": "NAM", + "name": "Namibia", + "numeric": "516" + }, + { + "alpha_2": "NC", + "alpha_3": "NCL", + "name": "New Caledonia", + "numeric": "540" + }, + { + "alpha_2": "NE", + "alpha_3": "NER", + "name": "Niger", + "numeric": "562" + }, + { + "alpha_2": "NF", + "alpha_3": "NFK", + "name": "Norfolk Island", + "numeric": "574" + }, + { + "alpha_2": "NG", + "alpha_3": "NGA", + "name": "Nigeria", + "numeric": "566", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "NI", + "alpha_3": "NIC", + "name": "Nicaragua", + "numeric": "558" + }, + { + "alpha_2": "NL", + "alpha_3": "NLD", + "name": "Netherlands", + "numeric": "528", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "NO", + "alpha_3": "NOR", + "name": "Norway", + "numeric": "578", + "groups": [ + "NATO", + "OECD" + ] + }, + { + "alpha_2": "NP", + "alpha_3": "NPL", + "name": "Nepal", + "numeric": "524" + }, + { + "alpha_2": "NR", + "alpha_3": "NRU", + "name": "Nauru", + "numeric": "520" + }, + { + "alpha_2": "NU", + "alpha_3": "NIU", + "name": "Niue", + "numeric": "570" + }, + { + "alpha_2": "NZ", + "alpha_3": "NZL", + "name": "New Zealand", + "numeric": "554", + "groups": [ + "OECD" + ] + }, + { + "alpha_2": "OM", + "alpha_3": "OMN", + "name": "Oman", + "numeric": "512" + }, + { + "alpha_2": "PA", + "alpha_3": "PAN", + "name": "Panama", + "numeric": "591" + }, + { + "alpha_2": "PE", + "alpha_3": "PER", + "name": "Peru", + "numeric": "604" + }, + { + "alpha_2": "PF", + "alpha_3": "PYF", + "name": "French Polynesia", + "numeric": "258" + }, + { + "alpha_2": "PG", + "alpha_3": "PNG", + "name": "Papua New Guinea", + "numeric": "598" + }, + { + "alpha_2": "PH", + "alpha_3": "PHL", + "name": "Philippines", + "numeric": "608" + }, + { + "alpha_2": "PK", + "alpha_3": "PAK", + "name": "Pakistan", + "numeric": "586" + }, + { + "alpha_2": "PL", + "alpha_3": "POL", + "name": "Poland", + "numeric": "616", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "PM", + "alpha_3": "SPM", + "name": "Saint Pierre and Miquelon", + "numeric": "666" + }, + { + "alpha_2": "PN", + "alpha_3": "PCN", + "name": "Pitcairn", + "numeric": "612" + }, + { + "alpha_2": "PR", + "alpha_3": "PRI", + "name": "Puerto Rico", + "numeric": "630" + }, + { + "alpha_2": "PS", + "alpha_3": "PSE", + "name": "Palestine, State of", + "numeric": "275" + }, + { + "alpha_2": "PT", + "alpha_3": "PRT", + "name": "Portugal", + "numeric": "620", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "PW", + "alpha_3": "PLW", + "name": "Palau", + "numeric": "585" + }, + { + "alpha_2": "PY", + "alpha_3": "PRY", + "name": "Paraguay", + "numeric": "600" + }, + { + "alpha_2": "QA", + "alpha_3": "QAT", + "name": "Qatar", + "numeric": "634" + }, + { + "alpha_2": "RE", + "alpha_3": "REU", + "name": "Reunion", + "numeric": "638" + }, + { + "alpha_2": "RO", + "alpha_3": "ROU", + "name": "Romania", + "numeric": "642", + "groups": [ + "EU", + "NATO" + ] + }, + { + "alpha_2": "RS", + "alpha_3": "SRB", + "name": "Serbia", + "numeric": "688" + }, + { + "alpha_2": "RU", + "alpha_3": "RUS", + "name": "Russian Federation", + "numeric": "643", + "groups": [ + "BRICS", + "G20" + ] + }, + { + "alpha_2": "RW", + "alpha_3": "RWA", + "name": "Rwanda", + "numeric": "646" + }, + { + "alpha_2": "SA", + "alpha_3": "SAU", + "name": "Saudi Arabia", + "numeric": "682", + "groups": [ + "G20", + "OPEC" + ] + }, + { + "alpha_2": "SB", + "alpha_3": "SLB", + "name": "Solomon Islands", + "numeric": "090" + }, + { + "alpha_2": "SC", + "alpha_3": "SYC", + "name": "Seychelles", + "numeric": "690" + }, + { + "alpha_2": "SD", + "alpha_3": "SDN", + "name": "Sudan", + "numeric": "729" + }, + { + "alpha_2": "SE", + "alpha_3": "SWE", + "name": "Sweden", + "numeric": "752", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "SG", + "alpha_3": "SGP", + "name": "Singapore", + "numeric": "702" + }, + { + "alpha_2": "SH", + "alpha_3": "SHN", + "name": "Saint Helena, Ascension and Tristan da Cunha", + "numeric": "654" + }, + { + "alpha_2": "SI", + "alpha_3": "SVN", + "name": "Slovenia", + "numeric": "705", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "SJ", + "alpha_3": "SJM", + "name": "Svalbard and Jan Mayen", + "numeric": "744" + }, + { + "alpha_2": "SK", + "alpha_3": "SVK", + "name": "Slovakia", + "numeric": "703", + "groups": [ + "EU", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "SL", + "alpha_3": "SLE", + "name": "Sierra Leone", + "numeric": "694" + }, + { + "alpha_2": "SM", + "alpha_3": "SMR", + "name": "San Marino", + "numeric": "674" + }, + { + "alpha_2": "SN", + "alpha_3": "SEN", + "name": "Senegal", + "numeric": "686" + }, + { + "alpha_2": "SO", + "alpha_3": "SOM", + "name": "Somalia", + "numeric": "706" + }, + { + "alpha_2": "SR", + "alpha_3": "SUR", + "name": "Suriname", + "numeric": "740" + }, + { + "alpha_2": "SS", + "alpha_3": "SSD", + "name": "South Sudan", + "numeric": "728" + }, + { + "alpha_2": "ST", + "alpha_3": "STP", + "name": "Sao Tome and Principe", + "numeric": "678" + }, + { + "alpha_2": "SV", + "alpha_3": "SLV", + "name": "El Salvador", + "numeric": "222" + }, + { + "alpha_2": "SX", + "alpha_3": "SXM", + "name": "Sint Maarten (Dutch part)", + "numeric": "534" + }, + { + "alpha_2": "SY", + "alpha_3": "SYR", + "name": "Syrian Arab Republic", + "numeric": "760" + }, + { + "alpha_2": "SZ", + "alpha_3": "SWZ", + "name": "Eswatini", + "numeric": "748" + }, + { + "alpha_2": "TC", + "alpha_3": "TCA", + "name": "Turks and Caicos Islands", + "numeric": "796" + }, + { + "alpha_2": "TD", + "alpha_3": "TCD", + "name": "Chad", + "numeric": "148" + }, + { + "alpha_2": "TF", + "alpha_3": "ATF", + "name": "French Southern Territories", + "numeric": "260" + }, + { + "alpha_2": "TG", + "alpha_3": "TGO", + "name": "Togo", + "numeric": "768" + }, + { + "alpha_2": "TH", + "alpha_3": "THA", + "name": "Thailand", + "numeric": "764" + }, + { + "alpha_2": "TJ", + "alpha_3": "TJK", + "name": "Tajikistan", + "numeric": "762" + }, + { + "alpha_2": "TK", + "alpha_3": "TKL", + "name": "Tokelau", + "numeric": "772" + }, + { + "alpha_2": "TL", + "alpha_3": "TLS", + "name": "Timor-Leste", + "numeric": "626" + }, + { + "alpha_2": "TM", + "alpha_3": "TKM", + "name": "Turkmenistan", + "numeric": "795" + }, + { + "alpha_2": "TN", + "alpha_3": "TUN", + "name": "Tunisia", + "numeric": "788" + }, + { + "alpha_2": "TO", + "alpha_3": "TON", + "name": "Tonga", + "numeric": "776" + }, + { + "alpha_2": "TR", + "alpha_3": "TUR", + "name": "Turkiye", + "numeric": "792", + "groups": [ + "G20", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "TT", + "alpha_3": "TTO", + "name": "Trinidad and Tobago", + "numeric": "780" + }, + { + "alpha_2": "TV", + "alpha_3": "TUV", + "name": "Tuvalu", + "numeric": "798" + }, + { + "alpha_2": "TW", + "alpha_3": "TWN", + "name": "Taiwan, Province of China", + "numeric": "158" + }, + { + "alpha_2": "TZ", + "alpha_3": "TZA", + "name": "Tanzania, United Republic of", + "numeric": "834" + }, + { + "alpha_2": "UA", + "alpha_3": "UKR", + "name": "Ukraine", + "numeric": "804" + }, + { + "alpha_2": "UG", + "alpha_3": "UGA", + "name": "Uganda", + "numeric": "800" + }, + { + "alpha_2": "UM", + "alpha_3": "UMI", + "name": "United States Minor Outlying Islands", + "numeric": "581" + }, + { + "alpha_2": "US", + "alpha_3": "USA", + "name": "United States", + "numeric": "840", + "groups": [ + "G20", + "G7", + "NATO", + "OECD" + ] + }, + { + "alpha_2": "UY", + "alpha_3": "URY", + "name": "Uruguay", + "numeric": "858" + }, + { + "alpha_2": "UZ", + "alpha_3": "UZB", + "name": "Uzbekistan", + "numeric": "860" + }, + { + "alpha_2": "VA", + "alpha_3": "VAT", + "name": "Holy See (Vatican City State)", + "numeric": "336" + }, + { + "alpha_2": "VC", + "alpha_3": "VCT", + "name": "Saint Vincent and the Grenadines", + "numeric": "670" + }, + { + "alpha_2": "VE", + "alpha_3": "VEN", + "name": "Venezuela, Bolivarian Republic of", + "numeric": "862", + "groups": [ + "OPEC" + ] + }, + { + "alpha_2": "VG", + "alpha_3": "VGB", + "name": "Virgin Islands, British", + "numeric": "092" + }, + { + "alpha_2": "VI", + "alpha_3": "VIR", + "name": "Virgin Islands, U.S.", + "numeric": "850" + }, + { + "alpha_2": "VN", + "alpha_3": "VNM", + "name": "Viet Nam", + "numeric": "704" + }, + { + "alpha_2": "VU", + "alpha_3": "VUT", + "name": "Vanuatu", + "numeric": "548" + }, + { + "alpha_2": "WF", + "alpha_3": "WLF", + "name": "Wallis and Futuna", + "numeric": "876" + }, + { + "alpha_2": "WS", + "alpha_3": "WSM", + "name": "Samoa", + "numeric": "882" + }, + { + "alpha_2": "YE", + "alpha_3": "YEM", + "name": "Yemen", + "numeric": "887" + }, + { + "alpha_2": "YT", + "alpha_3": "MYT", + "name": "Mayotte", + "numeric": "175" + }, + { + "alpha_2": "ZA", + "alpha_3": "ZAF", + "name": "South Africa", + "numeric": "710", + "groups": [ + "BRICS", + "G20" + ] + }, + { + "alpha_2": "ZM", + "alpha_3": "ZMB", + "name": "Zambia", + "numeric": "894" + }, + { + "alpha_2": "ZW", + "alpha_3": "ZWE", + "name": "Zimbabwe", + "numeric": "716" + } ] } diff --git a/openbb_platform/core/openbb_core/provider/utils/country_utils.py b/openbb_platform/core/openbb_core/provider/utils/country_utils.py index aeed69bcb1a..be7ac1bbfad 100644 --- a/openbb_platform/core/openbb_core/provider/utils/country_utils.py +++ b/openbb_platform/core/openbb_core/provider/utils/country_utils.py @@ -3,20 +3,58 @@ This module provides a Country type that inherits from str for type checker compatibility while providing full access to ISO 3166 country data via a static dataset. -Providers can access alpha_2, alpha_3, name, and numeric properties as needed. +Providers can access alpha_2, alpha_3, name, numeric, and groups properties as needed. + +Supported membership groups: + - G7: Group of Seven major advanced economies + - G20: Group of Twenty major economies + - EU: European Union member states + - NATO: North Atlantic Treaty Organization members + - OECD: Organisation for Economic Co-operation and Development members + - OPEC: Organization of the Petroleum Exporting Countries + - BRICS: Brazil, Russia, India, China, South Africa (+ 2024 expansion) References: - ISO 3166-1: https://en.wikipedia.org/wiki/ISO_3166-1 + - G7: https://en.wikipedia.org/wiki/G7 + - G20: https://en.wikipedia.org/wiki/G20 + - EU: https://european-union.europa.eu/principles-countries-history/eu-countries_en + - NATO: https://www.nato.int/en/about-us/organization/nato-member-countries + - OECD: https://en.wikipedia.org/wiki/OECD + - OPEC: https://en.wikipedia.org/wiki/OPEC + - BRICS: https://en.wikipedia.org/wiki/BRICS """ import json +import unicodedata from pathlib import Path -from typing import Any +from typing import Any, TypedDict from pydantic_core import core_schema -def _load_country_data() -> dict[str, dict[str, str]]: +class CountryData(TypedDict, total=False): + """Type definition for country data dictionary.""" + + alpha_2: str + alpha_3: str + name: str + numeric: str + groups: list[str] + + +_COMMON_ALIASES: dict[str, list[str]] = { + "TR": ["turkey"], +} + + +def _strip_accents(text: str) -> str: + """Strip diacritical marks from text (e.g., Curaçao -> Curacao).""" + nfd = unicodedata.normalize("NFD", text) + return "".join(c for c in nfd if not unicodedata.combining(c)) + + +def _load_country_data() -> dict[str, CountryData]: """Load country data from JSON and build lookup indices. Returns a dict with lookup keys (alpha_2, alpha_3, name variants) mapping to country data. @@ -25,24 +63,30 @@ def _load_country_data() -> dict[str, dict[str, str]]: with open(data_path, encoding="utf-8") as f: data = json.load(f) - lookup: dict[str, dict[str, str]] = {} + lookup: dict[str, CountryData] = {} for country in data["countries"]: - # Index by alpha_2 (case-insensitive) lookup[country["alpha_2"].upper()] = country lookup[country["alpha_2"].lower()] = country - # Index by alpha_3 (case-insensitive) lookup[country["alpha_3"].upper()] = country lookup[country["alpha_3"].lower()] = country - # Index by name (case-insensitive) name_lower = country["name"].lower() lookup[name_lower] = country lookup[country["name"]] = country - # Index by snake_case name + ascii_lower = _strip_accents(name_lower) + if ascii_lower != name_lower: + lookup[ascii_lower] = country + + for alias in _COMMON_ALIASES.get(country["alpha_2"], []): + lookup[alias] = country + snake_name = name_lower.replace(" ", "_").replace(",", "").replace("'", "") lookup[snake_name] = country + ascii_snake = _strip_accents(snake_name) + if ascii_snake != snake_name: + lookup[ascii_snake] = country return lookup @@ -74,9 +118,13 @@ class Country(str): 'USA' >>> c.name 'United States' + >>> c.groups + ['G7', 'G20', 'NATO', 'OECD'] + >>> c.is_member_of("G7") + True """ - _country_data: dict[str, str] + _country_data: CountryData def __new__(cls, value: Any) -> "Country": """Create a new Country instance. @@ -109,7 +157,7 @@ class Country(str): return instance @staticmethod - def _lookup_country(value: Any) -> dict[str, str]: + def _lookup_country(value: Any) -> CountryData: """Look up a country from various input formats. Parameters @@ -119,8 +167,8 @@ class Country(str): Returns ------- - dict[str, str] - The country data dictionary with alpha_2, alpha_3, name, numeric. + CountryData + The country data dictionary with alpha_2, alpha_3, name, numeric, groups. Raises ------ @@ -129,19 +177,20 @@ class Country(str): """ val = str(value).strip() - # Convert lower_snake_case to lookup key if "_" in val: val = val.replace("_", " ") - # Try direct lookup lookup_key = val.lower() if lookup_key in _COUNTRY_LOOKUP: return _COUNTRY_LOOKUP[lookup_key] - # Try original case if val in _COUNTRY_LOOKUP: return _COUNTRY_LOOKUP[val] + ascii_key = _strip_accents(lookup_key) + if ascii_key in _COUNTRY_LOOKUP: + return _COUNTRY_LOOKUP[ascii_key] + raise ValueError( f"Invalid country: '{value}'. " "Accepts ISO 3166-1 alpha-2 codes (e.g., 'US'), " @@ -169,6 +218,44 @@ class Country(str): """ISO 3166-1 numeric code (e.g., '840'), if available.""" return self._country_data.get("numeric") + @property + def groups(self) -> list[str]: + """List of membership groups this country belongs to. + + Available groups: G7, G20, EU, NATO, OECD, OPEC, BRICS. + + Examples + -------- + >>> c = Country("US") + >>> c.groups + ['G7', 'G20', 'NATO', 'OECD'] + """ + return self._country_data.get("groups", []) + + def is_member_of(self, group: str) -> bool: + """Check if country is a member of a specific group. + + Parameters + ---------- + group : str + The group to check membership for (e.g., 'G7', 'EU', 'NATO'). + Case-insensitive. + + Returns + ------- + bool + True if the country is a member of the group. + + Examples + -------- + >>> c = Country("Germany") + >>> c.is_member_of("G7") + True + >>> c.is_member_of("OPEC") + False + """ + return group.upper() in [g.upper() for g in self.groups] + @classmethod def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> Any: """Return the Pydantic core schema for validation.""" diff --git a/openbb_platform/core/openbb_core/provider/utils/update_country_data.py b/openbb_platform/core/openbb_core/provider/utils/update_country_data.py new file mode 100644 index 00000000000..c3767abcef8 --- /dev/null +++ b/openbb_platform/core/openbb_core/provider/utils/update_country_data.py @@ -0,0 +1,607 @@ +# ruff: noqa: T201 +"""Developer script to regenerate country_data.json from authoritative sources. + +Sources: + - ISO 3166-1: pycountry library + - G7: https://en.wikipedia.org/wiki/G7 + - G20: https://en.wikipedia.org/wiki/G20 + - EU: https://european-union.europa.eu/principles-countries-history/eu-countries_en + - NATO: https://en.wikipedia.org/wiki/Member_states_of_NATO + - OECD: https://en.wikipedia.org/wiki/OECD + - OPEC: https://en.wikipedia.org/wiki/OPEC + - BRICS: https://en.wikipedia.org/wiki/BRICS + +Usage: + python update_country_data.py [--dry-run] [--output PATH] + +Requirements: + pip install pycountry beautifulsoup4 requests +""" + +from __future__ import annotations + +import argparse +import json +import re +import unicodedata +from datetime import date +from pathlib import Path + +import requests + +try: + import pycountry + from bs4 import BeautifulSoup +except ImportError as e: + missing_package = str(e).split("'")[1] if "'" in str(e) else "unknown" + raise ImportError( + f"Missing required package: {missing_package}. " + "Please install dependencies: pip install pycountry beautifulsoup4 requests" + ) from e + +SCRIPT_DIR = Path(__file__).parent +DEFAULT_OUTPUT = SCRIPT_DIR / "country_data.json" + +HEADERS = { + "User-Agent": ( + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" + ) +} + +# Expected member counts for sanity checks (approximate — update when membership changes) +EXPECTED_COUNTS: dict[str, int | tuple[int, int]] = { + "G7": 7, + "G20": 20, # 19 countries + EU (we only count countries) + "EU": 27, + "NATO": 32, + "OECD": 38, + "OPEC": (12, 14), # fluctuates + "BRICS": (9, 11), # 2024 expansion + Indonesia 2025 +} + +# ── Name normalization ────────────────────────────────────────────────────── +# Map common display names → pycountry names or alpha_2 codes +NAME_OVERRIDES: dict[str, str | None] = { + "brunei": "BN", + "bolivia": "BO", + "congo": "CG", + "democratic republic of the congo": "CD", + "côte d'ivoire": "CI", + "cote d'ivoire": "CI", + "ivory coast": "CI", + "czech republic": "CZ", + "czechia": "CZ", + "eswatini": "SZ", + "swaziland": "SZ", + "iran": "IR", + "south korea": "KR", + "korea": "KR", + "korea, republic of": "KR", + "republic of korea": "KR", + "north korea": "KP", + "north macedonia": "MK", + "laos": "LA", + "moldova": "MD", + "russia": "RU", + "russian federation": "RU", + "syria": "SY", + "taiwan": "TW", + "tanzania": "TZ", + "türkiye": "TR", + "turkey": "TR", + "united states of america": "US", + "united states": "US", + "usa": "US", + "united kingdom": "GB", + "uk": "GB", + "venezuela": "VE", + "vietnam": "VN", + "palestine": "PS", + "the netherlands": "NL", + "netherlands": "NL", + "uae": "AE", + "united arab emirates": "AE", + "saudi arabia": "SA", + "south africa": "ZA", + "european union": None, # skip — not a country + "african union": None, +} + + +def _strip_accents(text: str) -> str: + """Strip diacritical marks (e.g., Côte d'Ivoire -> Cote d'Ivoire).""" + nfd = unicodedata.normalize("NFD", text) + return "".join(c for c in nfd if not unicodedata.combining(c)) + + +def _build_name_index() -> dict[str, str]: + """Build lowercase name → alpha_2 lookup from pycountry + overrides.""" + idx: dict[str, str] = {} + for c in pycountry.countries: + idx[c.name.lower()] = c.alpha_2 # type: ignore + if hasattr(c, "common_name"): + idx[c.common_name.lower()] = c.alpha_2 # type: ignore + if hasattr(c, "official_name"): + idx[c.official_name.lower()] = c.alpha_2 # type: ignore + idx.update(NAME_OVERRIDES) # type: ignore + return idx + + +NAME_INDEX = _build_name_index() + + +def resolve_country(name: str) -> str | None: + """Resolve a display name to ISO alpha_2, or None if not a country.""" + clean = re.sub(r"\[.*?\]", "", name).strip() # remove wiki refs like [1] + clean = re.sub(r"\(.*?\)", "", clean).strip() # remove parentheticals + clean = clean.strip("* \t\n") + key = clean.lower() + + if key in NAME_INDEX: + return NAME_INDEX[key] + + # Try pycountry fuzzy search as fallback + try: + results = pycountry.countries.search_fuzzy(clean) + if results: + return results[0].alpha_2 # type: ignore + except LookupError: + pass + + return None + + +# ── Scrapers ──────────────────────────────────────────────────────────────── + + +def fetch_soup(url: str) -> BeautifulSoup: + """Fetch a URL and return parsed BeautifulSoup.""" + resp = requests.get(url, headers=HEADERS, timeout=30) + resp.raise_for_status() + return BeautifulSoup(resp.text, "html.parser") + + +def scrape_g7() -> set[str]: + """Scrape G7 members from Wikipedia. + + G7 membership is extremely stable (no changes since Russia left in 2014), + so we use the infobox and validate against hardcoded list. + """ + soup = fetch_soup("https://en.wikipedia.org/wiki/G7") + hardcoded = {"CA", "FR", "DE", "IT", "JP", "GB", "US"} + + members = set() + # Look for the infobox — it has a row labeled "Members" with country links + infobox = soup.find("table", class_="infobox") + if infobox: + for tr in infobox.find_all("tr"): + th = tr.find("th") + if th and "member" in th.get_text().lower(): + td = tr.find("td") + if td: + for li in td.find_all("li"): + a = li.find("a") + if a: + code = resolve_country(a.get_text()) + if code: + members.add(code) + + return members if 6 <= len(members) <= 9 else hardcoded + + +def scrape_g20() -> set[str]: + """Scrape G20 members from Wikipedia.""" + soup = fetch_soup("https://en.wikipedia.org/wiki/G20") + hardcoded = { + "AR", + "AU", + "BR", + "CA", + "CN", + "FR", + "DE", + "IN", + "ID", + "IT", + "JP", + "KR", + "MX", + "RU", + "SA", + "ZA", + "TR", + "GB", + "US", + } + + members = set() + # Find the member states section + for header in soup.find_all(["h2", "h3"]): + if "member" in header.get_text().lower(): + # Scan the next sibling elements for a table or list + for sib in header.find_next_siblings(): + if sib.name in ("h2", "h3"): + break + for a in sib.find_all("a"): + code = resolve_country(a.get_text()) + if code: + members.add(code) + + # Filter to reasonable count (G20 has 19 country members + EU) + return members if len(members) >= 19 else hardcoded + + +def scrape_eu() -> set[str]: + """Scrape EU member states from Wikipedia's member states table.""" + hardcoded = { + "AT", + "BE", + "BG", + "HR", + "CY", + "CZ", + "DK", + "EE", + "FI", + "FR", + "DE", + "GR", + "HU", + "IE", + "IT", + "LV", + "LT", + "LU", + "MT", + "NL", + "PL", + "PT", + "RO", + "SK", + "SI", + "ES", + "SE", + } + + soup = fetch_soup( + "https://en.wikipedia.org/wiki/Member_state_of_the_European_Union" + ) + members = set() + + # Find the first wikitable sortable — this is the member states table + table = soup.find("table", class_="wikitable sortable") + if table: + for row in table.find_all("tr")[1:]: # skip header + cells = row.find_all("td") + if not cells: + continue + # Country name is typically in the first cell with a link + a = cells[0].find("a") + if a: + code = resolve_country(a.get_text()) + if code: + members.add(code) + + return members if 25 <= len(members) <= 30 else hardcoded + + +def scrape_nato() -> set[str]: + """Scrape NATO member states from Wikipedia.""" + soup = fetch_soup("https://en.wikipedia.org/wiki/Member_states_of_NATO") + members = set() + + for table in soup.find_all("table", class_="wikitable"): + prev_header = table.find_previous("h2") + if not prev_header or "member" not in prev_header.get_text().lower(): + continue + for row in table.find_all("tr"): + cells = row.find_all("td") + if not cells: + continue + # First cell with a link is usually the country + for a in cells[0].find_all("a"): + code = resolve_country(a.get_text()) + if code: + members.add(code) + break + + hardcoded = { + "AL", + "BE", + "BG", + "CA", + "HR", + "CZ", + "DK", + "EE", + "FI", + "FR", + "DE", + "GR", + "HU", + "IS", + "IT", + "LV", + "LT", + "LU", + "ME", + "NL", + "MK", + "NO", + "PL", + "PT", + "RO", + "SK", + "SI", + "ES", + "SE", + "TR", + "GB", + "US", + } + return members if len(members) >= 30 else hardcoded + + +def scrape_oecd() -> set[str]: + """Scrape OECD member states from Wikipedia.""" + soup = fetch_soup("https://en.wikipedia.org/wiki/OECD") + members = set() + + # Find "Member countries" section + for header in soup.find_all(["h2", "h3"]): + htext = header.get_text().lower() + if "member" in htext and "country" in htext or "countries" in htext: + for sib in header.find_next_siblings(): + if sib.name in ("h2", "h3"): + break + for a in sib.find_all("a"): + code = resolve_country(a.get_text()) + if code: + members.add(code) + break + + hardcoded = { + "AU", + "AT", + "BE", + "CA", + "CL", + "CO", + "CR", + "CZ", + "DK", + "EE", + "FI", + "FR", + "DE", + "GR", + "HU", + "IS", + "IE", + "IL", + "IT", + "JP", + "KR", + "LV", + "LT", + "LU", + "MX", + "NL", + "NZ", + "NO", + "PL", + "PT", + "SK", + "SI", + "ES", + "SE", + "CH", + "TR", + "GB", + "US", + } + return members if len(members) >= 35 else hardcoded + + +def scrape_opec() -> set[str]: + """Scrape OPEC member states from Wikipedia.""" + soup = fetch_soup("https://en.wikipedia.org/wiki/OPEC") + members = set() + + # Find "Current members" or "Member countries" section + for header in soup.find_all(["h2", "h3"]): + htext = header.get_text().lower() + if ("current" in htext and "member" in htext) or ( + "member" in htext and ("country" in htext or "countries" in htext) + ): + for sib in header.find_next_siblings(): + if sib.name in ("h2", "h3"): + break + for a in sib.find_all("a"): + code = resolve_country(a.get_text()) + if code: + members.add(code) + if members: + break + + hardcoded = { + "DZ", + "CG", + "GQ", + "GA", + "IR", + "IQ", + "KW", + "LY", + "NG", + "SA", + "AE", + "VE", + } + return members if len(members) >= 10 else hardcoded + + +def scrape_brics() -> set[str]: + """Scrape BRICS member states from Wikipedia.""" + soup = fetch_soup("https://en.wikipedia.org/wiki/BRICS") + members = set() + + # Find "Member states" section + for header in soup.find_all(["h2", "h3"]): + if "member" in header.get_text().lower(): + for sib in header.find_next_siblings(): + if sib.name in ("h2", "h3"): + break + # Look in tables for member states + if sib.name == "table": + for row in sib.find_all("tr"): + for a in row.find_all("a"): + code = resolve_country(a.get_text()) + if code: + members.add(code) + for a in sib.find_all("a"): + code = resolve_country(a.get_text()) + if code: + members.add(code) + if members: + break + + # 2024 expansion: Egypt, Ethiopia, Iran, UAE + Indonesia (Jan 2025) + hardcoded = {"BR", "RU", "IN", "CN", "ZA", "EG", "ET", "IR", "AE", "ID"} + return members if len(members) >= 9 else hardcoded + + +# ── Main logic ────────────────────────────────────────────────────────────── + +GROUP_SCRAPERS = { + "G7": scrape_g7, + "G20": scrape_g20, + "EU": scrape_eu, + "NATO": scrape_nato, + "OECD": scrape_oecd, + "OPEC": scrape_opec, + "BRICS": scrape_brics, +} + + +def build_country_data() -> dict: + """Build the complete country_data.json structure.""" + # 1. Scrape all group memberships + group_members: dict[str, set[str]] = {} + for group_name, scraper in GROUP_SCRAPERS.items(): + print(f"Scraping {group_name}...", end=" ", flush=True) + try: + members = scraper() + group_members[group_name] = members + print(f"✅ {len(members)} members") + except Exception as e: + print(f"❌ Error: {e}") + group_members[group_name] = set() + + # 2. Sanity checks + print("\n── Sanity Checks ──") + for group_name, members in group_members.items(): + expected = EXPECTED_COUNTS.get(group_name) + count = len(members) + if expected is None: + continue + if isinstance(expected, tuple): + lo, hi = expected + ok = lo <= count <= hi + expected_str = f"{lo}-{hi}" + elif isinstance(expected, int): + ok = abs(count - expected) <= 2 # allow ±2 tolerance + expected_str = str(expected) + status = "✅" if ok else "⚠️" + print(f" {status} {group_name}: got {count}, expected ~{expected_str}") + if not ok: + print(f" Members: {sorted(members)}") + + # 3. Build alpha_2 → groups mapping + country_groups: dict[str, list[str]] = {} + for group_name, members in group_members.items(): + for alpha_2 in members: + country_groups.setdefault(alpha_2, []).append(group_name) + + # Sort group lists for consistency + for groups in country_groups.values(): + groups.sort() + + # 4. Build country list from pycountry + countries = [] + for c in sorted(pycountry.countries, key=lambda x: x.alpha_2): # type: ignore + entry: dict = { + "alpha_2": c.alpha_2, # type: ignore + "alpha_3": c.alpha_3, # type: ignore + "name": _strip_accents(c.name), # type: ignore + "numeric": c.numeric, # type: ignore + } + groups = country_groups.get(c.alpha_2) # type: ignore + if groups: + entry["groups"] = groups + countries.append(entry) + + return { + "_last_updated": date.today().isoformat(), + "_sources": { + "iso_3166": "pycountry library", + "G7": "https://en.wikipedia.org/wiki/G7", + "G20": "https://en.wikipedia.org/wiki/G20", + "EU": "https://european-union.europa.eu/principles-countries-history/eu-countries_en", + "NATO": "https://en.wikipedia.org/wiki/Member_states_of_NATO", + "OECD": "https://en.wikipedia.org/wiki/OECD", + "OPEC": "https://en.wikipedia.org/wiki/OPEC", + "BRICS": "https://en.wikipedia.org/wiki/BRICS", + }, + "countries": countries, + } + + +def main(): + """CLI entrypoint to regenerate country_data.json.""" + parser = argparse.ArgumentParser( + description="Regenerate country_data.json from authoritative sources." + ) + parser.add_argument( + "--output", + "-o", + type=Path, + default=DEFAULT_OUTPUT, + help=f"Output path (default: {DEFAULT_OUTPUT})", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Print summary without writing file.", + ) + args = parser.parse_args() + + print("🌍 Updating country_data.json\n") + data = build_country_data() + + countries_with_groups = sum(1 for c in data["countries"] if "groups" in c) + total = len(data["countries"]) + print("\n── Summary ──") + print(f" Total countries: {total}") + print(f" Countries with group memberships: {countries_with_groups}") + print(f" Last updated: {data['_last_updated']}") + + if args.dry_run: + print("\n[DRY RUN] Would write to:", args.output) + # Print first 5 entries as sample + print("\nSample (first 5 entries):") + sample = { + "_last_updated": data["_last_updated"], + "countries": data["countries"][:5], + } + print(json.dumps(sample, indent=2, ensure_ascii=False)) + else: + with open(args.output, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + f.write("\n") + print(f"\n✅ Written to: {args.output}") + + +if __name__ == "__main__": + main() diff --git a/openbb_platform/core/tests/provider/utils/test_country_utils.py b/openbb_platform/core/tests/provider/utils/test_country_utils.py index 20182fd637c..84c1fcf6c70 100644 --- a/openbb_platform/core/tests/provider/utils/test_country_utils.py +++ b/openbb_platform/core/tests/provider/utils/test_country_utils.py @@ -97,3 +97,95 @@ class TestCountry: def test_country_param_alias(self): """CountryParam should be an alias for Country.""" assert CountryParam is Country + + def test_groups_property(self): + """Country should expose membership groups.""" + c = Country("US") + groups = c.groups + assert isinstance(groups, list) + assert "G7" in groups + assert "G20" in groups + assert "NATO" in groups + assert "OECD" in groups + # US is not a member of these + assert "EU" not in groups + assert "OPEC" not in groups + + def test_groups_empty_for_non_member(self): + """Countries not in any tracked group should have empty groups list.""" + # Most small countries won't be in any group + c = Country("Fiji") + assert c.groups == [] + + def test_is_member_of(self): + """is_member_of should check group membership.""" + c = Country("Germany") + assert c.is_member_of("G7") is True + assert c.is_member_of("EU") is True + assert c.is_member_of("NATO") is True + assert c.is_member_of("OECD") is True + assert c.is_member_of("OPEC") is False + assert c.is_member_of("BRICS") is False + + def test_is_member_of_case_insensitive(self): + """is_member_of should be case-insensitive.""" + c = Country("FR") + assert c.is_member_of("g7") is True + assert c.is_member_of("G7") is True + assert c.is_member_of("Eu") is True + + def test_opec_members(self): + """OPEC members should be correctly identified.""" + opec_countries = ["SA", "AE", "KW", "IQ", "IR", "VE", "NG"] + for code in opec_countries: + c = Country(code) + assert c.is_member_of("OPEC") is True, f"{code} should be OPEC member" + + def test_brics_members(self): + """BRICS members should be correctly identified.""" + brics_countries = ["BR", "RU", "IN", "CN", "ZA"] + for code in brics_countries: + c = Country(code) + assert c.is_member_of("BRICS") is True, f"{code} should be BRICS member" + + def test_eu_members(self): + """EU members should be correctly identified.""" + eu_countries = ["DE", "FR", "IT", "ES", "NL", "BE", "PL"] + for code in eu_countries: + c = Country(code) + assert c.is_member_of("EU") is True, f"{code} should be EU member" + c = Country("GB") + assert c.is_member_of("EU") is False + + @pytest.mark.parametrize( + "input_name, expected_alpha2", + [ + ("Curacao", "CW"), + ("Reunion", "RE"), + ("Aland Islands", "AX"), + ("Saint Barthelemy", "BL"), + ("Turkiye", "TR"), + ("Turkey", "TR"), + ("Cote d'Ivoire", "CI"), + ], + ) + def test_ascii_normalized_names(self, input_name, expected_alpha2): + """ASCII variants of accented country names should resolve correctly.""" + c = Country(input_name) + assert c.alpha_2 == expected_alpha2 + + @pytest.mark.parametrize( + "input_name, expected_alpha2", + [ + ("Curaçao", "CW"), + ("Réunion", "RE"), + ("Åland Islands", "AX"), + ("Saint Barthélemy", "BL"), + ("Türkiye", "TR"), + ("Côte d'Ivoire", "CI"), + ], + ) + def test_accented_names(self, input_name, expected_alpha2): + """Accented country names should resolve correctly.""" + c = Country(input_name) + assert c.alpha_2 == expected_alpha2