- 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>
407 lines
14 KiB
PL/PgSQL
407 lines
14 KiB
PL/PgSQL
-- ============================================================================
|
|
-- SCHEMA: ml
|
|
-- TABLE: market_analysis
|
|
-- DESCRIPTION: Analisis de mercado generado por ML (sesgo, volatilidad, estructura)
|
|
-- VERSION: 1.0.0
|
|
-- CREATED: 2026-01-16
|
|
-- SPRINT: Sprint 4 - DDL Implementation Roadmap Q1-2026
|
|
-- ============================================================================
|
|
|
|
-- Enum para tipo de analisis
|
|
DO $$ BEGIN
|
|
CREATE TYPE ml.analysis_type AS ENUM (
|
|
'daily_bias', -- Sesgo diario
|
|
'weekly_outlook', -- Perspectiva semanal
|
|
'session_analysis', -- Analisis de sesion
|
|
'structure_analysis', -- Analisis de estructura
|
|
'volatility_forecast', -- Pronostico de volatilidad
|
|
'correlation_report', -- Reporte de correlaciones
|
|
'sentiment_analysis', -- Analisis de sentimiento
|
|
'fundamental_impact', -- Impacto fundamental
|
|
'comprehensive' -- Analisis completo
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para sesgo de mercado
|
|
DO $$ BEGIN
|
|
CREATE TYPE ml.market_bias AS ENUM (
|
|
'strong_bullish', -- Fuertemente alcista
|
|
'bullish', -- Alcista
|
|
'slightly_bullish', -- Ligeramente alcista
|
|
'neutral', -- Neutral
|
|
'slightly_bearish', -- Ligeramente bajista
|
|
'bearish', -- Bajista
|
|
'strong_bearish' -- Fuertemente bajista
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para regimen de volatilidad
|
|
DO $$ BEGIN
|
|
CREATE TYPE ml.volatility_regime AS ENUM (
|
|
'extremely_low', -- Extremadamente baja
|
|
'low', -- Baja
|
|
'normal', -- Normal
|
|
'high', -- Alta
|
|
'extreme', -- Extrema
|
|
'transitioning' -- En transicion
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Enum para estructura de mercado
|
|
DO $$ BEGIN
|
|
CREATE TYPE ml.market_structure AS ENUM (
|
|
'uptrend_strong', -- Tendencia alcista fuerte
|
|
'uptrend_weak', -- Tendencia alcista debil
|
|
'downtrend_strong', -- Tendencia bajista fuerte
|
|
'downtrend_weak', -- Tendencia bajista debil
|
|
'ranging_tight', -- Rango estrecho
|
|
'ranging_wide', -- Rango amplio
|
|
'consolidating', -- Consolidando
|
|
'breaking_out', -- Rompiendo
|
|
'reversing' -- Revirtiendo
|
|
);
|
|
EXCEPTION
|
|
WHEN duplicate_object THEN null;
|
|
END $$;
|
|
|
|
-- Tabla de Analisis de Mercado
|
|
CREATE TABLE IF NOT EXISTS ml.market_analysis (
|
|
-- Identificadores
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL,
|
|
|
|
-- Simbolo y scope
|
|
symbol VARCHAR(20) NOT NULL,
|
|
asset_class ml.asset_class NOT NULL DEFAULT 'FOREX',
|
|
analysis_type ml.analysis_type NOT NULL,
|
|
timeframe VARCHAR(10) NOT NULL DEFAULT 'D1',
|
|
|
|
-- Periodo del analisis
|
|
analysis_date DATE NOT NULL,
|
|
valid_from TIMESTAMPTZ NOT NULL,
|
|
valid_until TIMESTAMPTZ NOT NULL,
|
|
|
|
-- Sesgo y direccion
|
|
bias ml.market_bias NOT NULL,
|
|
bias_confidence DECIMAL(5, 4) NOT NULL DEFAULT 0.5
|
|
CHECK (bias_confidence BETWEEN 0 AND 1),
|
|
bias_reasoning TEXT,
|
|
|
|
-- Estructura de mercado
|
|
structure ml.market_structure NOT NULL,
|
|
structure_timeframe VARCHAR(10), -- TF de la estructura dominante
|
|
trend_strength DECIMAL(5, 2), -- -100 a +100
|
|
|
|
-- Volatilidad
|
|
volatility_regime ml.volatility_regime NOT NULL,
|
|
current_atr DECIMAL(15, 8),
|
|
atr_percentile DECIMAL(5, 2), -- Percentil historico
|
|
expected_range_pips DECIMAL(10, 2),
|
|
volatility_forecast DECIMAL(10, 4), -- Volatilidad esperada
|
|
|
|
-- Niveles clave
|
|
key_resistance_1 DECIMAL(15, 8),
|
|
key_resistance_2 DECIMAL(15, 8),
|
|
key_resistance_3 DECIMAL(15, 8),
|
|
key_support_1 DECIMAL(15, 8),
|
|
key_support_2 DECIMAL(15, 8),
|
|
key_support_3 DECIMAL(15, 8),
|
|
pivot_point DECIMAL(15, 8),
|
|
|
|
-- Order blocks y FVGs
|
|
bullish_order_blocks JSONB DEFAULT '[]'::JSONB,
|
|
bearish_order_blocks JSONB DEFAULT '[]'::JSONB,
|
|
fair_value_gaps JSONB DEFAULT '[]'::JSONB,
|
|
liquidity_levels JSONB DEFAULT '[]'::JSONB,
|
|
|
|
-- Indicadores
|
|
indicators JSONB DEFAULT '{}'::JSONB, -- RSI, MACD, etc.
|
|
moving_averages JSONB DEFAULT '{}'::JSONB, -- SMAs, EMAs
|
|
|
|
-- Correlaciones
|
|
correlation_dxy DECIMAL(5, 4), -- Correlacion con DXY
|
|
correlation_gold DECIMAL(5, 4), -- Correlacion con oro
|
|
correlation_sp500 DECIMAL(5, 4), -- Correlacion con S&P500
|
|
correlated_pairs JSONB DEFAULT '[]'::JSONB, -- Pares correlacionados
|
|
|
|
-- Sentimiento
|
|
sentiment_score DECIMAL(5, 2), -- -100 a +100
|
|
sentiment_sources JSONB DEFAULT '{}'::JSONB, -- Fuentes de sentimiento
|
|
cot_report JSONB DEFAULT '{}'::JSONB, -- Datos COT
|
|
|
|
-- Eventos fundamentales
|
|
upcoming_events JSONB DEFAULT '[]'::JSONB, -- Eventos proximos
|
|
recent_events_impact JSONB DEFAULT '[]'::JSONB, -- Impacto de eventos recientes
|
|
high_impact_event_today BOOLEAN NOT NULL DEFAULT FALSE,
|
|
|
|
-- Escenarios
|
|
primary_scenario JSONB DEFAULT '{}'::JSONB, -- Escenario principal
|
|
secondary_scenario JSONB DEFAULT '{}'::JSONB, -- Escenario alternativo
|
|
risk_scenario JSONB DEFAULT '{}'::JSONB, -- Escenario de riesgo
|
|
|
|
-- Zonas de interes
|
|
buy_zones JSONB DEFAULT '[]'::JSONB, -- Zonas de compra
|
|
sell_zones JSONB DEFAULT '[]'::JSONB, -- Zonas de venta
|
|
avoid_zones JSONB DEFAULT '[]'::JSONB, -- Zonas a evitar
|
|
|
|
-- Modelo
|
|
model_id VARCHAR(100),
|
|
model_version VARCHAR(50) NOT NULL,
|
|
analysis_confidence DECIMAL(5, 4) NOT NULL DEFAULT 0.5
|
|
CHECK (analysis_confidence BETWEEN 0 AND 1),
|
|
|
|
-- Resumen
|
|
summary TEXT, -- Resumen ejecutivo
|
|
trading_plan TEXT, -- Plan de trading sugerido
|
|
key_insights JSONB DEFAULT '[]'::JSONB, -- Insights clave
|
|
|
|
-- Resultado (post-validacion)
|
|
actual_direction VARCHAR(20), -- Direccion real
|
|
actual_range_pips DECIMAL(10, 2),
|
|
accuracy_score DECIMAL(5, 2), -- Score de precision
|
|
validated_at TIMESTAMPTZ,
|
|
|
|
-- Metadata
|
|
tags VARCHAR(50)[],
|
|
notes TEXT,
|
|
metadata JSONB DEFAULT '{}'::JSONB,
|
|
|
|
-- Timestamps
|
|
generated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
|
|
-- Constraints
|
|
CONSTRAINT market_analysis_unique UNIQUE (tenant_id, symbol, analysis_type, analysis_date)
|
|
);
|
|
|
|
COMMENT ON TABLE ml.market_analysis IS
|
|
'Analisis de mercado generado por modelos ML: sesgo, estructura, volatilidad, niveles clave';
|
|
|
|
COMMENT ON COLUMN ml.market_analysis.bias IS
|
|
'Sesgo direccional del mercado para el periodo analizado';
|
|
|
|
COMMENT ON COLUMN ml.market_analysis.structure IS
|
|
'Estructura de mercado dominante (tendencia, rango, etc.)';
|
|
|
|
-- Indices
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_tenant
|
|
ON ml.market_analysis(tenant_id);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_symbol
|
|
ON ml.market_analysis(symbol, analysis_date DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_date
|
|
ON ml.market_analysis(analysis_date DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_type
|
|
ON ml.market_analysis(analysis_type, analysis_date DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_bias
|
|
ON ml.market_analysis(bias, analysis_date DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_active
|
|
ON ml.market_analysis(valid_until)
|
|
WHERE valid_until > NOW();
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_model
|
|
ON ml.market_analysis(model_version, analysis_date DESC);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_high_impact
|
|
ON ml.market_analysis(analysis_date, high_impact_event_today)
|
|
WHERE high_impact_event_today = TRUE;
|
|
|
|
-- GIN indices para JSONB
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_events_gin
|
|
ON ml.market_analysis USING GIN (upcoming_events);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_market_analysis_insights_gin
|
|
ON ml.market_analysis USING GIN (key_insights);
|
|
|
|
-- Trigger para updated_at
|
|
DROP TRIGGER IF EXISTS market_analysis_updated_at ON ml.market_analysis;
|
|
CREATE TRIGGER market_analysis_updated_at
|
|
BEFORE UPDATE ON ml.market_analysis
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION ml.update_ml_timestamp();
|
|
|
|
-- Funcion para obtener analisis actual
|
|
CREATE OR REPLACE FUNCTION ml.get_current_analysis(
|
|
p_tenant_id UUID,
|
|
p_symbol VARCHAR,
|
|
p_analysis_type ml.analysis_type DEFAULT 'daily_bias'
|
|
)
|
|
RETURNS ml.market_analysis AS $$
|
|
DECLARE
|
|
v_analysis ml.market_analysis;
|
|
BEGIN
|
|
SELECT * INTO v_analysis
|
|
FROM ml.market_analysis
|
|
WHERE tenant_id = p_tenant_id
|
|
AND symbol = p_symbol
|
|
AND analysis_type = p_analysis_type
|
|
AND valid_until > NOW()
|
|
ORDER BY analysis_date DESC
|
|
LIMIT 1;
|
|
|
|
RETURN v_analysis;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Funcion para obtener sesgo agregado de multiples simbolos
|
|
CREATE OR REPLACE FUNCTION ml.get_market_overview(
|
|
p_tenant_id UUID,
|
|
p_symbols VARCHAR[] DEFAULT NULL
|
|
)
|
|
RETURNS TABLE (
|
|
symbol VARCHAR,
|
|
bias ml.market_bias,
|
|
bias_confidence DECIMAL,
|
|
structure ml.market_structure,
|
|
volatility_regime ml.volatility_regime,
|
|
analysis_date DATE
|
|
) AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT DISTINCT ON (ma.symbol)
|
|
ma.symbol,
|
|
ma.bias,
|
|
ma.bias_confidence,
|
|
ma.structure,
|
|
ma.volatility_regime,
|
|
ma.analysis_date
|
|
FROM ml.market_analysis ma
|
|
WHERE ma.tenant_id = p_tenant_id
|
|
AND ma.analysis_type = 'daily_bias'
|
|
AND ma.valid_until > NOW()
|
|
AND (p_symbols IS NULL OR ma.symbol = ANY(p_symbols))
|
|
ORDER BY ma.symbol, ma.analysis_date DESC;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Funcion para validar analisis con datos reales
|
|
CREATE OR REPLACE FUNCTION ml.validate_market_analysis(
|
|
p_analysis_id UUID,
|
|
p_actual_high DECIMAL,
|
|
p_actual_low DECIMAL,
|
|
p_actual_close DECIMAL
|
|
)
|
|
RETURNS VOID AS $$
|
|
DECLARE
|
|
v_analysis ml.market_analysis;
|
|
v_direction VARCHAR;
|
|
v_accuracy DECIMAL;
|
|
BEGIN
|
|
SELECT * INTO v_analysis FROM ml.market_analysis WHERE id = p_analysis_id;
|
|
|
|
IF NOT FOUND THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- Determinar direccion real
|
|
IF p_actual_close > (p_actual_high + p_actual_low) / 2 THEN
|
|
v_direction := 'bullish';
|
|
ELSE
|
|
v_direction := 'bearish';
|
|
END IF;
|
|
|
|
-- Calcular accuracy basado en sesgo predicho vs real
|
|
v_accuracy := CASE
|
|
WHEN v_analysis.bias IN ('strong_bullish', 'bullish', 'slightly_bullish') AND v_direction = 'bullish' THEN 100
|
|
WHEN v_analysis.bias IN ('strong_bearish', 'bearish', 'slightly_bearish') AND v_direction = 'bearish' THEN 100
|
|
WHEN v_analysis.bias = 'neutral' THEN 50
|
|
ELSE 0
|
|
END;
|
|
|
|
UPDATE ml.market_analysis
|
|
SET actual_direction = v_direction,
|
|
actual_range_pips = (p_actual_high - p_actual_low) * 10000,
|
|
accuracy_score = v_accuracy,
|
|
validated_at = NOW()
|
|
WHERE id = p_analysis_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Vista de analisis activos
|
|
CREATE OR REPLACE VIEW ml.v_current_market_analysis AS
|
|
SELECT
|
|
id,
|
|
tenant_id,
|
|
symbol,
|
|
asset_class,
|
|
analysis_type,
|
|
analysis_date,
|
|
bias,
|
|
bias_confidence,
|
|
structure,
|
|
volatility_regime,
|
|
expected_range_pips,
|
|
key_resistance_1,
|
|
key_support_1,
|
|
pivot_point,
|
|
sentiment_score,
|
|
high_impact_event_today,
|
|
summary,
|
|
analysis_confidence,
|
|
valid_until
|
|
FROM ml.market_analysis
|
|
WHERE valid_until > NOW()
|
|
ORDER BY analysis_date DESC, symbol;
|
|
|
|
-- Vista de rendimiento de analisis
|
|
CREATE OR REPLACE VIEW ml.v_market_analysis_performance AS
|
|
SELECT
|
|
symbol,
|
|
analysis_type,
|
|
model_version,
|
|
COUNT(*) AS total_analyses,
|
|
ROUND(AVG(accuracy_score)::NUMERIC, 2) AS avg_accuracy,
|
|
ROUND(AVG(bias_confidence)::NUMERIC, 4) AS avg_confidence,
|
|
COUNT(*) FILTER (WHERE accuracy_score >= 70) AS accurate_count,
|
|
COUNT(*) FILTER (WHERE accuracy_score < 30) AS inaccurate_count,
|
|
ROUND((COUNT(*) FILTER (WHERE accuracy_score >= 70)::DECIMAL
|
|
/ NULLIF(COUNT(*) FILTER (WHERE validated_at IS NOT NULL), 0) * 100), 2) AS accuracy_rate
|
|
FROM ml.market_analysis
|
|
WHERE validated_at IS NOT NULL
|
|
GROUP BY symbol, analysis_type, model_version
|
|
ORDER BY accuracy_rate DESC NULLS LAST;
|
|
|
|
-- Vista de correlaciones actuales
|
|
CREATE OR REPLACE VIEW ml.v_market_correlations AS
|
|
SELECT
|
|
symbol,
|
|
analysis_date,
|
|
correlation_dxy,
|
|
correlation_gold,
|
|
correlation_sp500,
|
|
bias,
|
|
structure
|
|
FROM ml.market_analysis
|
|
WHERE analysis_type = 'daily_bias'
|
|
AND valid_until > NOW()
|
|
AND (correlation_dxy IS NOT NULL OR correlation_gold IS NOT NULL)
|
|
ORDER BY analysis_date DESC, symbol;
|
|
|
|
-- RLS Policies
|
|
ALTER TABLE ml.market_analysis ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY market_analysis_tenant ON ml.market_analysis
|
|
FOR ALL
|
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
|
|
|
-- Grants
|
|
GRANT SELECT, INSERT, UPDATE ON ml.market_analysis TO trading_app;
|
|
GRANT SELECT ON ml.market_analysis TO trading_readonly;
|
|
GRANT SELECT ON ml.v_current_market_analysis TO trading_app;
|
|
GRANT SELECT ON ml.v_market_analysis_performance TO trading_app;
|
|
GRANT SELECT ON ml.v_market_correlations TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION ml.get_current_analysis TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION ml.get_market_overview TO trading_app;
|
|
GRANT EXECUTE ON FUNCTION ml.validate_market_analysis TO trading_app;
|