132 lines
4.3 KiB
PL/PgSQL
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';
|