[TASK-2026-02-03-ANALISIS-DDL-MODELADO] feat(ddl): FASE-3 Moderate P1 gaps

ST-3.1: Course tags system
- education.course_tags with slug, color, featured flag
- education.course_tag_assignments (M:N) with auto usage_count
- Seeds: 10 initial tags (forex, crypto, ICT, etc.)

ST-3.2: Drawing tools for charts
- Enum: trading.drawing_tool_type (18 types including ICT)
- trading.drawing_tools with JSONB points and styles
- trading.drawing_templates for reusable presets

ST-3.3: Complete agent_executions
- Added 10 columns: execution_time_ms, slippage, risk_score, etc.
- 5 new performance indexes
- Trigger for updated_at

ST-3.4: ML composite indexes
- 8 new composite/partial indexes for predictions
- Optimized for symbol+timeframe+date queries
- Partial indexes for high confidence and overlay display

New files: 7 DDL, 2 migrations, 1 seed
Modified: 3 existing DDL files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-02-03 23:55:24 -06:00
parent f64251e459
commit 6da2786590
10 changed files with 660 additions and 19 deletions

View File

@ -0,0 +1,47 @@
-- =====================================================
-- TABLE: education.course_tags
-- =====================================================
-- Proyecto: OrbiQuant IA (Trading Platform)
-- Modulo: OQI-002 - Education
-- Purpose: Tags for categorizing and searching courses
-- Related: OQI-002 Education Module, GAP-005
-- Created: 2026-02-03
-- =====================================================
CREATE TABLE IF NOT EXISTS education.course_tags (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(50) NOT NULL,
slug VARCHAR(50) NOT NULL UNIQUE,
description TEXT,
color VARCHAR(7) DEFAULT '#6B7280', -- Hex color for UI
icon VARCHAR(50), -- Icon identifier
is_featured BOOLEAN NOT NULL DEFAULT false,
usage_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT course_tags_name_not_empty CHECK (length(trim(name)) > 0),
CONSTRAINT course_tags_slug_format CHECK (slug ~ '^[a-z0-9-]+$'),
CONSTRAINT course_tags_color_format CHECK (color ~ '^#[0-9A-Fa-f]{6}$'),
CONSTRAINT course_tags_usage_positive CHECK (usage_count >= 0)
);
-- Indexes
CREATE INDEX idx_course_tags_slug ON education.course_tags(slug);
CREATE INDEX idx_course_tags_name ON education.course_tags(name);
CREATE INDEX idx_course_tags_featured ON education.course_tags(is_featured) WHERE is_featured = true;
CREATE INDEX idx_course_tags_usage ON education.course_tags(usage_count DESC);
-- Trigger
CREATE TRIGGER trg_course_tags_updated_at
BEFORE UPDATE ON education.course_tags
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at();
-- Comments
COMMENT ON TABLE education.course_tags IS 'Tags for categorizing courses (e.g., forex, crypto, technical-analysis)';
COMMENT ON COLUMN education.course_tags.slug IS 'URL-friendly identifier for the tag';
COMMENT ON COLUMN education.course_tags.color IS 'Hex color code for UI display';
COMMENT ON COLUMN education.course_tags.icon IS 'Icon identifier for UI display';
COMMENT ON COLUMN education.course_tags.is_featured IS 'Whether tag appears in featured/promoted sections';
COMMENT ON COLUMN education.course_tags.usage_count IS 'Denormalized count of courses using this tag';

View File

