-- ============================================================================ -- SCHEMA: broker_integration -- TABLE: spread_statistics, price_adjustment_model -- DESCRIPTION: Estadisticas de spread y modelos de ajuste de precio -- VERSION: 1.0.0 -- CREATED: 2026-01-16 -- SPRINT: Sprint 6 - DDL Implementation Roadmap Q1-2026 -- ============================================================================ -- Tabla de Estadisticas de Spread CREATE TABLE IF NOT EXISTS broker_integration.spread_statistics ( -- Identificadores id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, broker_account_id UUID REFERENCES broker_integration.broker_accounts(id) ON DELETE CASCADE, -- Simbolo y periodo symbol VARCHAR(20) NOT NULL, period_start TIMESTAMPTZ NOT NULL, period_end TIMESTAMPTZ NOT NULL, period_type VARCHAR(20) NOT NULL, -- 'hourly', 'daily', 'session' -- Estadisticas de spread spread_min DECIMAL(10, 4), spread_max DECIMAL(10, 4), spread_avg DECIMAL(10, 4), spread_median DECIMAL(10, 4), spread_stddev DECIMAL(10, 4), spread_percentile_95 DECIMAL(10, 4), -- Spread por sesion spread_asia_avg DECIMAL(10, 4), spread_london_avg DECIMAL(10, 4), spread_ny_avg DECIMAL(10, 4), -- Frecuencia de widening widening_events INTEGER NOT NULL DEFAULT 0, max_widening_factor DECIMAL(10, 2), -- Samples sample_count INTEGER NOT NULL DEFAULT 0, trading_hours_covered INTEGER, -- Horas de trading cubiertas -- Comparacion historica spread_vs_7d_avg DECIMAL(10, 4), -- % vs promedio 7 dias spread_vs_30d_avg DECIMAL(10, 4), -- % vs promedio 30 dias -- Metadata metadata JSONB DEFAULT '{}'::JSONB, -- Timestamps calculated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Constraints CONSTRAINT spread_stats_unique UNIQUE (broker_account_id, symbol, period_start, period_type) ); COMMENT ON TABLE broker_integration.spread_statistics IS 'Estadisticas historicas de spread por simbolo, broker y periodo'; -- Indices CREATE INDEX IF NOT EXISTS idx_spread_stats_account ON broker_integration.spread_statistics(broker_account_id); CREATE INDEX IF NOT EXISTS idx_spread_stats_symbol ON broker_integration.spread_statistics(symbol, period_start DESC); CREATE INDEX IF NOT EXISTS idx_spread_stats_period ON broker_integration.spread_statistics(period_type, period_start DESC); -- ============================================================================ -- TABLE: price_adjustment_model -- ============================================================================ -- Tabla de Modelos de Ajuste de Precio CREATE TABLE IF NOT EXISTS broker_integration.price_adjustment_model ( -- Identificadores id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, -- Simbolo symbol VARCHAR(20) NOT NULL, -- Tipo de modelo model_type VARCHAR(50) NOT NULL DEFAULT 'spread_based', -- Parametros del modelo base_spread_pips DECIMAL(10, 4) NOT NULL DEFAULT 1, slippage_avg_pips DECIMAL(10, 4) NOT NULL DEFAULT 0.2, slippage_max_pips DECIMAL(10, 4) NOT NULL DEFAULT 2, -- Ajustes por sesion asia_adjustment DECIMAL(5, 4) DEFAULT 1.2, -- Multiplicador london_adjustment DECIMAL(5, 4) DEFAULT 1.0, ny_adjustment DECIMAL(5, 4) DEFAULT 1.0, overlap_adjustment DECIMAL(5, 4) DEFAULT 0.9, -- London-NY overlap -- Ajustes por volatilidad low_volatility_adjustment DECIMAL(5, 4) DEFAULT 0.8, high_volatility_adjustment DECIMAL(5, 4) DEFAULT 1.5, extreme_volatility_adjustment DECIMAL(5, 4) DEFAULT 2.5, -- Ajustes por horario market_open_adjustment DECIMAL(5, 4) DEFAULT 1.5, market_close_adjustment DECIMAL(5, 4) DEFAULT 1.3, news_event_adjustment DECIMAL(5, 4) DEFAULT 2.0, -- Limites max_total_adjustment DECIMAL(5, 2) DEFAULT 5.0, -- Max pips de ajuste total min_acceptable_spread DECIMAL(10, 4), max_acceptable_spread DECIMAL(10, 4), -- Estado is_active BOOLEAN NOT NULL DEFAULT TRUE, -- Validez valid_from TIMESTAMPTZ NOT NULL DEFAULT NOW(), valid_until TIMESTAMPTZ, -- Performance accuracy_score DECIMAL(5, 2), -- 0-100 last_accuracy_check TIMESTAMPTZ, predictions_count INTEGER NOT NULL DEFAULT 0, accurate_predictions INTEGER NOT NULL DEFAULT 0, -- Metadata training_data_range JSONB, -- {"start": "...", "end": "..."} metadata JSONB DEFAULT '{}'::JSONB, notes TEXT, -- Timestamps created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), -- Constraints CONSTRAINT price_adj_model_unique UNIQUE (tenant_id, symbol, model_type) ); COMMENT ON TABLE broker_integration.price_adjustment_model IS 'Modelos para ajustar precios de entrada considerando spread y slippage'; -- Indices CREATE INDEX IF NOT EXISTS idx_price_adj_symbol ON broker_integration.price_adjustment_model(symbol); CREATE INDEX IF NOT EXISTS idx_price_adj_active ON broker_integration.price_adjustment_model(symbol, is_active) WHERE is_active = TRUE; -- Funcion para calcular ajuste de precio CREATE OR REPLACE FUNCTION broker_integration.calculate_price_adjustment( p_symbol VARCHAR, p_direction VARCHAR, p_current_price DECIMAL, p_session VARCHAR DEFAULT NULL, p_volatility VARCHAR DEFAULT 'normal' ) RETURNS TABLE ( adjusted_entry DECIMAL, adjusted_sl DECIMAL, adjusted_tp DECIMAL, total_adjustment_pips DECIMAL, adjustment_breakdown JSONB ) AS $$ DECLARE v_model broker_integration.price_adjustment_model; v_adjustment DECIMAL; v_session_mult DECIMAL; v_volatility_mult DECIMAL; BEGIN SELECT * INTO v_model FROM broker_integration.price_adjustment_model WHERE symbol = p_symbol AND is_active = TRUE AND (valid_until IS NULL OR valid_until > NOW()) ORDER BY valid_from DESC LIMIT 1; IF NOT FOUND THEN -- Retornar sin ajuste RETURN QUERY SELECT p_current_price, p_current_price, p_current_price, 0::DECIMAL, '{}'::JSONB; RETURN; END IF; -- Determinar multiplicador de sesion v_session_mult := CASE p_session WHEN 'asia' THEN v_model.asia_adjustment WHEN 'london' THEN v_model.london_adjustment WHEN 'ny' THEN v_model.ny_adjustment WHEN 'overlap' THEN v_model.overlap_adjustment ELSE 1.0 END; -- Determinar multiplicador de volatilidad v_volatility_mult := CASE p_volatility WHEN 'low' THEN v_model.low_volatility_adjustment WHEN 'high' THEN v_model.high_volatility_adjustment WHEN 'extreme' THEN v_model.extreme_volatility_adjustment ELSE 1.0 END; -- Calcular ajuste total en pips v_adjustment := (v_model.base_spread_pips + v_model.slippage_avg_pips) * v_session_mult * v_volatility_mult; -- Limitar ajuste v_adjustment := LEAST(v_adjustment, v_model.max_total_adjustment); -- Convertir a precio v_adjustment := v_adjustment * 0.0001; -- 1 pip = 0.0001 RETURN QUERY SELECT CASE p_direction WHEN 'long' THEN p_current_price + v_adjustment ELSE p_current_price - v_adjustment END, p_current_price, -- SL sin ajustar p_current_price, -- TP sin ajustar v_adjustment * 10000, -- En pips jsonb_build_object( 'base_spread', v_model.base_spread_pips, 'slippage', v_model.slippage_avg_pips, 'session_multiplier', v_session_mult, 'volatility_multiplier', v_volatility_mult ); END; $$ LANGUAGE plpgsql; -- Trigger para updated_at DROP TRIGGER IF EXISTS price_adj_updated_at ON broker_integration.price_adjustment_model; CREATE TRIGGER price_adj_updated_at BEFORE UPDATE ON broker_integration.price_adjustment_model FOR EACH ROW EXECUTE FUNCTION broker_integration.update_broker_timestamp(); -- RLS ALTER TABLE broker_integration.spread_statistics ENABLE ROW LEVEL SECURITY; ALTER TABLE broker_integration.price_adjustment_model ENABLE ROW LEVEL SECURITY; CREATE POLICY spread_stats_tenant ON broker_integration.spread_statistics FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); CREATE POLICY price_adj_tenant ON broker_integration.price_adjustment_model FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); -- Grants GRANT SELECT, INSERT, UPDATE, DELETE ON broker_integration.spread_statistics TO trading_app; GRANT SELECT, INSERT, UPDATE, DELETE ON broker_integration.price_adjustment_model TO trading_app; GRANT SELECT ON broker_integration.spread_statistics TO trading_readonly; GRANT SELECT ON broker_integration.price_adjustment_model TO trading_readonly; GRANT EXECUTE ON FUNCTION broker_integration.calculate_price_adjustment TO trading_app;