trading-platform-database-v2/ddl/schemas/data_sources/tables/002_ticker_mapping.sql
rckrdmrd 9f8bbb7494 [DDL] feat: Sprint 4 - Add data_sources and ml_predictions schemas
- data_sources schema:
  - api_providers: Proveedores de datos de mercado
  - ticker_mapping: Mapeo de simbolos entre proveedores
  - data_sync_status: Estado de sincronizacion de datos

- ml schema additions:
  - range_predictions: Predicciones de rango (particionada)
  - entry_signals: Señales de entrada con metodologias ICT/SMC/AMD
  - market_analysis: Analisis de mercado (sesgo, estructura, volatilidad)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 20:08:51 -06:00

197 lines
6.8 KiB
PL/PgSQL

-- ============================================================================
-- SCHEMA: data_sources
-- TABLE: ticker_mapping
-- DESCRIPTION: Mapeo de simbolos entre proveedores y sistema interno
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 4 - DDL Implementation Roadmap Q1-2026
-- ============================================================================
-- Tabla de Mapeo de Tickers
CREATE TABLE IF NOT EXISTS data_sources.ticker_mapping (
-- Identificadores
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
-- Referencias
provider_id UUID NOT NULL REFERENCES data_sources.api_providers(id) ON DELETE CASCADE,
ticker_id UUID REFERENCES market_data.tickers(id),
symbol_id UUID REFERENCES trading.symbols(id),
-- Simbolos
internal_symbol VARCHAR(20) NOT NULL, -- Simbolo interno: EURUSD
provider_symbol VARCHAR(50) NOT NULL, -- Simbolo del provider: EUR/USD, EURUSD=X
provider_code VARCHAR(50) NOT NULL, -- Codigo del proveedor
-- Tipo de activo
asset_class VARCHAR(30) NOT NULL, -- 'forex', 'crypto', 'stock', 'index', 'commodity'
instrument_type VARCHAR(30), -- 'spot', 'future', 'option', 'cfd'
-- Configuracion de precision
price_precision INTEGER NOT NULL DEFAULT 5, -- Decimales de precio
lot_precision INTEGER NOT NULL DEFAULT 2, -- Decimales de lote
min_lot_size DECIMAL(10, 4) DEFAULT 0.01,
max_lot_size DECIMAL(10, 4) DEFAULT 100,
lot_step DECIMAL(10, 4) DEFAULT 0.01,
-- Factor de ajuste
price_multiplier DECIMAL(10, 6) DEFAULT 1.0, -- Para convertir precio del provider
inverse_quote BOOLEAN NOT NULL DEFAULT FALSE, -- Si el provider da inverso
-- Monedas
base_currency VARCHAR(10), -- EUR
quote_currency VARCHAR(10), -- USD
-- Estado
is_active BOOLEAN NOT NULL DEFAULT TRUE,
is_verified BOOLEAN NOT NULL DEFAULT FALSE, -- Verificado manualmente
-- Horarios de trading
trading_hours JSONB, -- [{"day": 0, "open": "00:00", "close": "23:59"}]
timezone VARCHAR(50) DEFAULT 'UTC',
-- Estadisticas de uso
last_data_at TIMESTAMPTZ,
data_points_count BIGINT NOT NULL DEFAULT 0,
error_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
-- Metadata
metadata JSONB DEFAULT '{}'::JSONB,
notes TEXT,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
verified_at TIMESTAMPTZ,
verified_by UUID REFERENCES users.users(id),
-- Constraints
CONSTRAINT ticker_mapping_unique UNIQUE (tenant_id, provider_id, internal_symbol),
CONSTRAINT ticker_mapping_provider_symbol_unique UNIQUE (provider_id, provider_symbol)
);
COMMENT ON TABLE data_sources.ticker_mapping IS
'Mapeo de simbolos entre proveedores externos y nomenclatura interna';
COMMENT ON COLUMN data_sources.ticker_mapping.price_multiplier IS
'Factor para convertir precio del proveedor a precio interno. Ej: 1.0 para EURUSD, 0.01 para JPY pairs';
COMMENT ON COLUMN data_sources.ticker_mapping.inverse_quote IS
'TRUE si el proveedor reporta USD/EUR en lugar de EUR/USD';
-- Indices
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_tenant
ON data_sources.ticker_mapping(tenant_id);
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_provider
ON data_sources.ticker_mapping(provider_id);
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_internal
ON data_sources.ticker_mapping(internal_symbol);
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_provider_symbol
ON data_sources.ticker_mapping(provider_symbol);
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_active
ON data_sources.ticker_mapping(provider_id, is_active)
WHERE is_active = TRUE;
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_asset
ON data_sources.ticker_mapping(asset_class);
CREATE INDEX IF NOT EXISTS idx_ticker_mapping_ticker
ON data_sources.ticker_mapping(ticker_id)
WHERE ticker_id IS NOT NULL;
-- Trigger para updated_at
DROP TRIGGER IF EXISTS ticker_mapping_updated_at ON data_sources.ticker_mapping;
CREATE TRIGGER ticker_mapping_updated_at
BEFORE UPDATE ON data_sources.ticker_mapping
FOR EACH ROW
EXECUTE FUNCTION data_sources.update_timestamp();
-- Funcion para obtener simbolo del proveedor
CREATE OR REPLACE FUNCTION data_sources.get_provider_symbol(
p_tenant_id UUID,
p_provider_code VARCHAR,
p_internal_symbol VARCHAR
)
RETURNS data_sources.ticker_mapping AS $$
DECLARE
v_mapping data_sources.ticker_mapping;
BEGIN
SELECT tm.* INTO v_mapping
FROM data_sources.ticker_mapping tm
JOIN data_sources.api_providers ap ON tm.provider_id = ap.id
WHERE tm.tenant_id = p_tenant_id
AND ap.code = p_provider_code
AND tm.internal_symbol = p_internal_symbol
AND tm.is_active = TRUE;
RETURN v_mapping;
END;
$$ LANGUAGE plpgsql;
-- Funcion para convertir precio del proveedor a interno
CREATE OR REPLACE FUNCTION data_sources.convert_provider_price(
p_mapping_id UUID,
p_provider_price DECIMAL
)
RETURNS DECIMAL AS $$
DECLARE
v_mapping data_sources.ticker_mapping;
v_converted DECIMAL;
BEGIN
SELECT * INTO v_mapping FROM data_sources.ticker_mapping WHERE id = p_mapping_id;
IF v_mapping IS NULL THEN
RETURN p_provider_price;
END IF;
v_converted := p_provider_price * v_mapping.price_multiplier;
IF v_mapping.inverse_quote THEN
v_converted := 1.0 / v_converted;
END IF;
RETURN ROUND(v_converted, v_mapping.price_precision);
END;
$$ LANGUAGE plpgsql;
-- Vista de mappings activos por proveedor
CREATE OR REPLACE VIEW data_sources.v_ticker_mappings AS
SELECT
tm.id,
tm.tenant_id,
ap.name AS provider_name,
ap.code AS provider_code,
tm.internal_symbol,
tm.provider_symbol,
tm.asset_class,
tm.price_precision,
tm.price_multiplier,
tm.inverse_quote,
tm.is_active,
tm.is_verified,
tm.last_data_at,
tm.data_points_count
FROM data_sources.ticker_mapping tm
JOIN data_sources.api_providers ap ON tm.provider_id = ap.id
WHERE tm.is_active = TRUE
ORDER BY tm.internal_symbol, ap.priority;
-- RLS Policies
ALTER TABLE data_sources.ticker_mapping ENABLE ROW LEVEL SECURITY;
CREATE POLICY ticker_mapping_tenant_isolation ON data_sources.ticker_mapping
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON data_sources.ticker_mapping TO trading_app;
GRANT SELECT ON data_sources.ticker_mapping TO trading_readonly;
GRANT SELECT ON data_sources.v_ticker_mappings TO trading_app;
GRANT EXECUTE ON FUNCTION data_sources.get_provider_symbol TO trading_app;
GRANT EXECUTE ON FUNCTION data_sources.convert_provider_price TO trading_app;