-- ============================================================================ -- 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;