trading-platform-database/ddl/schemas/financial/tables/07-currency_exchange_rates.sql

132 lines
4.3 KiB
PL/PgSQL

-- =====================================================
-- ORBIQUANT IA - CURRENCY EXCHANGE RATES TABLE
-- =====================================================
-- Description: Historical exchange rates for multi-currency support
-- Schema: financial
-- =====================================================
CREATE TABLE financial.currency_exchange_rates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Par de monedas
from_currency financial.currency_code NOT NULL,
to_currency financial.currency_code NOT NULL,
-- Tasa de cambio
rate DECIMAL(18,8) NOT NULL,
-- Fuente de datos
source VARCHAR(100) NOT NULL DEFAULT 'manual', -- manual, api, stripe, coinbase, etc.
provider VARCHAR(100), -- nombre del proveedor si es API
-- Validez temporal
valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
valid_to TIMESTAMPTZ,
-- Metadata
metadata JSONB DEFAULT '{}',
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Constraints
CONSTRAINT positive_rate CHECK (rate > 0),
CONSTRAINT different_currencies CHECK (from_currency != to_currency),
CONSTRAINT valid_dates_order CHECK (
valid_to IS NULL OR valid_to > valid_from
),
CONSTRAINT unique_rate_period UNIQUE(from_currency, to_currency, valid_from)
);
-- Indexes
CREATE INDEX idx_cer_currencies ON financial.currency_exchange_rates(from_currency, to_currency);
CREATE INDEX idx_cer_valid_from ON financial.currency_exchange_rates(valid_from DESC);
CREATE INDEX idx_cer_valid_period ON financial.currency_exchange_rates(from_currency, to_currency, valid_from DESC)
WHERE valid_to IS NULL OR valid_to > NOW();
CREATE INDEX idx_cer_source ON financial.currency_exchange_rates(source);
-- Comments
COMMENT ON TABLE financial.currency_exchange_rates IS 'Historical exchange rates for currency conversion';
COMMENT ON COLUMN financial.currency_exchange_rates.rate IS 'Exchange rate: 1 from_currency = rate * to_currency';
COMMENT ON COLUMN financial.currency_exchange_rates.source IS 'Source of exchange rate data';
COMMENT ON COLUMN financial.currency_exchange_rates.valid_from IS 'Start of rate validity period';
COMMENT ON COLUMN financial.currency_exchange_rates.valid_to IS 'End of rate validity period (NULL = currently valid)';
COMMENT ON COLUMN financial.currency_exchange_rates.metadata IS 'Additional rate metadata (bid, ask, spread, etc.)';
-- Función helper para obtener tasa de cambio actual
CREATE OR REPLACE FUNCTION financial.get_exchange_rate(
p_from_currency financial.currency_code,
p_to_currency financial.currency_code,
p_at_time TIMESTAMPTZ DEFAULT NOW()
)
RETURNS DECIMAL(18,8)
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
v_rate DECIMAL(18,8);
BEGIN
-- Si son la misma moneda, retornar 1
IF p_from_currency = p_to_currency THEN
RETURN 1.0;
END IF;
-- Buscar tasa de cambio válida
SELECT rate INTO v_rate
FROM financial.currency_exchange_rates
WHERE from_currency = p_from_currency
AND to_currency = p_to_currency
AND valid_from <= p_at_time
AND (valid_to IS NULL OR valid_to > p_at_time)
ORDER BY valid_from DESC
LIMIT 1;
-- Si no se encuentra, intentar inversa
IF v_rate IS NULL THEN
SELECT 1.0 / rate INTO v_rate
FROM financial.currency_exchange_rates
WHERE from_currency = p_to_currency
AND to_currency = p_from_currency
AND valid_from <= p_at_time
AND (valid_to IS NULL OR valid_to > p_at_time)
ORDER BY valid_from DESC
LIMIT 1;
END IF;
-- Si aún no hay tasa, retornar NULL
RETURN v_rate;
END;
$$;
COMMENT ON FUNCTION financial.get_exchange_rate IS 'Get exchange rate between currencies at specific time';
-- Función para convertir montos
CREATE OR REPLACE FUNCTION financial.convert_currency(
p_amount DECIMAL,
p_from_currency financial.currency_code,
p_to_currency financial.currency_code,
p_at_time TIMESTAMPTZ DEFAULT NOW()
)
RETURNS DECIMAL(20,8)
LANGUAGE plpgsql
STABLE
AS $$
DECLARE
v_rate DECIMAL(18,8);
BEGIN
-- Obtener tasa de cambio
v_rate := financial.get_exchange_rate(p_from_currency, p_to_currency, p_at_time);
-- Si no hay tasa, retornar NULL
IF v_rate IS NULL THEN
RETURN NULL;
END IF;
-- Convertir y retornar
RETURN p_amount * v_rate;
END;
$$;
COMMENT ON FUNCTION financial.convert_currency IS 'Convert amount between currencies at specific time';