@ -0,0 +1,50 @@
-- =====================================================
-- TABLE: education.course_tag_assignments
-- =====================================================
-- Proyecto: OrbiQuant IA (Trading Platform)
-- Modulo: OQI-002 - Education
-- Purpose: Many-to-many relationship between courses and tags
-- Related: OQI-002 Education Module, GAP-005
-- Created: 2026-02-03
-- =====================================================
CREATE TABLE IF NOT EXISTS education.course_tag_assignments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
course_id UUID NOT NULL REFERENCES education.courses(id) ON DELETE CASCADE,
tag_id UUID NOT NULL REFERENCES education.course_tags(id) ON DELETE CASCADE,
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
assigned_by UUID REFERENCES auth.users(id),
CONSTRAINT unique_course_tag UNIQUE (course_id, tag_id)
);
-- Indexes
CREATE INDEX idx_course_tag_assignments_course ON education.course_tag_assignments(course_id);
CREATE INDEX idx_course_tag_assignments_tag ON education.course_tag_assignments(tag_id);
-- Function to update usage_count on course_tags
CREATE OR REPLACE FUNCTION education.update_tag_usage_count()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
UPDATE education.course_tags SET usage_count = usage_count + 1 WHERE id = NEW.tag_id;
ELSIF TG_OP = 'DELETE' THEN
UPDATE education.course_tags SET usage_count = usage_count - 1 WHERE id = OLD.tag_id;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
-- Trigger to maintain usage_count
CREATE TRIGGER trg_course_tag_usage
AFTER INSERT OR DELETE ON education.course_tag_assignments
FOR EACH ROW
EXECUTE FUNCTION education.update_tag_usage_count();
-- Comments
COMMENT ON TABLE education.course_tag_assignments IS 'Junction table for course-tag relationships';
COMMENT ON COLUMN education.course_tag_assignments.course_id IS 'Reference to the course';
COMMENT ON COLUMN education.course_tag_assignments.tag_id IS 'Reference to the tag';
COMMENT ON COLUMN education.course_tag_assignments.assigned_at IS 'When the tag was assigned to the course';
COMMENT ON COLUMN education.course_tag_assignments.assigned_by IS 'User who assigned the tag (optional)';
COMMENT ON FUNCTION education.update_tag_usage_count() IS 'Maintains denormalized usage_count in course_tags table';

View File

