-- ===================================================== -- MIGRATION: Add overlay columns to ml.predictions -- ===================================================== -- Date: 2026-02-03 -- Task: TASK-2026-02-03-ANALISIS-DDL-MODELADO / ST-1.4 -- Description: Complete overlay structure for ML predictions visualization -- ===================================================== -- =========================================== -- PART 1: ADD COLUMNS TO ml.predictions -- =========================================== -- Add overlay visualization data ALTER TABLE ml.predictions ADD COLUMN IF NOT EXISTS overlay_data JSONB DEFAULT '{}'; -- Add chart configuration ALTER TABLE ml.predictions ADD COLUMN IF NOT EXISTS chart_config JSONB DEFAULT '{ "show_on_chart": true, "color": "#4CAF50", "line_style": "dashed", "opacity": 0.8 }'; -- Add display priority for ordering multiple predictions ALTER TABLE ml.predictions ADD COLUMN IF NOT EXISTS display_priority INTEGER DEFAULT 0; -- =========================================== -- PART 2: INDEXES FOR OVERLAY QUERIES -- =========================================== -- Index for overlay queries CREATE INDEX IF NOT EXISTS idx_predictions_overlay ON ml.predictions(symbol, timeframe, display_priority DESC) WHERE (chart_config->>'show_on_chart')::boolean = true; -- Index for overlay data CREATE INDEX IF NOT EXISTS idx_predictions_overlay_data ON ml.predictions USING GIN (overlay_data) WHERE overlay_data IS NOT NULL AND overlay_data != '{}'; -- =========================================== -- PART 3: COLUMN COMMENTS -- =========================================== COMMENT ON COLUMN ml.predictions.overlay_data IS 'JSON data for chart overlay visualization (price levels, zones, etc)'; COMMENT ON COLUMN ml.predictions.chart_config IS 'Configuration for how to display this prediction on charts'; COMMENT ON COLUMN ml.predictions.display_priority IS 'Priority for ordering when multiple predictions exist (higher = more prominent)'; -- =========================================== -- PART 4: CREATE OVERLAY TYPE ENUM (if not exists) -- =========================================== DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'overlay_type' AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'ml')) THEN CREATE TYPE ml.overlay_type AS ENUM ( 'support_resistance', 'trend_line', 'zone', 'arrow', 'label', 'fibonacci', 'order_block', 'fair_value_gap', 'liquidity_level', 'ict_killzone' ); END IF; END $$; -- =========================================== -- PART 5: CREATE prediction_overlays TABLE -- =========================================== CREATE TABLE IF NOT EXISTS ml.prediction_overlays ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), prediction_id UUID NOT NULL REFERENCES ml.predictions(id) ON DELETE CASCADE, overlay_type ml.overlay_type NOT NULL, label VARCHAR(100), price_levels DECIMAL(18,8)[] DEFAULT '{}', time_range TSTZRANGE, time_point TIMESTAMPTZ, price_point DECIMAL(18,8), coordinates JSONB DEFAULT '[]', style_config JSONB DEFAULT '{ "color": "#4CAF50", "line_width": 1, "line_style": "solid", "fill_opacity": 0.2, "text_color": "#FFFFFF", "font_size": 12 }', metadata JSONB DEFAULT '{}', is_active BOOLEAN NOT NULL DEFAULT true, display_priority INTEGER DEFAULT 0, z_index INTEGER DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ ); -- Indexes for prediction_overlays CREATE INDEX IF NOT EXISTS idx_prediction_overlays_prediction ON ml.prediction_overlays(prediction_id); CREATE INDEX IF NOT EXISTS idx_prediction_overlays_active ON ml.prediction_overlays(prediction_id, is_active) WHERE is_active = true; CREATE INDEX IF NOT EXISTS idx_prediction_overlays_type ON ml.prediction_overlays(overlay_type); CREATE INDEX IF NOT EXISTS idx_prediction_overlays_time_range ON ml.prediction_overlays USING GIST (time_range) WHERE time_range IS NOT NULL; CREATE INDEX IF NOT EXISTS idx_prediction_overlays_priority ON ml.prediction_overlays(display_priority DESC, z_index DESC) WHERE is_active = true; CREATE INDEX IF NOT EXISTS idx_prediction_overlays_expires ON ml.prediction_overlays(expires_at) WHERE expires_at IS NOT NULL AND is_active = true; CREATE INDEX IF NOT EXISTS idx_prediction_overlays_metadata ON ml.prediction_overlays USING GIN (metadata) WHERE metadata IS NOT NULL AND metadata != '{}'; -- Trigger for updated_at CREATE OR REPLACE FUNCTION ml.update_prediction_overlays_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS trigger_prediction_overlays_updated_at ON ml.prediction_overlays; CREATE TRIGGER trigger_prediction_overlays_updated_at BEFORE UPDATE ON ml.prediction_overlays FOR EACH ROW EXECUTE FUNCTION ml.update_prediction_overlays_updated_at(); -- Comments COMMENT ON TABLE ml.prediction_overlays IS 'Complex overlay configurations for chart visualization'; COMMENT ON COLUMN ml.prediction_overlays.prediction_id IS 'Reference to parent ML prediction'; COMMENT ON COLUMN ml.prediction_overlays.overlay_type IS 'Type of overlay for rendering'; COMMENT ON COLUMN ml.prediction_overlays.price_levels IS 'Array of price levels for horizontal overlays'; COMMENT ON COLUMN ml.prediction_overlays.time_range IS 'Time range for time-bounded overlays'; COMMENT ON COLUMN ml.prediction_overlays.coordinates IS 'JSON array of {time, price} coordinates'; COMMENT ON COLUMN ml.prediction_overlays.style_config IS 'Visual styling configuration'; COMMENT ON COLUMN ml.prediction_overlays.is_active IS 'Whether overlay should be displayed';