Files
sure/app/views/settings/hostings/_provider_selection.html.erb
Artem Danilov 9de9a23ce2 feat(prices): add T-Invest (T-Bank) securities + brand-logo provider (#2408)
Adds Provider::TinkoffInvest, a token-based securities provider built on the
public T-Invest REST gateway (invest-public-api.tinkoff.ru/rest). It serves
prices for Russian instruments (shares, ETF/БПИФ, bonds) and, crucially, brand
logos via the T-Invest CDN — the authoritative logo source for MOEX
instruments, which ISS (MoexPublic) does not provide.

- Registry: register `tinkoff_invest` under the :securities concept; token via
  ENV TINKOFF_INVEST_API_KEY or encrypted Setting.tinkoff_invest_api_key.
- Logos independent of the price provider: Security#import_brand_logo consults
  T-Invest for a logo whenever a token is configured (after the price-provider
  metadata fetch, so it never short-circuits website_url backfill). Gated on
  token presence, not the securities checklist.
- display_logo_url: with no website domain, a stored provider logo (T-Invest)
  now beats the ticker-only Brandfetch lettermark; when a domain exists,
  Brandfetch still wins (unchanged).
- MoexPublic no longer reports moex.com as the issuer website — it's the
  exchange, not the issuer, and would make Brandfetch render the exchange logo
  for every instrument and shadow the real brand logo.
- Prices: GetCandles (daily, paged) + GetLastPrices; Quotation units+nano/1e9;
  bonds priced as percent-of-par x nominal (missing nominal raises, not 0).
- Settings: encrypted token field (always shown) + provider checkbox + en locale.
- Tests for search/info/logo-url/prices/bond/incomplete-candle and display logic.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-19 17:07:09 +02:00

80 lines
3.8 KiB
Plaintext

<div class="space-y-6">
<%# Exchange Rate Provider - single dropdown %>
<div class="space-y-2">
<h3 class="font-medium text-sm"><%= t(".exchange_rate_title") %></h3>
<p class="text-secondary text-xs mb-2"><%= t(".exchange_rate_description") %></p>
<%= styled_form_with model: Setting.new,
url: settings_hosting_path,
method: :patch,
data: {
controller: "auto-submit-form",
"auto-submit-form-trigger-event-value": "change"
} do |form| %>
<%= form.select :exchange_rate_provider,
[
[t(".providers.twelve_data"), "twelve_data"],
[t(".providers.yahoo_finance"), "yahoo_finance"],
[t(".providers.moex_public"), "moex_public"]
],
{ label: t(".exchange_rate_provider_label") },
{
value: ENV.fetch("EXCHANGE_RATE_PROVIDER", Setting.exchange_rate_provider),
disabled: ENV["EXCHANGE_RATE_PROVIDER"].present?,
data: { "auto-submit-form-target": "auto" }
} %>
<% end %>
</div>
<%# Securities Providers - multiple checkboxes %>
<div class="space-y-2">
<h3 class="font-medium text-sm"><%= t(".securities_title") %></h3>
<p class="text-secondary text-xs mb-2"><%= t(".securities_description") %></p>
<%= styled_form_with model: Setting.new,
url: settings_hosting_path,
method: :patch,
data: {
controller: "auto-submit-form"
} do |form| %>
<% disabled = ENV["SECURITIES_PROVIDERS"].present? || ENV["SECURITIES_PROVIDER"].present? %>
<% enabled_providers = Setting.enabled_securities_providers %>
<div class="space-y-2">
<%# Hidden field to ensure empty array is submitted when all unchecked %>
<input type="hidden" name="setting[securities_providers][]" value="">
<% [
["twelve_data", t(".providers.twelve_data"), t(".twelve_data_hint")],
["yahoo_finance", t(".providers.yahoo_finance"), t(".yahoo_finance_hint")],
["tiingo", t(".providers.tiingo"), t(".requires_api_key")],
["eodhd", t(".providers.eodhd"), t(".requires_api_key_eodhd")],
["alpha_vantage", t(".providers.alpha_vantage"), t(".requires_api_key_alpha_vantage")],
["mfapi", t(".providers.mfapi"), t(".mfapi_hint")],
["binance_public", t(".providers.binance_public"), t(".binance_public_hint")],
["moex_public", t(".providers.moex_public"), t(".moex_public_hint")],
["tinkoff_invest", t(".providers.tinkoff_invest"), t(".tinkoff_invest_hint")],
].each do |value, label, hint| %>
<label class="flex items-center gap-2 cursor-pointer">
<input type="checkbox"
name="setting[securities_providers][]"
value="<%= value %>"
class="rounded border-primary text-primary focus:ring-primary"
<%= "checked" if enabled_providers.include?(value) %>
<%= "disabled" if disabled %>
data-auto-submit-form-target="auto">
<span class="text-sm"><%= label %></span>
<% if hint %>
<span class="text-xs text-secondary">(<%= hint %>)</span>
<% end %>
</label>
<% end %>
</div>
<% end %>
</div>
<% if ENV["EXCHANGE_RATE_PROVIDER"].present? || ENV["SECURITIES_PROVIDERS"].present? || ENV["SECURITIES_PROVIDER"].present? %>
<%= render DS::Alert.new(message: t(".env_configured_message"), variant: :warning) %>
<% end %>
</div>