@ -3,8 +3,11 @@
-- =====================================================
-- Description: Tracking de ejecuciones de trading agents
-- Schema: investment
-- Gap: GAP-DDL-005
-- Gap: GAP-DDL-005, GAP-007
-- Task: ST-3.3
-- Created: 2026-02-03
-- Updated: 2026-02-03
-- Related: OQI-004 Investment Accounts
-- =====================================================
CREATE TABLE investment.agent_executions (
@ -21,55 +24,149 @@ CREATE TABLE investment.agent_executions (
execution_type IN ('trade', 'rebalance', 'distribution', 'stop_loss', 'take_profit', 'hedge')
),
-- Detalles del trade (si aplica)
-- Detalles del trade
symbol VARCHAR(20),
side VARCHAR(4) CHECK (side IN ('buy', 'sell')),
quantity DECIMAL(20,8),
entry_price DECIMAL(20,8),
exit_price DECIMAL(20,8),
-- Resultado
-- Resultado (P&L)
pnl DECIMAL(20,8),
pnl_percentage DECIMAL(8,4),
fees DECIMAL(20,8) DEFAULT 0,
-- Detalles adicionales
-- Metricas de ejecucion
execution_time_ms INTEGER,
slippage DECIMAL(20,8),
-- Metricas de riesgo
risk_score DECIMAL(5,4) CHECK (risk_score IS NULL OR risk_score BETWEEN 0 AND 1),
position_size_percent DECIMAL(5,2),
-- Detalles adicionales (JSON)
trade_details JSONB DEFAULT '{}',
market_conditions JSONB, -- volatility, trend, etc.
market_conditions JSONB, -- volatility, trend, liquidity, etc.
-- Estado
status VARCHAR(20) DEFAULT 'executed' CHECK (
status IN ('pending', 'executed', 'partially_filled', 'cancelled', 'failed')
),
error_code VARCHAR(50),
failure_reason TEXT,
notes TEXT,
-- Metadata ML/AI
signal_source VARCHAR(50), -- ml_model, llm, manual, scheduled
confidence_score DECIMAL(5,4),
signal_source VARCHAR(50), -- ml_model, llm, manual, scheduled, ensemble
confidence_score DECIMAL(5,4) CHECK (confidence_score IS NULL OR confidence_score BETWEEN 0 AND 1),
model_version VARCHAR(50),
model_id UUID, -- Reference to ML model
-- Referencias externas
external_order_id VARCHAR(100),
-- Timestamps
executed_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
closed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indices
-- =====================================================
-- INDEXES
-- =====================================================
-- Primary lookup indexes
CREATE INDEX idx_agent_exec_account ON investment.agent_executions(account_id);
CREATE INDEX idx_agent_exec_type ON investment.agent_executions(agent_type);
CREATE INDEX idx_agent_exec_date ON investment.agent_executions(executed_at DESC);
CREATE INDEX idx_agent_exec_symbol ON investment.agent_executions(symbol)
WHERE symbol IS NOT NULL;
CREATE INDEX idx_agent_exec_pnl ON investment.agent_executions(pnl)
WHERE pnl IS NOT NULL;
CREATE INDEX idx_agent_exec_status ON investment.agent_executions(status);
-- Indice compuesto para reportes
-- Time-based indexes
CREATE INDEX idx_agent_exec_date ON investment.agent_executions(executed_at DESC);
CREATE INDEX idx_agent_exec_account_date ON investment.agent_executions(account_id, executed_at DESC);
-- Comentarios
-- Symbol and P&L indexes (partial for efficiency)
CREATE INDEX idx_agent_exec_symbol ON investment.agent_executions(symbol)
WHERE symbol IS NOT NULL;
CREATE INDEX idx_agent_exec_pnl ON investment.agent_executions(pnl DESC NULLS LAST)
WHERE pnl IS NOT NULL;
-- Performance analysis indexes
CREATE INDEX idx_agent_exec_slippage ON investment.agent_executions(slippage)
WHERE slippage IS NOT NULL;
CREATE INDEX idx_agent_exec_risk_score ON investment.agent_executions(risk_score)
WHERE risk_score IS NOT NULL;
-- External reference indexes
CREATE INDEX idx_agent_exec_external_order ON investment.agent_executions(external_order_id)
WHERE external_order_id IS NOT NULL;
CREATE INDEX idx_agent_exec_model ON investment.agent_executions(model_id)
WHERE model_id IS NOT NULL;
-- Signal analysis index (composite)
CREATE INDEX idx_agent_exec_signal_confidence ON investment.agent_executions(signal_source, confidence_score DESC NULLS LAST)
WHERE signal_source IS NOT NULL;
-- =====================================================
-- TRIGGER
-- =====================================================
CREATE TRIGGER trg_agent_executions_updated_at
BEFORE UPDATE ON investment.agent_executions
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at();
-- =====================================================
-- COMMENTS
-- =====================================================
COMMENT ON TABLE investment.agent_executions IS 'Tracking de ejecuciones de trading agents (Atlas, Orion, Nova)';
-- Agent and execution type
COMMENT ON COLUMN investment.agent_executions.agent_type IS 'Agente que ejecuto: atlas (conservador), orion (moderado), nova (agresivo)';
COMMENT ON COLUMN investment.agent_executions.execution_type IS 'Tipo: trade, rebalance, distribution, stop_loss, take_profit, hedge';
COMMENT ON COLUMN investment.agent_executions.trade_details IS 'JSON con detalles adicionales del trade';
COMMENT ON COLUMN investment.agent_executions.market_conditions IS 'Condiciones de mercado al momento de ejecucion';
COMMENT ON COLUMN investment.agent_executions.signal_source IS 'Fuente de la senal: ml_model, llm, manual, scheduled';
-- Trade details
COMMENT ON COLUMN investment.agent_executions.symbol IS 'Trading pair symbol (e.g., BTCUSDT, EURUSD)';
COMMENT ON COLUMN investment.agent_executions.side IS 'Trade direction: buy or sell';
COMMENT ON COLUMN investment.agent_executions.quantity IS 'Position size in base currency';
COMMENT ON COLUMN investment.agent_executions.entry_price IS 'Entry price of the position';
COMMENT ON COLUMN investment.agent_executions.exit_price IS 'Exit price (null if position still open)';
-- P&L
COMMENT ON COLUMN investment.agent_executions.pnl IS 'Profit/Loss in quote currency';
COMMENT ON COLUMN investment.agent_executions.pnl_percentage IS 'P&L as percentage of position';
COMMENT ON COLUMN investment.agent_executions.fees IS 'Trading fees/commissions paid';
-- Execution metrics
COMMENT ON COLUMN investment.agent_executions.execution_time_ms IS 'Time taken to execute the trade in milliseconds';
COMMENT ON COLUMN investment.agent_executions.slippage IS 'Price slippage between requested and executed price';
-- Risk metrics
COMMENT ON COLUMN investment.agent_executions.risk_score IS 'Risk assessment score at time of execution (0-1)';
COMMENT ON COLUMN investment.agent_executions.position_size_percent IS 'Position size as percentage of portfolio';
-- JSON details
COMMENT ON COLUMN investment.agent_executions.trade_details IS 'JSON with additional trade details (leverage, margin, etc.)';
COMMENT ON COLUMN investment.agent_executions.market_conditions IS 'Market conditions at execution (volatility, trend, liquidity)';
-- Status and errors
COMMENT ON COLUMN investment.agent_executions.status IS 'Execution status: pending, executed, partially_filled, cancelled, failed';
COMMENT ON COLUMN investment.agent_executions.error_code IS 'Standardized error code for failures';
COMMENT ON COLUMN investment.agent_executions.failure_reason IS 'Detailed failure description';
COMMENT ON COLUMN investment.agent_executions.notes IS 'Manual annotations or additional context';
-- ML/AI metadata
COMMENT ON COLUMN investment.agent_executions.signal_source IS 'Signal origin: ml_model, llm, manual, scheduled, ensemble';
COMMENT ON COLUMN investment.agent_executions.confidence_score IS 'ML model confidence score (0-1)';
COMMENT ON COLUMN investment.agent_executions.model_version IS 'Version of the ML model used';
COMMENT ON COLUMN investment.agent_executions.model_id IS 'Reference to ML model that generated the signal';
-- External references
COMMENT ON COLUMN investment.agent_executions.external_order_id IS 'Order ID from external broker/exchange (Binance, MT4, etc.)';
-- Timestamps
COMMENT ON COLUMN investment.agent_executions.executed_at IS 'When the trade was executed';
COMMENT ON COLUMN investment.agent_executions.closed_at IS 'When the position was closed';
COMMENT ON COLUMN investment.agent_executions.created_at IS 'Record creation timestamp';
COMMENT ON COLUMN investment.agent_executions.updated_at IS 'Last modification timestamp';

View File

@ -92,3 +92,51 @@ COMMENT ON COLUMN ml.predictions.model_output IS
"raw_score": 0.5823,
"feature_contributions": {...}
}';
-- =====================================================
-- PERFORMANCE COMPOSITE INDEXES (added 2026-02-03, ST-3.4)
-- =====================================================
-- Index 1: Symbol + Timeframe + Created (most common query pattern)
-- Use: WHERE symbol = ? AND timeframe = ? ORDER BY created_at DESC
CREATE INDEX idx_predictions_symbol_timeframe_created
ON ml.predictions(symbol, timeframe, created_at DESC);
-- Index 2: Symbol + Prediction Type + Created (filtered queries)
-- Use: WHERE symbol = ? AND prediction_type = ? ORDER BY created_at DESC
CREATE INDEX idx_predictions_symbol_type_created
ON ml.predictions(symbol, prediction_type, created_at DESC);
-- Index 3: Model + Symbol + Timeframe (model-specific queries)
-- Use: WHERE model_id = ? AND symbol = ? AND timeframe = ? ORDER BY created_at DESC
CREATE INDEX idx_predictions_model_symbol_timeframe
ON ml.predictions(model_id, symbol, timeframe, created_at DESC);
-- Index 4: High confidence predictions (partial index)
-- Use: WHERE symbol = ? AND timeframe = ? AND confidence_score >= 0.7
CREATE INDEX idx_predictions_high_confidence
ON ml.predictions(symbol, timeframe, created_at DESC)
WHERE confidence_score >= 0.7;
-- Index 5: Recent valid predictions (partial index for active queries)
-- Use: WHERE symbol = ? AND timeframe = ? AND valid_until IS NOT NULL
CREATE INDEX idx_predictions_recent_valid
ON ml.predictions(symbol, timeframe, prediction_type, created_at DESC)
WHERE valid_until IS NOT NULL;
-- Index 6: Overlay display (for chart rendering)
-- Use: WHERE symbol = ? AND timeframe = ? AND show_on_chart = true
CREATE INDEX idx_predictions_overlay_display
ON ml.predictions(symbol, timeframe, display_priority DESC, created_at DESC)
WHERE (chart_config->>'show_on_chart')::boolean = true;
-- Index 7: Symbol + Confidence Score (ranking queries)
-- Use: WHERE symbol = ? ORDER BY confidence_score DESC
CREATE INDEX idx_predictions_symbol_confidence
ON ml.predictions(symbol, confidence_score DESC, created_at DESC);
-- Index 8: Timeframe + Prediction Result (cross-symbol analysis)
-- Use: WHERE timeframe = ? AND prediction_result = ? ORDER BY created_at DESC
CREATE INDEX idx_predictions_timeframe_result_created
ON ml.predictions(timeframe, prediction_result, created_at DESC)
WHERE prediction_result IS NOT NULL;

