feat: Update DDL schemas and add new structures

DDL updates:
- Update extensions and schemas configuration
- Add sessions table for auth schema
- Update education schema (videos, install/uninstall scripts)
- Add backtest_runs and llm_signals tables for ML schema

Scripts:
- Update database creation and migration scripts
- Add DDL validation script

New:
- Add migrations directory structure
- Add production seeds for auth, investment, market_data, trading

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Adrian Flores Cortes 2026-01-30 12:24:23 -06:00
parent 70c201da8e
commit 2a6d8367d8
12 changed files with 540 additions and 90 deletions

View File

@ -19,6 +19,9 @@ CREATE EXTENSION IF NOT EXISTS "unaccent";
-- Trigram similarity for fuzzy text matching
CREATE EXTENSION IF NOT EXISTS "pg_trgm";
-- Vector similarity search for LLM embeddings (pgvector)
CREATE EXTENSION IF NOT EXISTS "vector";
COMMENT ON EXTENSION "uuid-ossp" IS 'UUID generation functions';
COMMENT ON EXTENSION "pgcrypto" IS 'Cryptographic functions for secure password and token handling';
COMMENT ON EXTENSION "citext" IS 'Case-insensitive text type for email addresses';

View File

@ -12,6 +12,10 @@ COMMENT ON SCHEMA auth IS 'Authentication, authorization, and user management';
CREATE SCHEMA IF NOT EXISTS education;
COMMENT ON SCHEMA education IS 'Educational content, courses, and learning progress';
-- Market Data
CREATE SCHEMA IF NOT EXISTS market_data;
COMMENT ON SCHEMA market_data IS 'Market tickers, OHLCV data, and data staging';
-- Trading Operations
CREATE SCHEMA IF NOT EXISTS trading;
COMMENT ON SCHEMA trading IS 'Trading bots, orders, positions, and market data';

View File

