trading-platform-database-v2/ddl/schemas/trading/tables/002_strategies.sql

250 lines
8.0 KiB
PL/PgSQL

-- ============================================================================
-- SCHEMA: trading
-- TABLE: strategies
-- DESCRIPTION: Estrategias de trading definidas
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 3 - DDL Implementation Roadmap Q1-2026
-- ============================================================================
-- Enum para tipo de estrategia
DO $$ BEGIN
CREATE TYPE trading.strategy_type AS ENUM (
'manual', -- Estrategia manual (usuario decide)
'semi_auto', -- Semi-automatica (senales con confirmacion)
'fully_auto', -- Totalmente automatizada
'copy_trading' -- Copy trading de otro usuario/bot
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Enum para metodologia de trading
DO $$ BEGIN
CREATE TYPE trading.trading_methodology AS ENUM (
'trend_following', -- Seguimiento de tendencia
'mean_reversion', -- Reversion a la media
'breakout', -- Rupturas
'scalping', -- Scalping
'swing', -- Swing trading
'position', -- Position trading
'arbitrage', -- Arbitraje
'news_based', -- Basado en noticias
'smc_ict', -- Smart Money Concepts / ICT
'harmonic', -- Patrones armonicos
'custom' -- Personalizada
);
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Tabla de Estrategias
CREATE TABLE IF NOT EXISTS trading.strategies (
-- Identificadores
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
created_by UUID REFERENCES users.users(id),
-- Informacion basica
name VARCHAR(100) NOT NULL,
slug VARCHAR(100) NOT NULL,
description TEXT,
short_description VARCHAR(500),
-- Clasificacion
type trading.strategy_type NOT NULL DEFAULT 'manual',
methodology trading.trading_methodology NOT NULL DEFAULT 'custom',
-- Configuracion de riesgo
risk_config JSONB NOT NULL DEFAULT '{
"risk_per_trade_percent": 2,
"max_daily_loss_percent": 5,
"max_drawdown_percent": 20,
"max_concurrent_trades": 3,
"risk_reward_min": 1.5
}'::JSONB,
-- Configuracion de entrada
entry_config JSONB NOT NULL DEFAULT '{
"order_type": "market",
"slippage_pips": 2,
"max_spread_pips": 5,
"entry_confirmation_required": false
}'::JSONB,
-- Configuracion de salida
exit_config JSONB NOT NULL DEFAULT '{
"use_trailing_stop": false,
"trailing_stop_pips": 20,
"partial_close_enabled": false,
"partial_close_percent": 50,
"breakeven_enabled": false,
"breakeven_pips": 10
}'::JSONB,
-- Simbolos y timeframes
allowed_symbols VARCHAR(20)[], -- NULL = todos
allowed_timeframes trading.timeframe[],
primary_timeframe trading.timeframe DEFAULT 'H1',
-- Horarios de trading
trading_hours JSONB DEFAULT '{
"enabled": false,
"sessions": ["london", "new_york"],
"avoid_news": true,
"news_buffer_minutes": 30
}'::JSONB,
-- Indicadores utilizados
indicators_config JSONB DEFAULT '[]'::JSONB,
-- Reglas de entrada (para estrategias automaticas)
entry_rules JSONB DEFAULT '[]'::JSONB,
-- Reglas de salida
exit_rules JSONB DEFAULT '[]'::JSONB,
-- Estado
is_active BOOLEAN NOT NULL DEFAULT TRUE,
is_public BOOLEAN NOT NULL DEFAULT FALSE, -- Visible para otros usuarios
is_premium BOOLEAN NOT NULL DEFAULT FALSE, -- Requiere suscripcion
-- Estadisticas
total_trades INTEGER NOT NULL DEFAULT 0,
winning_trades INTEGER NOT NULL DEFAULT 0,
losing_trades INTEGER NOT NULL DEFAULT 0,
win_rate DECIMAL(5, 2) DEFAULT 0,
profit_factor DECIMAL(10, 4) DEFAULT 0,
average_profit DECIMAL(15, 4) DEFAULT 0,
average_loss DECIMAL(15, 4) DEFAULT 0,
max_drawdown DECIMAL(15, 4) DEFAULT 0,
total_profit_loss DECIMAL(15, 4) DEFAULT 0,
-- Backtesting
last_backtest_at TIMESTAMPTZ,
backtest_results JSONB,
-- Version control
version INTEGER NOT NULL DEFAULT 1,
previous_version_id UUID,
-- Metadata
tags VARCHAR(50)[],
metadata JSONB DEFAULT '{}'::JSONB,
-- Timestamps
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- Constraints
CONSTRAINT strategies_unique_slug UNIQUE (tenant_id, slug),
CONSTRAINT strategies_win_rate_check CHECK (win_rate BETWEEN 0 AND 100)
);
COMMENT ON TABLE trading.strategies IS
'Estrategias de trading con configuracion de riesgo, entrada y salida';
COMMENT ON COLUMN trading.strategies.risk_config IS
'Configuracion de gestion de riesgo de la estrategia';
-- Indices
CREATE INDEX IF NOT EXISTS idx_strategies_tenant
ON trading.strategies(tenant_id);
CREATE INDEX IF NOT EXISTS idx_strategies_creator
ON trading.strategies(created_by);
CREATE INDEX IF NOT EXISTS idx_strategies_type
ON trading.strategies(type);
CREATE INDEX IF NOT EXISTS idx_strategies_methodology
ON trading.strategies(methodology);
CREATE INDEX IF NOT EXISTS idx_strategies_active
ON trading.strategies(tenant_id, is_active)
WHERE is_active = TRUE;
CREATE INDEX IF NOT EXISTS idx_strategies_public
ON trading.strategies(is_public, win_rate DESC)
WHERE is_public = TRUE;
-- GIN index para tags
CREATE INDEX IF NOT EXISTS idx_strategies_tags_gin
ON trading.strategies USING GIN (tags);
-- Trigger para updated_at
DROP TRIGGER IF EXISTS strategy_updated_at ON trading.strategies;
CREATE TRIGGER strategy_updated_at
BEFORE UPDATE ON trading.strategies
FOR EACH ROW
EXECUTE FUNCTION trading.update_trading_timestamp();
-- Funcion para calcular estadisticas de estrategia
CREATE OR REPLACE FUNCTION trading.recalculate_strategy_stats(p_strategy_id UUID)
RETURNS VOID AS $$
DECLARE
v_stats RECORD;
BEGIN
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE profit_loss > 0) AS wins,
COUNT(*) FILTER (WHERE profit_loss < 0) AS losses,
COALESCE(AVG(profit_loss) FILTER (WHERE profit_loss > 0), 0) AS avg_profit,
COALESCE(AVG(ABS(profit_loss)) FILTER (WHERE profit_loss < 0), 0) AS avg_loss,
COALESCE(SUM(profit_loss), 0) AS total_pnl
INTO v_stats
FROM trading.positions
WHERE strategy_id = p_strategy_id
AND status IN ('closed', 'stopped', 'target_hit');
UPDATE trading.strategies
SET total_trades = v_stats.total,
winning_trades = v_stats.wins,
losing_trades = v_stats.losses,
win_rate = CASE WHEN v_stats.total > 0
THEN (v_stats.wins::DECIMAL / v_stats.total * 100)
ELSE 0 END,
profit_factor = CASE WHEN v_stats.avg_loss > 0
THEN v_stats.avg_profit / v_stats.avg_loss
ELSE 0 END,
average_profit = v_stats.avg_profit,
average_loss = v_stats.avg_loss,
total_profit_loss = v_stats.total_pnl
WHERE id = p_strategy_id;
END;
$$ LANGUAGE plpgsql;
-- Vista de estrategias publicas
CREATE OR REPLACE VIEW trading.v_public_strategies AS
SELECT
id,
name,
description,
type,
methodology,
primary_timeframe,
total_trades,
win_rate,
profit_factor,
max_drawdown,
is_premium,
created_at
FROM trading.strategies
WHERE is_public = TRUE
AND is_active = TRUE
AND total_trades >= 10 -- Minimo de trades para mostrar
ORDER BY win_rate DESC, profit_factor DESC;
-- RLS Policy para multi-tenancy
ALTER TABLE trading.strategies ENABLE ROW LEVEL SECURITY;
CREATE POLICY strategies_tenant_isolation ON trading.strategies
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON trading.strategies TO trading_app;
GRANT SELECT ON trading.strategies TO trading_readonly;
GRANT SELECT ON trading.v_public_strategies TO trading_app;
GRANT EXECUTE ON FUNCTION trading.recalculate_strategy_stats TO trading_app;