View File

@ -83,3 +83,27 @@ CREATE TYPE trading.bot_status AS ENUM (
'stopped',
'error'
);
-- Drawing tool types for chart annotations
CREATE TYPE trading.drawing_tool_type AS ENUM (
'trend_line',
'horizontal_line',
'vertical_line',
'ray',
'extended_line',
'parallel_channel',
'fibonacci_retracement',
'fibonacci_extension',
'rectangle',
'ellipse',
'triangle',
'arrow',
'text',
'price_range',
'date_range',
'order_block',
'fair_value_gap',
'liquidity_level'
);
COMMENT ON TYPE trading.drawing_tool_type IS 'Types of drawing tools available on charts';

View File

@ -0,0 +1,77 @@
-- ============================================================================
-- Schema: trading
-- File: 12-drawing_tools.sql
-- Description: User drawings and annotations on trading charts
-- Related: OQI-003 Trading Charts, GAP-006
-- Dependencies: 00-enums.sql, 01-symbols.sql, auth.users
-- ============================================================================
CREATE TABLE IF NOT EXISTS trading.drawing_tools (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
symbol_id UUID NOT NULL REFERENCES trading.symbols(id) ON DELETE CASCADE,
timeframe trading.timeframe NOT NULL,
tool_type trading.drawing_tool_type NOT NULL,
name VARCHAR(100),
-- Coordinates (stored as JSON for flexibility)
points JSONB NOT NULL DEFAULT '[]', -- Array of {time, price} points
-- Style configuration
style JSONB NOT NULL DEFAULT '{
"color": "#2196F3",
"lineWidth": 1,
"lineStyle": "solid",
"fillColor": null,
"fillOpacity": 0.2,
"showLabel": true,
"labelPosition": "right"
}',
-- Fibonacci specific (if applicable)
fib_levels DECIMAL(5,4)[] DEFAULT NULL, -- e.g., {0, 0.236, 0.382, 0.5, 0.618, 0.786, 1}
-- Text content (for text/label tools)
text_content TEXT,
-- Visibility and state
is_visible BOOLEAN NOT NULL DEFAULT true,
is_locked BOOLEAN NOT NULL DEFAULT false,
z_index INTEGER NOT NULL DEFAULT 0,
-- Template/sharing
is_template BOOLEAN NOT NULL DEFAULT false,
is_shared BOOLEAN NOT NULL DEFAULT false,
-- Metadata
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT drawing_tools_points_not_empty CHECK (jsonb_array_length(points) >= 1)
);
-- Indexes
CREATE INDEX idx_drawing_tools_user_id ON trading.drawing_tools(user_id);
CREATE INDEX idx_drawing_tools_symbol_id ON trading.drawing_tools(symbol_id);
CREATE INDEX idx_drawing_tools_user_symbol ON trading.drawing_tools(user_id, symbol_id, timeframe);
CREATE INDEX idx_drawing_tools_visible ON trading.drawing_tools(user_id, is_visible) WHERE is_visible = true;
CREATE INDEX idx_drawing_tools_templates ON trading.drawing_tools(is_template) WHERE is_template = true;
CREATE INDEX idx_drawing_tools_shared ON trading.drawing_tools(is_shared) WHERE is_shared = true;
CREATE INDEX idx_drawing_tools_points ON trading.drawing_tools USING GIN(points);
-- Trigger
CREATE TRIGGER trg_drawing_tools_updated_at
BEFORE UPDATE ON trading.drawing_tools
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at();
-- Comments
COMMENT ON TABLE trading.drawing_tools IS 'User drawings and annotations on trading charts';
COMMENT ON COLUMN trading.drawing_tools.points IS 'Array of coordinate points [{time: ISO string, price: number}]';
COMMENT ON COLUMN trading.drawing_tools.style IS 'Visual style configuration (color, line width, fill, etc.)';
COMMENT ON COLUMN trading.drawing_tools.fib_levels IS 'Fibonacci levels for retracement/extension tools';
COMMENT ON COLUMN trading.drawing_tools.z_index IS 'Z-index for layering multiple drawings';
COMMENT ON COLUMN trading.drawing_tools.is_template IS 'Whether this drawing can be used as a template';
COMMENT ON COLUMN trading.drawing_tools.is_shared IS 'Whether this drawing is visible to other users';