@ -15,6 +15,10 @@ CREATE TABLE auth.sessions (
-- Session Token
session_token VARCHAR(255) NOT NULL UNIQUE,
-- Token Rotation (for security)
refresh_token_hash VARCHAR(64),
refresh_token_issued_at TIMESTAMPTZ,
-- Session Lifecycle
expires_at TIMESTAMPTZ NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
@ -70,6 +74,8 @@ COMMENT ON TABLE auth.sessions IS 'User session management for authentication an
COMMENT ON COLUMN auth.sessions.id IS 'Unique identifier for the session';
COMMENT ON COLUMN auth.sessions.user_id IS 'Reference to the user account';
COMMENT ON COLUMN auth.sessions.session_token IS 'Unique session token for authentication';
COMMENT ON COLUMN auth.sessions.refresh_token_hash IS 'SHA-256 hash of refresh token for rotation security';
COMMENT ON COLUMN auth.sessions.refresh_token_issued_at IS 'Timestamp when current refresh token was issued';
COMMENT ON COLUMN auth.sessions.expires_at IS 'Session expiration timestamp';
COMMENT ON COLUMN auth.sessions.is_active IS 'Whether the session is currently active';
COMMENT ON COLUMN auth.sessions.ip_address IS 'IP address of the session';

View File

@ -13,7 +13,7 @@ CREATE TABLE education.videos (
-- Relaciones
course_id UUID NOT NULL REFERENCES education.courses(id) ON DELETE CASCADE,
lesson_id UUID REFERENCES education.lessons(id) ON DELETE SET NULL,
uploaded_by UUID NOT NULL REFERENCES core.users(id) ON DELETE RESTRICT,
uploaded_by UUID NOT NULL REFERENCES auth.users(id) ON DELETE RESTRICT,
-- Información básica
title VARCHAR(200) NOT NULL,

View File

@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS ml.backtest_runs (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- User who initiated the backtest (optional for system backtests)
user_id UUID REFERENCES core.users(id) ON DELETE SET NULL,
user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
-- Symbol and date range
symbol VARCHAR(20) NOT NULL,

View File

@ -0,0 +1,123 @@
-- =====================================================
-- ML SCHEMA - LLM SIGNALS TABLE
-- =====================================================
-- Description: Logs ML signals and LLM decisions for feedback and fine-tuning
-- Schema: ml
-- Author: ML-Specialist (NEXUS v4.0)
-- Date: 2026-01-25
-- Module: OQI-007-llm-strategy-agent
-- =====================================================
CREATE TABLE ml.llm_signals (
id SERIAL PRIMARY KEY,
signal_id UUID NOT NULL UNIQUE DEFAULT gen_random_uuid(),
-- Symbol and timing
symbol VARCHAR(20) NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
-- ML Predictions (from metamodel)
ml_prediction JSONB,
-- Expected structure:
-- {
-- "direction": "BULLISH"|"BEARISH"|"NEUTRAL",
-- "magnitude": 0.5,
-- "confidence": 0.75,
-- "delta_high": 10.5,
-- "delta_low": 8.2,
-- "volatility_regime": "HIGH"|"MEDIUM"|"LOW",
-- "market_phase": "accumulation"|"distribution"|"manipulation"|"trend"
-- }
-- Strategy predictions (individual strategies)
strategy_predictions JSONB,
-- Expected structure:
-- [
-- {"name": "PVA", "direction": "BULLISH", "confidence": 0.8, "weight": 0.25},
-- {"name": "MRD", "direction": "NEUTRAL", "confidence": 0.5, "weight": 0.20},
-- ...
-- ]
-- LLM interaction
llm_prompt TEXT,
llm_response TEXT,
-- Parsed decision from LLM
decision JSONB,
-- Expected structure:
-- {
-- "action": "TRADE"|"NO_TRADE"|"WAIT",
-- "direction": "LONG"|"SHORT"|null,
-- "entry": 2340.50,
-- "stop_loss": 2335.00,
-- "take_profit": [2350.00, 2360.00],
-- "position_size": 1.0,
-- "reasoning": "...",
-- "is_valid": true
-- }
-- Trade result (updated after trade closes)
trade_result JSONB,
-- Expected structure:
-- {
-- "result": "WIN"|"LOSS"|"BREAKEVEN"|"PARTIAL",
-- "pnl": 50.00,
-- "pnl_pct": 5.0,
-- "entry_price": 2340.50,
-- "exit_price": 2350.00,
-- "stop_loss_hit": false,
-- "take_profit_hit": true,
-- "duration_minutes": 45,
-- "exit_reason": "TP1"
-- }
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Indices for common queries
CREATE INDEX idx_llm_signals_symbol ON ml.llm_signals(symbol);
CREATE INDEX idx_llm_signals_timestamp ON ml.llm_signals(timestamp DESC);
CREATE INDEX idx_llm_signals_created_at ON ml.llm_signals(created_at DESC);
-- Index for finding signals with results (for training data export)
CREATE INDEX idx_llm_signals_with_results ON ml.llm_signals(timestamp DESC)
WHERE trade_result IS NOT NULL;
-- Index for decision types
CREATE INDEX idx_llm_signals_decision_action ON ml.llm_signals((decision->>'action'));
-- GIN index for JSONB queries
CREATE INDEX idx_llm_signals_ml_prediction ON ml.llm_signals USING GIN (ml_prediction);
CREATE INDEX idx_llm_signals_trade_result ON ml.llm_signals USING GIN (trade_result);
-- Trigger to update updated_at
CREATE OR REPLACE FUNCTION ml.update_llm_signals_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_llm_signals_updated_at
BEFORE UPDATE ON ml.llm_signals
FOR EACH ROW
EXECUTE FUNCTION ml.update_llm_signals_updated_at();
-- Comments
COMMENT ON TABLE ml.llm_signals IS 'Logs ML signals and LLM decisions for performance tracking and fine-tuning dataset generation';
COMMENT ON COLUMN ml.llm_signals.id IS 'Auto-incrementing primary key';
COMMENT ON COLUMN ml.llm_signals.signal_id IS 'Unique UUID for external reference';
COMMENT ON COLUMN ml.llm_signals.symbol IS 'Trading symbol (e.g., XAUUSD, EURUSD)';
COMMENT ON COLUMN ml.llm_signals.timestamp IS 'Timestamp of the signal generation';
COMMENT ON COLUMN ml.llm_signals.ml_prediction IS 'JSONB with metamodel prediction output';
COMMENT ON COLUMN ml.llm_signals.strategy_predictions IS 'JSONB array with individual strategy predictions';
COMMENT ON COLUMN ml.llm_signals.llm_prompt IS 'Full prompt sent to the LLM';
COMMENT ON COLUMN ml.llm_signals.llm_response IS 'Raw response from the LLM';
COMMENT ON COLUMN ml.llm_signals.decision IS 'JSONB with parsed trading decision';
COMMENT ON COLUMN ml.llm_signals.trade_result IS 'JSONB with trade outcome (null until trade closes)';
COMMENT ON COLUMN ml.llm_signals.created_at IS 'Record creation timestamp';
COMMENT ON COLUMN ml.llm_signals.updated_at IS 'Record last update timestamp';

View File

@ -0,0 +1,17 @@
-- ============================================================================
-- Migration: Add Token Rotation Columns
-- Date: 2026-01-27
-- Author: Claude Code (BLOCKER-001 Token Refresh Improvements)
-- ============================================================================
-- Add columns for token rotation security
ALTER TABLE auth.sessions
ADD COLUMN IF NOT EXISTS refresh_token_hash VARCHAR(64),
ADD COLUMN IF NOT EXISTS refresh_token_issued_at TIMESTAMPTZ;
-- Add comments
COMMENT ON COLUMN auth.sessions.refresh_token_hash IS 'SHA-256 hash of refresh token for rotation security';
COMMENT ON COLUMN auth.sessions.refresh_token_issued_at IS 'Timestamp when current refresh token was issued';
-- Create index for performance (hash lookups)
CREATE INDEX IF NOT EXISTS idx_sessions_refresh_token_hash ON auth.sessions(refresh_token_hash);

View File

@ -0,0 +1,72 @@
-- =====================================================
-- SEED DATA - Schema Auth (Production)
-- =====================================================
-- Project: OrbiQuant IA (Trading Platform)
-- Tables: auth.users, auth.user_profiles
-- Description: Admin super user seed for platform bootstrap
-- Date: 2026-01-27
-- =====================================================
SET search_path TO auth, public;
-- =====================================================
-- 1. ADMIN USER
-- =====================================================
INSERT INTO auth.users (
id,
email,
email_verified,
email_verified_at,
password_hash,
status,
role,
mfa_enabled,
mfa_method
) VALUES (
'00000000-0000-0000-0000-000000000000',
'admin@orbiquant.com',
true,
NOW(),
'$2b$12$LJ3m4yv5E7Qj8K9F2N1P6eR4T6Y8U0I2O4P6A8S0D2F4G6H8J0K2',
'active',
'super_admin',
false,
'none'
) ON CONFLICT (email) DO NOTHING;
-- =====================================================
-- 2. ADMIN USER PROFILE
-- =====================================================
INSERT INTO auth.user_profiles (
user_id,
first_name,
last_name,
display_name,
language,
timezone,
newsletter_subscribed,
marketing_emails_enabled,
notifications_enabled,
metadata
) VALUES (
'00000000-0000-0000-0000-000000000000',
'System',
'Administrator',
'OrbiQuant Admin',
'en',
'UTC',
false,
false,
true,
'{"role_description": "Platform super administrator", "created_by": "seed"}'::jsonb
) ON CONFLICT (user_id) DO NOTHING;
-- =====================================================
-- VERIFICATION
-- =====================================================
SELECT 'auth.users (admin) seed:' AS info, COUNT(*) AS count
FROM auth.users WHERE email = 'admin@orbiquant.com';
SELECT 'auth.user_profiles (admin) seed:' AS info, COUNT(*) AS count
FROM auth.user_profiles WHERE user_id = '00000000-0000-0000-0000-000000000000';

View File

@ -0,0 +1,80 @@
-- =====================================================
-- SEED DATA - Schema Investment (Production)
-- =====================================================
-- Project: OrbiQuant IA (Trading Platform)
-- Table: investment.products
-- Description: PAMM investment products (Atlas, Orion, Nova)
-- Date: 2026-01-27
-- =====================================================
SET search_path TO investment, public;
-- =====================================================
-- 1. PAMM INVESTMENT PRODUCTS (3 products)
-- =====================================================
INSERT INTO investment.products (
code, name, description,
trading_agent,
min_investment, max_investment,
target_return_min, target_return_max,
distribution_frequency,
investor_share_percent, platform_share_percent,
recommended_risk_profile,
is_active, is_accepting_new_investors,
total_capacity, current_aum
) VALUES
-- Atlas: Conservative strategy
(
'PAMM-ATLAS',
'Atlas',
'Conservative PAMM strategy focused on capital preservation with steady returns. '
'Targets 3-5% monthly returns through low-risk forex positions and hedging strategies. '
'Ideal for investors seeking stable growth with minimal drawdown.',
'atlas',
1000.00, 500000.00,
3.00, 5.00,
'monthly',
80.00, 20.00,
'conservative',
true, true,
10000000.00, 0.00
),
-- Orion: Moderate strategy
(
'PAMM-ORION',
'Orion',
'Moderate PAMM strategy balancing risk and reward. '
'Targets 5-10% monthly returns through diversified forex and commodity positions. '
'Suitable for investors comfortable with moderate volatility for higher returns.',
'orion',
5000.00, 1000000.00,
5.00, 10.00,
'monthly',
80.00, 20.00,
'moderate',
true, true,
25000000.00, 0.00
),
-- Nova: Aggressive strategy
(
'PAMM-NOVA',
'Nova',
'Aggressive PAMM strategy maximizing return potential. '
'Targets 10%+ monthly returns through high-conviction forex, crypto, and commodity trades. '
'Designed for experienced investors who accept higher volatility for outsized returns.',
'nova',
10000.00, 2000000.00,
10.00, 20.00,
'monthly',
80.00, 20.00,
'aggressive',
true, true,
50000000.00, 0.00
)
ON CONFLICT (code) DO NOTHING;
-- =====================================================
-- VERIFICATION
-- =====================================================
SELECT 'investment.products seed:' AS info, COUNT(*) AS count FROM investment.products;

View File

@ -0,0 +1,73 @@
-- =====================================================
-- SEED DATA - Schema Market Data (Production)
-- =====================================================
-- Project: OrbiQuant IA (Trading Platform)
-- Table: market_data.tickers
-- Description: Market data tickers matching trading symbols
-- Date: 2026-01-27
-- Note: The DDL (01-tickers.sql) contains inline seeds for
-- the same 6 tickers. This seed file serves as the
-- canonical source and uses ON CONFLICT to be idempotent.
-- =====================================================
SET search_path TO market_data, public;
-- =====================================================
-- 1. MARKET DATA TICKERS (6 tickers)
-- =====================================================
INSERT INTO market_data.tickers (
symbol, name,
asset_type, base_currency, quote_currency,
is_ml_enabled, supported_timeframes,
polygon_ticker,
is_active
) VALUES
(
'XAUUSD', 'Gold / US Dollar',
'commodity', 'XAU', 'USD',
true, ARRAY['5m', '15m', '1h'],
'C:XAUUSD',
true
),
(
'EURUSD', 'Euro / US Dollar',
'forex', 'EUR', 'USD',
true, ARRAY['5m', '15m'],
'C:EURUSD',
true
),
(
'GBPUSD', 'British Pound / US Dollar',
'forex', 'GBP', 'USD',
true, ARRAY['5m', '15m'],
'C:GBPUSD',
true
),
(
'USDJPY', 'US Dollar / Japanese Yen',
'forex', 'USD', 'JPY',
true, ARRAY['5m', '15m'],
'C:USDJPY',
true
),
(
'AUDUSD', 'Australian Dollar / US Dollar',
'forex', 'AUD', 'USD',
true, ARRAY['5m', '15m'],
'C:AUDUSD',
true
),
(
'BTCUSD', 'Bitcoin / US Dollar',
'crypto', 'BTC', 'USD',
true, ARRAY['5m', '15m', '1h'],
'X:BTCUSD',
true
)
ON CONFLICT (symbol) DO NOTHING;
-- =====================================================
-- VERIFICATION
-- =====================================================
SELECT 'market_data.tickers seed:' AS info, COUNT(*) AS count FROM market_data.tickers;

View File

@ -0,0 +1,72 @@
-- =====================================================
-- SEED DATA - Schema Trading (Production)
-- =====================================================
-- Project: OrbiQuant IA (Trading Platform)
-- Table: trading.symbols
-- Description: Base trading symbols for forex and crypto pairs
-- Date: 2026-01-27
-- =====================================================
SET search_path TO trading, public;
-- =====================================================
-- 1. TRADING SYMBOLS (6 pairs)
-- =====================================================
INSERT INTO trading.symbols (
symbol, name, base_asset, quote_asset,
asset_class, exchange,
price_precision, quantity_precision,
min_quantity, max_quantity, min_notional,
is_active, is_tradeable
) VALUES
-- Forex pairs
(
'XAUUSD', 'Gold / US Dollar', 'XAU', 'USD',
'forex', 'oanda',
2, 2,
0.01, 100.00, 10.00,
true, true
),
(
'EURUSD', 'Euro / US Dollar', 'EUR', 'USD',
'forex', 'oanda',
5, 2,
0.01, 1000000.00, 1.00,
true, true
),
(
'GBPUSD', 'British Pound / US Dollar', 'GBP', 'USD',
'forex', 'oanda',
5, 2,
0.01, 1000000.00, 1.00,
true, true
),
(
'USDJPY', 'US Dollar / Japanese Yen', 'USD', 'JPY',
'forex', 'oanda',
3, 2,
0.01, 1000000.00, 1.00,
true, true
),
(
'AUDUSD', 'Australian Dollar / US Dollar', 'AUD', 'USD',
'forex', 'oanda',
5, 2,
0.01, 1000000.00, 1.00,
true, true
),
-- Crypto pair
(
'BTCUSD', 'Bitcoin / US Dollar', 'BTC', 'USD',
'crypto', 'binance',
2, 5,
0.00001, 100.00, 10.00,
true, true
)
ON CONFLICT (symbol) DO NOTHING;
-- =====================================================
-- VERIFICATION
-- =====================================================
SELECT 'trading.symbols seed:' AS info, COUNT(*) AS count FROM trading.symbols;