- 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>
197 lines
6.8 KiB
PL/PgSQL
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;
|