View File

@ -0,0 +1,45 @@
-- ============================================================================
-- Schema: trading
-- File: 13-drawing_templates.sql
-- Description: Reusable drawing templates and presets
-- Related: OQI-003 Trading Charts, GAP-006
-- Dependencies: 00-enums.sql, auth.users
-- ============================================================================
CREATE TABLE IF NOT EXISTS trading.drawing_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, -- NULL = system template
name VARCHAR(100) NOT NULL,
description TEXT,
tool_type trading.drawing_tool_type NOT NULL,
style JSONB NOT NULL,
fib_levels DECIMAL(5,4)[] DEFAULT NULL,
is_system BOOLEAN NOT NULL DEFAULT false,
is_public BOOLEAN NOT NULL DEFAULT false,
usage_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT drawing_templates_name_not_empty CHECK (length(trim(name)) > 0)
);
-- Indexes
CREATE INDEX idx_drawing_templates_user ON trading.drawing_templates(user_id);
CREATE INDEX idx_drawing_templates_tool_type ON trading.drawing_templates(tool_type);
CREATE INDEX idx_drawing_templates_public ON trading.drawing_templates(is_public) WHERE is_public = true;
CREATE INDEX idx_drawing_templates_system ON trading.drawing_templates(is_system) WHERE is_system = true;
-- Trigger
CREATE TRIGGER trg_drawing_templates_updated_at
BEFORE UPDATE ON trading.drawing_templates
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at();
-- Comments
COMMENT ON TABLE trading.drawing_templates IS 'Reusable drawing templates and presets for chart annotations';
COMMENT ON COLUMN trading.drawing_templates.user_id IS 'Owner of template, NULL for system templates';
COMMENT ON COLUMN trading.drawing_templates.style IS 'Default style configuration for the template';
COMMENT ON COLUMN trading.drawing_templates.fib_levels IS 'Default Fibonacci levels for retracement/extension templates';
COMMENT ON COLUMN trading.drawing_templates.is_system IS 'System-provided template (cannot be deleted by users)';
COMMENT ON COLUMN trading.drawing_templates.is_public IS 'Template is visible to all users';
COMMENT ON COLUMN trading.drawing_templates.usage_count IS 'Number of times this template has been applied';

