- 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>
306 lines
10 KiB
PL/PgSQL
306 lines
10 KiB
PL/PgSQL
-- ============================================================================
|
|
-- SCHEMA: data_sources
|
|
-- TABLE: api_providers
|
|
-- DESCRIPTION: Proveedores de datos de mercado (Polygon, Alpha Vantage, etc.)
|
|
-- VERSION: 1.0.0
|
|
-- CREATED: 2026-01-16
|
|
-- SPRINT: Sprint 4 - DDL Implementation Roadmap Q1-2026
|
|
-- ============================================================================
|
|
|
|
-- Crear schema si no existe
|
|
CREATE SCHEMA IF NOT EXISTS data_sources;
|
|
|
|
COMMENT ON SCHEMA data_sources IS
|
|
'Fuentes de datos de mercado y proveedores de API';
|
|
|
|
-- Enum para tipo de proveedor
|
|
DO $$ BEGIN
|
|
CREATE TYPE data_sources.provider_type AS ENUM (
|
|
'market_data', -- Datos de mercado (OHLCV, quotes)
|
|
'news', -- Noticias financieras
|
|
'fundamental', -- Datos fundamentales
|
|
'sentiment', -- Analisis de sentimiento
|
|
'economic_calendar', -- Calendario economico
|
|
'alternative' -- Datos alternativos
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para estado del proveedor
|
|
DO $$ BEGIN
|
|
CREATE TYPE data_sources.provider_status AS ENUM (
|
|
'active', -- Activo y funcionando
|
|
'inactive', -- Inactivo temporalmente
|
|
'degraded', -- Funcionando con problemas
|
|
'rate_limited', -- Limitado por rate limit
|
|
'maintenance', -- En mantenimiento
|
|
'error', -- Con errores
|
|
'deprecated' -- Deprecado
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para frecuencia de datos
|
|
DO $$ BEGIN
|
|
CREATE TYPE data_sources.data_frequency AS ENUM (
|
|
'tick', -- Tick por tick
|
|
'second', -- Por segundo
|
|
'minute', -- Por minuto
|
|
'five_minutes', -- Cada 5 minutos
|
|
'fifteen_minutes', -- Cada 15 minutos
|
|
'hourly', -- Cada hora
|
|
'daily', -- Diario
|
|
'weekly', -- Semanal
|
|
'monthly' -- Mensual
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Tabla de Proveedores de API
|
|
CREATE TABLE IF NOT EXISTS data_sources.api_providers (
|
|
-- Identificadores
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Informacion del proveedor
|
|
name VARCHAR(100) NOT NULL,
|
|
code VARCHAR(50) NOT NULL, -- 'polygon', 'alpha_vantage', 'binance'
|
|
type data_sources.provider_type NOT NULL,
|
|
status data_sources.provider_status NOT NULL DEFAULT 'inactive',
|
|
|
|
-- Configuracion de API
|
|
base_url TEXT NOT NULL,
|
|
api_version VARCHAR(20),
|
|
auth_type VARCHAR(50) NOT NULL DEFAULT 'api_key', -- 'api_key', 'oauth2', 'basic', 'none'
|
|
|
|
-- Credenciales (encriptadas)
|
|
api_key_encrypted TEXT, -- Encriptado con pgcrypto
|
|
api_secret_encrypted TEXT,
|
|
additional_credentials JSONB DEFAULT '{}'::JSONB, -- Otras credenciales encriptadas
|
|
|
|
-- Rate Limiting
|
|
rate_limit_requests INTEGER, -- Requests por periodo
|
|
rate_limit_period_seconds INTEGER DEFAULT 60,
|
|
current_request_count INTEGER NOT NULL DEFAULT 0,
|
|
rate_limit_reset_at TIMESTAMPTZ,
|
|
burst_limit INTEGER, -- Limite de burst
|
|
|
|
-- Capacidades
|
|
supported_symbols JSONB DEFAULT '[]'::JSONB, -- Simbolos soportados
|
|
supported_frequencies data_sources.data_frequency[],
|
|
supports_realtime BOOLEAN NOT NULL DEFAULT FALSE,
|
|
supports_historical BOOLEAN NOT NULL DEFAULT TRUE,
|
|
max_historical_days INTEGER, -- Dias maximos de historico
|
|
|
|
-- Cobertura
|
|
asset_classes VARCHAR(50)[], -- ['forex', 'stocks', 'crypto']
|
|
exchanges VARCHAR(50)[], -- ['NYSE', 'NASDAQ', 'BINANCE']
|
|
regions VARCHAR(50)[], -- ['US', 'EU', 'LATAM']
|
|
|
|
-- Costos
|
|
cost_type VARCHAR(20) DEFAULT 'free', -- 'free', 'paid', 'freemium'
|
|
monthly_cost DECIMAL(10, 2),
|
|
cost_per_request DECIMAL(10, 6),
|
|
free_tier_limit INTEGER, -- Requests gratis por mes
|
|
|
|
-- Prioridad y fallback
|
|
priority INTEGER NOT NULL DEFAULT 100, -- Menor = mayor prioridad
|
|
fallback_provider_id UUID REFERENCES data_sources.api_providers(id),
|
|
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
|
|
|
|
-- Calidad de datos
|
|
data_quality_score DECIMAL(5, 2), -- 0-100
|
|
latency_avg_ms INTEGER,
|
|
uptime_percent DECIMAL(5, 2),
|
|
|
|
-- Health check
|
|
health_check_url TEXT,
|
|
last_health_check_at TIMESTAMPTZ,
|
|
last_health_status VARCHAR(20),
|
|
consecutive_failures INTEGER NOT NULL DEFAULT 0,
|
|
|
|
-- Estadisticas de uso
|
|
total_requests INTEGER NOT NULL DEFAULT 0,
|
|
successful_requests INTEGER NOT NULL DEFAULT 0,
|
|
failed_requests INTEGER NOT NULL DEFAULT 0,
|
|
last_request_at TIMESTAMPTZ,
|
|
last_error TEXT,
|
|
last_error_at TIMESTAMPTZ,
|
|
|
|
-- Documentacion
|
|
documentation_url TEXT,
|
|
terms_of_service_url TEXT,
|
|
support_email VARCHAR(255),
|
|
|
|
-- Metadata
|
|
metadata JSONB DEFAULT '{}'::JSONB,
|
|
notes TEXT,
|
|
|
|
-- Timestamps
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
activated_at TIMESTAMPTZ,
|
|
deactivated_at TIMESTAMPTZ,
|
|
|
|
-- Constraints
|
|
CONSTRAINT api_providers_code_unique UNIQUE (tenant_id, code),
|
|
CONSTRAINT api_providers_rate_check CHECK (rate_limit_requests IS NULL OR rate_limit_requests > 0)
|
|
);
|
|
|
|
COMMENT ON TABLE data_sources.api_providers IS
|
|
'Proveedores de datos de mercado configurados por tenant';
|
|
|
|
COMMENT ON COLUMN data_sources.api_providers.api_key_encrypted IS
|
|
'API key encriptada - usar pgcrypto para encrypt/decrypt';
|
|
|
|
COMMENT ON COLUMN data_sources.api_providers.priority IS
|
|
'Prioridad de uso - menor numero = mayor prioridad';
|
|
|
|
-- Indices
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_tenant
|
|
ON data_sources.api_providers(tenant_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_code
|
|
ON data_sources.api_providers(code);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_status
|
|
ON data_sources.api_providers(status);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_type
|
|
ON data_sources.api_providers(type);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_active
|
|
ON data_sources.api_providers(tenant_id, status, priority)
|
|
WHERE status = 'active';
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_primary
|
|
ON data_sources.api_providers(tenant_id, is_primary)
|
|
WHERE is_primary = TRUE;
|
|
|
|
-- GIN index para busqueda en asset_classes y exchanges
|
|
CREATE INDEX IF NOT EXISTS idx_api_providers_assets_gin
|
|
ON data_sources.api_providers USING GIN (asset_classes);
|
|
|
|
-- Funcion de timestamp
|
|
CREATE OR REPLACE FUNCTION data_sources.update_timestamp()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at := NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Trigger para updated_at
|
|
DROP TRIGGER IF EXISTS api_provider_updated_at ON data_sources.api_providers;
|
|
CREATE TRIGGER api_provider_updated_at
|
|
BEFORE UPDATE ON data_sources.api_providers
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION data_sources.update_timestamp();
|
|
|
|
-- Trigger para asegurar solo un proveedor primario por tipo
|
|
CREATE OR REPLACE FUNCTION data_sources.ensure_single_primary_provider()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF NEW.is_primary = TRUE THEN
|
|
UPDATE data_sources.api_providers
|
|
SET is_primary = FALSE
|
|
WHERE tenant_id = NEW.tenant_id
|
|
AND type = NEW.type
|
|
AND id != NEW.id
|
|
AND is_primary = TRUE;
|
|
END IF;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
DROP TRIGGER IF EXISTS api_provider_single_primary ON data_sources.api_providers;
|
|
CREATE TRIGGER api_provider_single_primary
|
|
BEFORE INSERT OR UPDATE OF is_primary ON data_sources.api_providers
|
|
FOR EACH ROW
|
|
WHEN (NEW.is_primary = TRUE)
|
|
EXECUTE FUNCTION data_sources.ensure_single_primary_provider();
|
|
|
|
-- Funcion para obtener proveedor activo por tipo
|
|
CREATE OR REPLACE FUNCTION data_sources.get_active_provider(
|
|
p_tenant_id UUID,
|
|
p_type data_sources.provider_type,
|
|
p_symbol VARCHAR DEFAULT NULL
|
|
)
|
|
RETURNS data_sources.api_providers AS $$
|
|
DECLARE
|
|
v_provider data_sources.api_providers;
|
|
BEGIN
|
|
SELECT * INTO v_provider
|
|
FROM data_sources.api_providers
|
|
WHERE tenant_id = p_tenant_id
|
|
AND type = p_type
|
|
AND status = 'active'
|
|
AND (p_symbol IS NULL OR p_symbol = ANY(
|
|
SELECT jsonb_array_elements_text(supported_symbols)
|
|
))
|
|
ORDER BY is_primary DESC, priority ASC
|
|
LIMIT 1;
|
|
|
|
RETURN v_provider;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Funcion para incrementar contador de requests
|
|
CREATE OR REPLACE FUNCTION data_sources.increment_request_count(
|
|
p_provider_id UUID,
|
|
p_success BOOLEAN DEFAULT TRUE
|
|
)
|
|
RETURNS VOID AS $$
|
|
BEGIN
|
|
UPDATE data_sources.api_providers
|
|
SET total_requests = total_requests + 1,
|
|
successful_requests = successful_requests + CASE WHEN p_success THEN 1 ELSE 0 END,
|
|
failed_requests = failed_requests + CASE WHEN NOT p_success THEN 1 ELSE 0 END,
|
|
current_request_count = current_request_count + 1,
|
|
last_request_at = NOW(),
|
|
consecutive_failures = CASE WHEN p_success THEN 0 ELSE consecutive_failures + 1 END
|
|
WHERE id = p_provider_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Vista de proveedores activos
|
|
CREATE OR REPLACE VIEW data_sources.v_active_providers AS
|
|
SELECT
|
|
id,
|
|
tenant_id,
|
|
name,
|
|
code,
|
|
type,
|
|
status,
|
|
is_primary,
|
|
priority,
|
|
rate_limit_requests,
|
|
current_request_count,
|
|
supports_realtime,
|
|
asset_classes,
|
|
data_quality_score,
|
|
uptime_percent,
|
|
total_requests,
|
|
last_request_at
|
|
FROM data_sources.api_providers
|
|
WHERE status = 'active'
|
|
ORDER BY is_primary DESC, priority ASC;
|
|
|
|
-- RLS Policies
|
|
ALTER TABLE data_sources.api_providers ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY api_providers_tenant_isolation ON data_sources.api_providers
|
|
FOR ALL
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
|
|
-- Grants
|
|
GRANT USAGE ON SCHEMA data_sources TO trading_app;
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON data_sources.api_providers TO trading_app;
|
|
GRANT SELECT ON data_sources.api_providers TO trading_readonly;
|
|
GRANT SELECT ON data_sources.v_active_providers TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION data_sources.get_active_provider TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION data_sources.increment_request_count TO trading_app;
|