View File

@ -0,0 +1,108 @@
-- =====================================================
-- MIGRATION: Add composite indexes to ml.predictions for performance
-- =====================================================
-- Date: 2026-02-03
-- Task: ST-3.4, GAP-008
-- Description: Optimize query performance with composite indexes
-- =====================================================
-- ===========================================
-- COMPOSITE INDEX 1: Symbol + Timeframe + Created
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE symbol = ? AND timeframe = ?
-- ORDER BY created_at DESC LIMIT ?
-- Use case: Most common query for fetching latest predictions for a symbol/timeframe
CREATE INDEX IF NOT EXISTS idx_predictions_symbol_timeframe_created
ON ml.predictions(symbol, timeframe, created_at DESC);
-- ===========================================
-- COMPOSITE INDEX 2: Symbol + Prediction Type + Created
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE symbol = ? AND prediction_type = ?
-- ORDER BY created_at DESC
-- Use case: Filtered queries by prediction type (e.g., only DIRECTION predictions)
CREATE INDEX IF NOT EXISTS idx_predictions_symbol_type_created
ON ml.predictions(symbol, prediction_type, created_at DESC);
-- ===========================================
-- COMPOSITE INDEX 3: Model + Symbol + Timeframe + Created
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE model_id = ? AND symbol = ? AND timeframe = ?
-- ORDER BY created_at DESC
-- Use case: Model-specific prediction queries for analysis and comparison
CREATE INDEX IF NOT EXISTS idx_predictions_model_symbol_timeframe
ON ml.predictions(model_id, symbol, timeframe, created_at DESC);
-- ===========================================
-- COMPOSITE INDEX 4: High Confidence Predictions (Partial)
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE symbol = ? AND timeframe = ? AND confidence_score >= 0.7
-- ORDER BY created_at DESC
-- Use case: Fetch only high-confidence predictions for display/alerts
CREATE INDEX IF NOT EXISTS idx_predictions_high_confidence
ON ml.predictions(symbol, timeframe, created_at DESC)
WHERE confidence_score >= 0.7;
-- ===========================================
-- COMPOSITE INDEX 5: Recent Valid Predictions (Partial)
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE symbol = ? AND timeframe = ? AND valid_until > NOW()
-- ORDER BY prediction_type, created_at DESC
-- Use case: Active predictions that haven't expired yet
-- Note: Uses immutable predicate-safe pattern (valid_until compared at query time)
CREATE INDEX IF NOT EXISTS idx_predictions_recent_valid
ON ml.predictions(symbol, timeframe, prediction_type, created_at DESC)
WHERE valid_until IS NOT NULL;
-- ===========================================
-- COMPOSITE INDEX 6: Overlay Display for Charts
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE symbol = ? AND timeframe = ?
-- AND (chart_config->>'show_on_chart')::boolean = true
-- ORDER BY display_priority DESC, created_at DESC
-- Use case: Chart rendering - fetch predictions configured for display
CREATE INDEX IF NOT EXISTS idx_predictions_overlay_display
ON ml.predictions(symbol, timeframe, display_priority DESC, created_at DESC)
WHERE (chart_config->>'show_on_chart')::boolean = true;
-- ===========================================
-- COMPOSITE INDEX 7: Symbol + Confidence Score (Descending)
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE symbol = ?
-- ORDER BY confidence_score DESC
-- Use case: Ranking predictions by confidence for a symbol
CREATE INDEX IF NOT EXISTS idx_predictions_symbol_confidence
ON ml.predictions(symbol, confidence_score DESC, created_at DESC);
-- ===========================================
-- COMPOSITE INDEX 8: Timeframe + Prediction Result + Created
-- ===========================================
-- Query pattern: SELECT * FROM ml.predictions
-- WHERE timeframe = ? AND prediction_result = ?
-- ORDER BY created_at DESC
-- Use case: Cross-symbol analysis of specific prediction results (e.g., all BUY signals on 1H)
CREATE INDEX IF NOT EXISTS idx_predictions_timeframe_result_created
ON ml.predictions(timeframe, prediction_result, created_at DESC)
WHERE prediction_result IS NOT NULL;
-- ===========================================
-- DOCUMENTATION NOTES
-- ===========================================
-- Performance considerations:
-- 1. These indexes are designed for read-heavy workloads
-- 2. Partial indexes reduce storage and improve write performance
-- 3. DESC ordering on timestamps optimizes LIMIT queries for recent data
-- 4. Run ANALYZE after creation in production:
-- ANALYZE ml.predictions;
--
-- Index maintenance:
-- - Monitor pg_stat_user_indexes for usage statistics
-- - Consider REINDEX CONCURRENTLY if fragmentation occurs
-- - Review query plans with EXPLAIN ANALYZE periodically

View File

@ -0,0 +1,124 @@
-- =====================================================
-- Migration: Complete agent_executions table
-- =====================================================
-- Description: Add missing columns for detailed execution tracking
-- Date: 2026-02-03
-- Task: ST-3.3, GAP-007
-- Related: OQI-004 Investment Accounts
-- =====================================================
-- =====================================================
-- ADD MISSING COLUMNS
-- =====================================================
-- Execution performance metrics
ALTER TABLE investment.agent_executions
ADD COLUMN IF NOT EXISTS execution_time_ms INTEGER,
ADD COLUMN IF NOT EXISTS slippage DECIMAL(20,8);
-- Risk metrics
ALTER TABLE investment.agent_executions
ADD COLUMN IF NOT EXISTS risk_score DECIMAL(5,4) CHECK (risk_score IS NULL OR risk_score BETWEEN 0 AND 1),
ADD COLUMN IF NOT EXISTS position_size_percent DECIMAL(5,2);
-- External references
ALTER TABLE investment.agent_executions
ADD COLUMN IF NOT EXISTS external_order_id VARCHAR(100),
ADD COLUMN IF NOT EXISTS model_id UUID;
-- Additional timestamps for lifecycle tracking
ALTER TABLE investment.agent_executions
ADD COLUMN IF NOT EXISTS closed_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
-- Error tracking (complement existing failure_reason)
ALTER TABLE investment.agent_executions
ADD COLUMN IF NOT EXISTS error_code VARCHAR(50);
-- Notes field for manual annotations
ALTER TABLE investment.agent_executions
ADD COLUMN IF NOT EXISTS notes TEXT;
-- =====================================================
-- ADD PERFORMANCE INDEXES
-- =====================================================
-- Index for slippage analysis
CREATE INDEX IF NOT EXISTS idx_agent_exec_slippage
ON investment.agent_executions(slippage)
WHERE slippage IS NOT NULL;
-- Index for risk score analysis
CREATE INDEX IF NOT EXISTS idx_agent_exec_risk_score
ON investment.agent_executions(risk_score)
WHERE risk_score IS NOT NULL;
-- Index for external order lookups
CREATE INDEX IF NOT EXISTS idx_agent_exec_external_order
ON investment.agent_executions(external_order_id)
WHERE external_order_id IS NOT NULL;
-- Index for model performance tracking
CREATE INDEX IF NOT EXISTS idx_agent_exec_model
ON investment.agent_executions(model_id)
WHERE model_id IS NOT NULL;
-- Composite index for signal source analysis
CREATE INDEX IF NOT EXISTS idx_agent_exec_signal_confidence
ON investment.agent_executions(signal_source, confidence_score DESC NULLS LAST)
WHERE signal_source IS NOT NULL;
-- =====================================================
-- ADD TRIGGER FOR updated_at
-- =====================================================
-- Create trigger function if not exists (reuse common function)
DO $$
BEGIN
-- Check if public.update_updated_at exists, if not use a local one
IF NOT EXISTS (
SELECT 1 FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public' AND p.proname = 'update_updated_at'
) THEN
CREATE OR REPLACE FUNCTION public.update_updated_at()
RETURNS TRIGGER AS $func$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$func$ LANGUAGE plpgsql;
END IF;
END $$;
-- Create or replace trigger
DROP TRIGGER IF EXISTS trg_agent_executions_updated_at ON investment.agent_executions;
CREATE TRIGGER trg_agent_executions_updated_at
BEFORE UPDATE ON investment.agent_executions
FOR EACH ROW
EXECUTE FUNCTION public.update_updated_at();
-- =====================================================
-- ADD COMMENTS FOR NEW COLUMNS
-- =====================================================
COMMENT ON COLUMN investment.agent_executions.execution_time_ms IS 'Time taken to execute the trade in milliseconds';
COMMENT ON COLUMN investment.agent_executions.slippage IS 'Price slippage between requested and executed price';
COMMENT ON COLUMN investment.agent_executions.risk_score IS 'Risk assessment score at time of execution (0-1)';
COMMENT ON COLUMN investment.agent_executions.position_size_percent IS 'Position size as percentage of portfolio';
COMMENT ON COLUMN investment.agent_executions.external_order_id IS 'Order ID from external broker/exchange';
COMMENT ON COLUMN investment.agent_executions.model_id IS 'Reference to ML model that generated the signal';
COMMENT ON COLUMN investment.agent_executions.closed_at IS 'Timestamp when position was closed';
COMMENT ON COLUMN investment.agent_executions.updated_at IS 'Last modification timestamp';
COMMENT ON COLUMN investment.agent_executions.error_code IS 'Standardized error code for failures';
COMMENT ON COLUMN investment.agent_executions.notes IS 'Manual annotations or additional context';
-- =====================================================
-- VERIFICATION QUERY (for manual check)
-- =====================================================
-- SELECT column_name, data_type, is_nullable
-- FROM information_schema.columns
-- WHERE table_schema = 'investment'
-- AND table_name = 'agent_executions'
-- ORDER BY ordinal_position;

View File

@ -0,0 +1,21 @@
-- =====================================================
-- SEED: Initial course tags
-- =====================================================
-- Proyecto: OrbiQuant IA (Trading Platform)
-- Modulo: OQI-002 - Education
-- Related: ST-3.1 - Course Tags System
-- Created: 2026-02-03
-- =====================================================
INSERT INTO education.course_tags (name, slug, description, color, is_featured) VALUES
('Forex', 'forex', 'Foreign exchange trading', '#10B981', true),
('Crypto', 'crypto', 'Cryptocurrency trading', '#F59E0B', true),
('Technical Analysis', 'technical-analysis', 'Chart patterns and indicators', '#3B82F6', true),
('Fundamental Analysis', 'fundamental-analysis', 'Economic and financial analysis', '#8B5CF6', false),
('Risk Management', 'risk-management', 'Position sizing and risk control', '#EF4444', true),
('Psychology', 'psychology', 'Trading mindset and emotions', '#EC4899', false),
('Beginner', 'beginner', 'Courses for new traders', '#6EE7B7', true),
('Advanced', 'advanced', 'Advanced trading concepts', '#FCD34D', false),
('ICT', 'ict', 'Inner Circle Trader concepts', '#818CF8', true),
('Smart Money', 'smart-money', 'Institutional trading concepts', '#F472B6', false)
ON CONFLICT (slug) DO NOTHING;