diff --git a/docs/01-arquitectura/DATABASE-SCHEMA.md b/docs/01-arquitectura/DATABASE-SCHEMA.md new file mode 100644 index 0000000..d330c87 --- /dev/null +++ b/docs/01-arquitectura/DATABASE-SCHEMA.md @@ -0,0 +1,515 @@ +# DATABASE-SCHEMA.md - Trading Platform + +**Version:** 1.0.0 +**Fecha:** 2026-01-30 +**Motor:** PostgreSQL 16+ +**Proyecto:** trading-platform + +--- + +## Resumen de Schemas + +| Schema | Tablas | Descripción | +|--------|--------|-------------| +| `auth` | 10 | Autenticación, usuarios, sesiones, OAuth | +| `education` | 14 | Cursos, lecciones, quizzes, gamificación | +| `trading` | 10+ | Símbolos, órdenes, posiciones, watchlists | +| `investment` | 7 | Productos PAMM, cuentas, distribuciones | +| `financial` | 10 | Wallets, transacciones, suscripciones | +| `portfolio` | 5+ | Perfiles de riesgo, allocations, goals | +| `market_data` | 4 | OHLCV 5m/15m, staging | +| `ml` | 9 | Modelos, predicciones, feature store | +| `llm` | 5 | Conversaciones, mensajes, embeddings | +| `audit` | 7 | Logs de auditoría, seguridad, compliance | +| `notifications` | 3+ | Notificaciones, preferencias | +| `feature_flags` | 3 | Flags, user overrides, evaluations | + +**Total estimado:** ~90 tablas + +--- + +## Diagrama ER por Schema + +### 1. Schema: auth + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ auth │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ users │──┬──<│ user_profiles │ │ oauth_accounts │ │ +│ ├─────────────────┤ │ ├─────────────────┤ ├─────────────────┤ │ +│ │ id (PK) │ │ │ user_id (FK) │ │ user_id (FK) │ │ +│ │ email │ │ │ first_name │ │ provider │ │ +│ │ password_hash │ │ │ last_name │ │ provider_id │ │ +│ │ status │ │ │ avatar_url │ │ access_token │ │ +│ │ role │ │ │ subscription_id │ │ refresh_token │ │ +│ │ mfa_enabled │ │ │ trading_exp │ └─────────────────┘ │ +│ │ mfa_secret │ │ │ risk_tolerance │ │ +│ │ backup_codes │ │ │ ... │ │ +│ │ ... │ │ └─────────────────┘ │ +│ └─────────────────┘ │ │ +│ │ │ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ └──<│ sessions │ │ auth_logs │ │ +│ │ ├─────────────────┤ ├─────────────────┤ │ +│ │ │ user_id (FK) │ │ user_id (FK) │ │ +│ │ │ token_hash │ │ event │ │ +│ │ │ ip_address │ │ ip_address │ │ +│ │ │ user_agent │ │ success │ │ +│ │ │ expires_at │ │ created_at │ │ +│ │ └─────────────────┘ └─────────────────┘ │ +│ │ │ +│ │ ┌──────────────────┐ ┌──────────────────┐ │ +│ └──<│ email_verify │ │ password_resets │ │ +│ ├──────────────────┤ ├──────────────────┤ │ +│ │ user_id (FK) │ │ user_id (FK) │ │ +│ │ token │ │ token │ │ +│ │ expires_at │ │ expires_at │ │ +│ └──────────────────┘ └──────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +### 2. Schema: education + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ education │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌───────────┐│ +│ │ categories │───<│ courses │───<│ modules │──<│ lessons ││ +│ ├─────────────┤ ├─────────────┤ ├─────────────┤ ├───────────┤│ +│ │ id (PK) │ │ id (PK) │ │ id (PK) │ │ id (PK) ││ +│ │ name │ │ category_id │ │ course_id │ │ module_id ││ +│ │ slug │ │ title │ │ title │ │ title ││ +│ │ description │ │ difficulty │ │ order │ │ type ││ +│ └─────────────┘ │ duration │ └─────────────┘ │ content ││ +│ │ price │ │ video_url ││ +│ │ status │ │ duration ││ +│ └─────────────┘ └───────────┘│ +│ │ │ │ +│ │ │ │ +│ ┌─────────────────┐ │ ┌─────────────────┐ │ │ +│ │ enrollments │<──┘ │ quizzes │<───────────┘ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ user_id (FK) │ │ lesson_id (FK) │ │ +│ │ course_id (FK) │ │ title │ │ +│ │ progress_pct │ │ passing_score │ │ +│ │ status │ │ time_limit │ │ +│ │ completed_at │ └────────┬────────┘ │ +│ └────────┬────────┘ │ │ +│ │ │ │ +│ ┌────────┴────────┐ ┌────────────┴──────────┐ │ +│ │ progress │ │ quiz_questions │ │ +│ ├─────────────────┤ ├───────────────────────┤ │ +│ │ enrollment_id │ │ quiz_id (FK) │ │ +│ │ lesson_id (FK) │ │ question_text │ │ +│ │ completed │ │ question_type │ │ +│ │ completed_at │ │ options (JSONB) │ │ +│ └─────────────────┘ │ correct_answer │ │ +│ └───────────────────────┘ │ +│ │ +│ ┌───────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │ +│ │ certificates │ │ user_achievements │ │ gamification │ │ +│ ├───────────────────┤ ├───────────────────┤ │ _profile │ │ +│ │ user_id (FK) │ │ user_id (FK) │ ├─────────────────┤ │ +│ │ course_id (FK) │ │ achievement_type │ │ user_id (FK) │ │ +│ │ issued_at │ │ earned_at │ │ xp_total │ │ +│ │ certificate_url │ │ metadata │ │ level │ │ +│ └───────────────────┘ └───────────────────┘ │ streak_days │ │ +│ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────────────┘ +``` + +### 3. Schema: trading + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ trading │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ │ +│ │ symbols │──────────────────────────────┐ │ +│ ├─────────────────┤ │ │ +│ │ id (PK) │ │ │ +│ │ name (XAUUSD) │ │ │ +│ │ base_currency │ │ │ +│ │ quote_currency │ │ │ +│ │ pip_value │ │ │ +│ │ is_active │ │ │ +│ └─────────────────┘ │ │ +│ │ │ │ +│ │ ┌─────────────────┐ ┌────────────┴──────────┐ │ +│ └────<│ watchlists │ │ paper_accounts │ │ +│ ├─────────────────┤ ├───────────────────────┤ │ +│ │ user_id (FK) │ │ user_id (FK) │ │ +│ │ name │ │ balance │ │ +│ │ symbols[] │ │ equity │ │ +│ │ is_default │ │ margin_used │ │ +│ └─────────────────┘ │ realized_pnl │ │ +│ └───────────┬───────────┘ │ +│ │ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ │ +│ │ trade_bots │───<│ bot_positions │ │ │ +│ ├─────────────────┤ ├─────────────────┤ │ │ +│ │ id (PK) │ │ bot_id (FK) │ │ │ +│ │ user_id (FK) │ │ symbol │ │ │ +│ │ name │ │ side (BUY/SELL) │ │ │ +│ │ strategy │ │ size │ │ │ +│ │ status │ │ entry_price │ │ │ +│ │ risk_per_trade │ │ current_price │ │ │ +│ └─────────────────┘ │ pnl │ │ │ +│ └─────────────────┘ │ │ +│ │ │ +│ ┌─────────────────────┐ ┌─────────────────┴───────┐ │ +│ │ paper_orders │ │ paper_positions │ │ +│ ├─────────────────────┤ ├─────────────────────────┤ │ +│ │ account_id (FK) │ │ account_id (FK) │ │ +│ │ symbol │ │ symbol │ │ +│ │ type (MARKET/LIMIT) │ │ side │ │ +│ │ side │ │ quantity │ │ +│ │ quantity │ │ entry_price │ │ +│ │ price │ │ current_price │ │ +│ │ status │ │ unrealized_pnl │ │ +│ │ filled_at │ │ stop_loss │ │ +│ └─────────────────────┘ │ take_profit │ │ +│ └─────────────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ signals │ │ user_signals │ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ id (PK) │ │ user_id (FK) │ │ +│ │ symbol │ │ signal_id (FK) │ │ +│ │ direction │ │ followed_at │ │ +│ │ entry_price │ │ status │ │ +│ │ confidence │ └─────────────────┘ │ +│ │ created_at │ │ +│ └─────────────────┘ │ +│ │ +└───────────────────────────────────────────────────────────────────────┘ +``` + +### 4. Schema: investment + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ investment │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────┐ │ +│ │ products │ (PAMM accounts: Atlas, Orion, Nova) │ +│ ├─────────────────────┤ │ +│ │ id (PK) │ │ +│ │ name │───────────────────────────────┐ │ +│ │ code (ATLAS/ORION) │ │ │ +│ │ risk_profile │ │ │ +│ │ target_return │ │ │ +│ │ max_drawdown │ │ │ +│ │ min_investment │ │ │ +│ │ management_fee │ │ │ +│ │ performance_fee │ │ │ +│ │ status │ │ │ +│ └─────────────────────┘ │ │ +│ │ │ +│ ┌─────────────────────┐ ┌─────────────────────┐│ │ +│ │ risk_questionnaire │ │ accounts ││ │ +│ ├─────────────────────┤ ├─────────────────────┤│ │ +│ │ user_id (FK) │ │ id (PK) ││ │ +│ │ answers (JSONB) │ │ user_id (FK) │┘ │ +│ │ risk_score │ │ product_id (FK) │ │ +│ │ profile_type │ │ balance │ │ +│ │ completed_at │ │ status │ │ +│ └─────────────────────┘ │ performance_pct │ │ +│ │ opened_at │ │ +│ └──────────┬──────────┘ │ +│ │ │ +│ ┌─────────────────────┐ │ │ +│ │ distributions │<─────────────┤ │ +│ ├─────────────────────┤ │ │ +│ │ account_id (FK) │ │ │ +│ │ amount │ │ │ +│ │ distribution_date │ │ │ +│ │ status │ │ │ +│ └─────────────────────┘ │ │ +│ │ │ +│ ┌─────────────────────┐ ┌────────┴────────────┐ │ +│ │ withdrawal_requests │ │ transactions │ │ +│ ├─────────────────────┤ ├─────────────────────┤ │ +│ │ account_id (FK) │ │ account_id (FK) │ │ +│ │ amount │ │ type (deposit/ │ │ +│ │ status │ │ withdrawal) │ │ +│ │ requested_at │ │ amount │ │ +│ │ processed_at │ │ status │ │ +│ └─────────────────────┘ │ created_at │ │ +│ └─────────────────────┘ │ +│ │ +│ ┌─────────────────────┐ │ +│ │ daily_performance │ │ +│ ├─────────────────────┤ │ +│ │ product_id (FK) │ │ +│ │ date │ │ +│ │ nav │ │ +│ │ return_pct │ │ +│ │ drawdown │ │ +│ └─────────────────────┘ │ +│ │ +└──────────────────────────────────────────────────────────────────────┘ +``` + +### 5. Schema: financial + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ financial │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ customers │ │ subscriptions │ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ user_id (FK) │────────────────────│ user_id (FK) │ │ +│ │ stripe_id │ │ plan_id │ │ +│ │ created_at │ │ status │ │ +│ └─────────────────┘ │ stripe_sub_id │ │ +│ │ │ current_period │ │ +│ │ └─────────────────┘ │ +│ │ │ +│ ┌───────┴─────────┐ ┌─────────────────┐ │ +│ │ wallets │───<│ wallet_trans │ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ user_id (FK) │ │ wallet_id (FK) │ │ +│ │ balance │ │ type │ │ +│ │ currency │ │ amount │ │ +│ │ updated_at │ │ status │ │ +│ └─────────────────┘ │ reference_id │ │ +│ └─────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ invoices │───<│ payments │ │ payment_methods │ │ +│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ +│ │ user_id (FK) │ │ invoice_id (FK) │ │ user_id (FK) │ │ +│ │ amount │ │ amount │ │ type │ │ +│ │ status │ │ method │ │ stripe_pm_id │ │ +│ │ due_date │ │ status │ │ is_default │ │ +│ │ paid_at │ │ processed_at │ │ last_four │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +│ │ +│ ┌─────────────────────────┐ ┌─────────────────────────┐ │ +│ │ currency_exchange_rates │ │ wallet_limits │ │ +│ ├─────────────────────────┤ ├─────────────────────────┤ │ +│ │ from_currency │ │ user_id (FK) │ │ +│ │ to_currency │ │ daily_limit │ │ +│ │ rate │ │ monthly_limit │ │ +│ │ updated_at │ │ single_tx_limit │ │ +│ └─────────────────────────┘ └─────────────────────────┘ │ +│ │ +└────────────────────────────────────────────────────────────────────────┘ +``` + +### 6. Schema: ml + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ ml │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ models │───<│ model_versions │ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ id (PK) │ │ model_id (FK) │ │ +│ │ name │ │ version │ │ +│ │ type │ │ metrics (JSONB) │ │ +│ │ symbol │ │ file_path │ │ +│ │ status │ │ is_active │ │ +│ └─────────────────┘ │ created_at │ │ +│ └─────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────────┐ │ +│ │ predictions │───<│ prediction_outcomes │ │ +│ ├─────────────────┤ ├─────────────────────┤ │ +│ │ model_id (FK) │ │ prediction_id (FK) │ │ +│ │ symbol │ │ actual_high │ │ +│ │ horizon │ │ actual_low │ │ +│ │ predicted_high │ │ accuracy │ │ +│ │ predicted_low │ │ evaluated_at │ │ +│ │ confidence │ └─────────────────────┘ │ +│ │ created_at │ │ +│ └─────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ feature_store │ │ llm_predictions │ │ llm_decisions │ │ +│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ +│ │ symbol │ │ id (PK) │ │ prediction_id │ │ +│ │ timestamp │ │ user_id (FK) │ │ decision │ │ +│ │ features (JSONB)│ │ symbol │ │ reasoning │ │ +│ │ created_at │ │ analysis │ │ confidence │ │ +│ └─────────────────┘ │ recommendation │ │ created_at │ │ +│ └─────────────────┘ └─────────────────┘ │ +│ │ +│ ┌─────────────────┐ │ +│ │ risk_events │ (Risk management alerts) │ +│ ├─────────────────┤ │ +│ │ user_id (FK) │ │ +│ │ event_type │ │ +│ │ severity │ │ +│ │ details (JSONB) │ │ +│ │ created_at │ │ +│ └─────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 7. Schema: llm + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ llm │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ conversations │───<│ messages │ │ +│ ├─────────────────┤ ├─────────────────┤ │ +│ │ id (PK) │ │ conversation_id │ │ +│ │ user_id (FK) │ │ role │ │ +│ │ title │ │ content │ │ +│ │ created_at │ │ tools_used │ │ +│ │ updated_at │ │ tokens_used │ │ +│ └─────────────────┘ │ created_at │ │ +│ └─────────────────┘ │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │user_preferences │ │ user_memory │ │ embeddings │ │ +│ ├─────────────────┤ ├─────────────────┤ ├─────────────────┤ │ +│ │ user_id (FK) │ │ user_id (FK) │ │ id (PK) │ │ +│ │ model_provider │ │ key │ │ content_type │ │ +│ │ temperature │ │ value │ │ content_id │ │ +│ │ context_length │ │ category │ │ embedding │ │ (pgvector) +│ │ system_prompt │ │ expires_at │ │ metadata │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 8. Schema: audit + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ audit │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ audit_logs │ │ security_events │ │ +│ ├─────────────────────┤ ├─────────────────────┤ │ +│ │ id (PK) │ │ id (PK) │ │ +│ │ event_type │ │ category │ │ +│ │ event_status │ │ severity │ │ +│ │ severity │ │ user_id (FK) │ │ +│ │ user_id (FK) │ │ ip_address │ │ +│ │ resource_type │ │ event_code │ │ +│ │ resource_id │ │ is_blocked │ │ +│ │ action │ │ requires_review │ │ +│ │ old_values (JSONB) │ │ raw_data (JSONB) │ │ +│ │ new_values (JSONB) │ │ created_at │ │ +│ │ ip_address │ └─────────────────────┘ │ +│ │ created_at │ │ +│ └─────────────────────┘ │ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ compliance_logs │ │ trading_audit │ │ +│ ├─────────────────────┤ ├─────────────────────┤ │ +│ │ id (PK) │ │ id (PK) │ │ +│ │ regulation │ │ user_id (FK) │ │ +│ │ requirement │ │ action │ │ +│ │ compliance_status │ │ symbol │ │ +│ │ risk_level │ │ order_details │ │ +│ │ evidence (JSONB) │ │ execution_time │ │ +│ │ remediation_req │ │ created_at │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ api_request_logs │ │ data_access_logs │ │ +│ ├─────────────────────┤ ├─────────────────────┤ │ +│ │ endpoint │ │ user_id (FK) │ │ +│ │ method │ │ table_name │ │ +│ │ status_code │ │ operation │ │ +│ │ response_time_ms │ │ record_id │ │ +│ │ user_id (FK) │ │ fields_accessed │ │ +│ │ created_at │ │ created_at │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +### 9. Schema: feature_flags + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ feature_flags │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ flags │───<│ user_flags │ │ +│ ├─────────────────────┤ ├─────────────────────┤ │ +│ │ id (PK) │ │ user_id (FK) │ │ +│ │ code (UNIQUE) │ │ flag_id (FK) │ │ +│ │ name │ │ is_enabled │ │ +│ │ description │ │ reason │ │ +│ │ category │ │ expires_at │ │ +│ │ status │ └─────────────────────┘ │ +│ │ rollout_percentage │ │ +│ │ targeting_rules │ ┌─────────────────────┐ │ +│ │ expires_at │───<│ evaluations │ │ +│ └─────────────────────┘ ├─────────────────────┤ │ +│ │ flag_id (FK) │ │ +│ │ user_id (FK) │ │ +│ │ flag_code │ │ +│ │ result │ │ +│ │ evaluation_reason │ │ +│ │ created_at │ │ +│ └─────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Extensiones PostgreSQL Utilizadas + +| Extensión | Uso | +|-----------|-----| +| `uuid-ossp` | Generación de UUIDs | +| `pgcrypto` | Funciones criptográficas | +| `citext` | Texto case-insensitive (emails) | +| `pgvector` | Embeddings para LLM (schema llm) | + +--- + +## Convenciones de Nomenclatura + +| Elemento | Convención | Ejemplo | +|----------|------------|---------| +| Tablas | snake_case, plural | `audit_logs`, `user_profiles` | +| Columnas | snake_case | `created_at`, `user_id` | +| PKs | `id` (UUID) | `id UUID PRIMARY KEY` | +| FKs | `{tabla}_id` | `user_id`, `course_id` | +| Índices | `idx_{tabla}_{columnas}` | `idx_users_email` | +| Enums | `{schema}.{nombre}` | `auth.user_status` | +| Timestamps | `_at` suffix | `created_at`, `updated_at` | + +--- + +## Credenciales de Conexión + +``` +Database: trading_platform +User: trading_user +Password: trading_dev_2026 +Host: localhost +Port: 5432 +``` + +--- + +**Última actualización:** 2026-01-30 +**Generado por:** Claude Code (Opus 4.5) - Sprint 4 diff --git a/docs/95-guias-desarrollo/TESTING-STRATEGY.md b/docs/95-guias-desarrollo/TESTING-STRATEGY.md new file mode 100644 index 0000000..be98822 --- /dev/null +++ b/docs/95-guias-desarrollo/TESTING-STRATEGY.md @@ -0,0 +1,638 @@ +# TESTING-STRATEGY.md - Trading Platform + +**Version:** 1.0.0 +**Fecha:** 2026-01-30 +**Proyecto:** trading-platform + +--- + +## Resumen Ejecutivo + +Este documento define la estrategia de testing para Trading Platform, cubriendo todos los niveles de la pirámide de testing y consideraciones específicas para sistemas de trading con ML. + +--- + +## Niveles de Testing + +### Pirámide de Testing + +``` + ┌─────────────┐ + │ E2E │ 10% + │ Tests │ + ┌──┴─────────────┴──┐ + │ Integration │ 20% + │ Tests │ + ┌──┴───────────────────┴──┐ + │ Unit Tests │ 70% + └─────────────────────────┘ +``` + +| Nivel | Porcentaje | Responsabilidad | +|-------|------------|-----------------| +| Unit | 70% | Lógica de negocio aislada | +| Integration | 20% | Interacción entre módulos | +| E2E | 10% | Flujos críticos completos | + +--- + +## 1. Unit Tests + +### 1.1 Backend (TypeScript/Express) + +**Framework:** Jest + ts-jest + +**Estructura:** +``` +apps/backend/src/ +├── modules/ +│ ├── auth/ +│ │ ├── services/ +│ │ │ ├── token.service.ts +│ │ │ └── __tests__/ +│ │ │ └── token.service.spec.ts +│ │ └── ... +│ └── ... +``` + +**Convenciones:** +- Archivos: `*.spec.ts` junto al archivo fuente +- Naming: `describe('ServiceName')` > `describe('methodName')` > `it('should...')` +- Mocks: Usar `jest.mock()` para dependencias externas + +**Ejemplo:** +```typescript +// token.service.spec.ts +describe('TokenService', () => { + describe('generateAccessToken', () => { + it('should return a valid JWT token', () => { + const token = tokenService.generateAccessToken(mockUser); + expect(token).toMatch(/^eyJ/); + }); + + it('should include user id in payload', () => { + const token = tokenService.generateAccessToken(mockUser); + const decoded = jwt.decode(token); + expect(decoded.sub).toBe(mockUser.id); + }); + }); +}); +``` + +**Coverage mínimo:** 80% + +### 1.2 Frontend (React) + +**Framework:** Vitest + React Testing Library + +**Estructura:** +``` +apps/frontend/src/ +├── components/ +│ ├── Button/ +│ │ ├── Button.tsx +│ │ └── Button.test.tsx +│ └── ... +├── hooks/ +│ ├── useAuth.ts +│ └── __tests__/ +│ └── useAuth.test.ts +``` + +**Convenciones:** +- Archivos: `*.test.tsx` o `*.test.ts` +- Testing Library: Preferir queries por rol y accesibilidad +- Avoid testing implementation details + +**Ejemplo:** +```typescript +// Button.test.tsx +describe('Button', () => { + it('should call onClick when clicked', async () => { + const handleClick = vi.fn(); + render(); + + await userEvent.click(screen.getByRole('button', { name: /click me/i })); + + expect(handleClick).toHaveBeenCalledTimes(1); + }); +}); +``` + +**Coverage mínimo:** 70% + +### 1.3 ML Engine (Python) + +**Framework:** pytest + pytest-cov + +**Estructura:** +``` +apps/ml-engine/ +├── src/ +│ ├── models/ +│ │ └── range_predictor.py +│ └── ... +├── tests/ +│ ├── unit/ +│ │ ├── test_range_predictor.py +│ │ └── test_feature_engineering.py +│ └── ... +``` + +**Convenciones:** +- Archivos: `test_*.py` +- Fixtures: Usar `@pytest.fixture` para datos de prueba +- Parametrize: Usar `@pytest.mark.parametrize` para múltiples casos + +**Ejemplo:** +```python +# test_range_predictor.py +class TestRangePredictor: + @pytest.fixture + def predictor(self): + return RangePredictor(symbol='XAUUSD') + + def test_predict_returns_valid_range(self, predictor, sample_features): + result = predictor.predict(sample_features) + + assert 'predicted_high' in result + assert 'predicted_low' in result + assert result['predicted_high'] > result['predicted_low'] + + @pytest.mark.parametrize('confidence_threshold', [0.5, 0.7, 0.9]) + def test_filter_by_confidence(self, predictor, confidence_threshold): + # Test filtering logic + pass +``` + +**Coverage mínimo:** 75% + +--- + +## 2. Integration Tests + +### 2.1 API Integration Tests + +**Framework:** Jest + Supertest + +**Objetivo:** Verificar endpoints con base de datos real (test DB) + +**Setup:** +```typescript +// setup/test-db.ts +beforeAll(async () => { + await setupTestDatabase(); +}); + +afterAll(async () => { + await teardownTestDatabase(); +}); + +beforeEach(async () => { + await clearTables(); +}); +``` + +**Ejemplo:** +```typescript +// auth.integration.spec.ts +describe('Auth API Integration', () => { + describe('POST /api/v1/auth/login', () => { + it('should return tokens for valid credentials', async () => { + // Arrange + await createTestUser({ email: 'test@example.com', password: 'Test123!' }); + + // Act + const response = await request(app) + .post('/api/v1/auth/login') + .send({ email: 'test@example.com', password: 'Test123!' }); + + // Assert + expect(response.status).toBe(200); + expect(response.body.data).toHaveProperty('accessToken'); + expect(response.body.data).toHaveProperty('refreshToken'); + }); + }); +}); +``` + +### 2.2 Database Integration Tests + +**Objetivo:** Verificar queries, triggers y funciones PL/pgSQL + +```typescript +// wallet.integration.spec.ts +describe('Wallet Transactions', () => { + it('should update balance after deposit', async () => { + const wallet = await createTestWallet({ balance: 100 }); + + await db.query( + 'SELECT financial.process_transaction($1, $2, $3)', + [wallet.id, 'deposit', 50] + ); + + const updated = await getWallet(wallet.id); + expect(updated.balance).toBe(150); + }); +}); +``` + +### 2.3 ML Pipeline Integration Tests + +**Objetivo:** Verificar pipeline completo de predicción + +```python +# test_prediction_pipeline.py +class TestPredictionPipeline: + def test_full_prediction_flow(self, db_session, redis_client): + # 1. Load features from feature store + features = load_features('XAUUSD', '2024-01-01') + + # 2. Run prediction + prediction = run_prediction(features) + + # 3. Verify stored in database + stored = db_session.query(Prediction).filter_by( + symbol='XAUUSD' + ).first() + + assert stored is not None + assert stored.predicted_high == prediction['high'] +``` + +--- + +## 3. E2E Tests + +### 3.1 Framework + +**Herramienta:** Playwright + +**Estructura:** +``` +e2e/ +├── tests/ +│ ├── auth/ +│ │ ├── login.spec.ts +│ │ └── register.spec.ts +│ ├── trading/ +│ │ └── place-order.spec.ts +│ └── ... +├── fixtures/ +│ └── test-data.ts +└── playwright.config.ts +``` + +### 3.2 Flujos Críticos a Testear + +| Prioridad | Flujo | Descripción | +|-----------|-------|-------------| +| P0 | Login/Logout | Autenticación completa | +| P0 | Registro + Verificación | Nuevo usuario | +| P0 | Depositar fondos | Wallet + Stripe | +| P1 | Abrir cuenta inversión | PAMM flow | +| P1 | Paper trading | Orden → Posición | +| P2 | Completar curso | Education flow | +| P2 | 2FA setup | Security flow | + +### 3.3 Ejemplo E2E + +```typescript +// e2e/tests/auth/login.spec.ts +import { test, expect } from '@playwright/test'; + +test.describe('Login Flow', () => { + test('user can login with valid credentials', async ({ page }) => { + // Navigate + await page.goto('/login'); + + // Fill form + await page.getByLabel('Email').fill('test@example.com'); + await page.getByLabel('Password').fill('Test123!'); + + // Submit + await page.getByRole('button', { name: /sign in/i }).click(); + + // Verify redirect to dashboard + await expect(page).toHaveURL('/dashboard'); + await expect(page.getByText('Welcome back')).toBeVisible(); + }); + + test('shows error for invalid credentials', async ({ page }) => { + await page.goto('/login'); + await page.getByLabel('Email').fill('wrong@example.com'); + await page.getByLabel('Password').fill('wrongpassword'); + await page.getByRole('button', { name: /sign in/i }).click(); + + await expect(page.getByText(/invalid credentials/i)).toBeVisible(); + }); +}); +``` + +--- + +## 4. Testing Específico para Trading + +### 4.1 Backtesting de Modelos ML + +**Objetivo:** Validar performance de modelos con datos históricos + +```python +# tests/backtesting/test_model_performance.py +class TestModelBacktest: + def test_model_accuracy_oos(self): + """Test Out-of-Sample accuracy""" + # Load 12 months of excluded data + oos_data = load_oos_data('2025-01', '2025-12') + + # Run predictions + predictions = model.predict(oos_data.features) + + # Calculate metrics + accuracy = calculate_directional_accuracy( + predictions, + oos_data.actual + ) + + # Minimum threshold + assert accuracy >= 0.55, f"OOS accuracy {accuracy} below threshold" +``` + +### 4.2 Paper Trading Tests + +**Objetivo:** Simular operaciones sin riesgo + +```typescript +// tests/paper-trading/order-execution.spec.ts +describe('Paper Trading Order Execution', () => { + it('should execute market order at current price', async () => { + const account = await createPaperAccount({ balance: 10000 }); + + const order = await paperTradingService.placeOrder({ + accountId: account.id, + symbol: 'XAUUSD', + side: 'BUY', + type: 'MARKET', + quantity: 0.1 + }); + + expect(order.status).toBe('FILLED'); + expect(order.filledPrice).toBeCloseTo(currentPrice, 2); + }); +}); +``` + +### 4.3 Risk Management Tests + +```typescript +// tests/risk/position-limits.spec.ts +describe('Position Limits', () => { + it('should reject order exceeding max position size', async () => { + const result = await riskService.validateOrder({ + userId: testUser.id, + symbol: 'XAUUSD', + quantity: 100 // Exceeds limit + }); + + expect(result.allowed).toBe(false); + expect(result.reason).toContain('position limit'); + }); +}); +``` + +--- + +## 5. Mocking Strategies + +### 5.1 External Services + +| Servicio | Estrategia | +|----------|------------| +| Stripe | Mock SDK responses | +| Binance API | Recorded fixtures | +| Redis | Redis-mock o real | +| LLM (Claude) | Canned responses | + +### 5.2 Mock de Stripe + +```typescript +// __mocks__/stripe.ts +export const mockStripe = { + customers: { + create: jest.fn().mockResolvedValue({ id: 'cus_test123' }), + retrieve: jest.fn().mockResolvedValue({ id: 'cus_test123', email: 'test@example.com' }) + }, + subscriptions: { + create: jest.fn().mockResolvedValue({ id: 'sub_test123', status: 'active' }) + } +}; + +jest.mock('stripe', () => ({ + __esModule: true, + default: jest.fn(() => mockStripe) +})); +``` + +### 5.3 Mock de Market Data + +```python +# tests/fixtures/market_data.py +@pytest.fixture +def mock_binance_client(mocker): + mock = mocker.patch('binance.Client') + mock.return_value.get_klines.return_value = [ + [1704067200000, "2050.00", "2055.00", "2048.00", "2052.00", "1000"], + [1704067260000, "2052.00", "2058.00", "2051.00", "2056.00", "1200"], + ] + return mock +``` + +--- + +## 6. CI/CD Integration + +### 6.1 GitHub Actions Workflow + +```yaml +# .github/workflows/test.yml +name: Tests + +on: [push, pull_request] + +jobs: + unit-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Install dependencies + run: npm ci + - name: Run unit tests + run: npm run test:unit + - name: Upload coverage + uses: codecov/codecov-action@v3 + + integration-tests: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16 + env: + POSTGRES_DB: trading_test + POSTGRES_USER: test + POSTGRES_PASSWORD: test + ports: + - 5432:5432 + redis: + image: redis:7 + ports: + - 6379:6379 + steps: + - uses: actions/checkout@v4 + - name: Run integration tests + run: npm run test:integration + + e2e-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Playwright + run: npx playwright install --with-deps + - name: Run E2E tests + run: npm run test:e2e +``` + +### 6.2 Pre-commit Hooks + +```yaml +# .pre-commit-config.yaml +repos: + - repo: local + hooks: + - id: test-affected + name: Run affected tests + entry: npm run test:affected + language: system + pass_filenames: false +``` + +--- + +## 7. Test Data Management + +### 7.1 Factories + +```typescript +// tests/factories/user.factory.ts +export const userFactory = { + build: (overrides = {}) => ({ + id: faker.string.uuid(), + email: faker.internet.email(), + passwordHash: bcrypt.hashSync('Test123!', 10), + status: 'active', + role: 'user', + ...overrides + }), + + create: async (overrides = {}) => { + const user = userFactory.build(overrides); + await db.query('INSERT INTO auth.users ...', [user]); + return user; + } +}; +``` + +### 7.2 Fixtures para ML + +```python +# tests/conftest.py +@pytest.fixture(scope='session') +def sample_ohlcv_data(): + return pd.read_parquet('tests/fixtures/xauusd_sample.parquet') + +@pytest.fixture(scope='session') +def trained_model(): + return joblib.load('tests/fixtures/model_v1.joblib') +``` + +--- + +## 8. Comandos de Ejecución + +### Backend +```bash +# Unit tests +npm run test:unit + +# Integration tests +npm run test:integration + +# Coverage +npm run test:coverage +``` + +### Frontend +```bash +# Unit tests +npm run test + +# Watch mode +npm run test:watch + +# Coverage +npm run test:coverage +``` + +### ML Engine +```bash +# All tests +pytest + +# Unit only +pytest tests/unit/ + +# With coverage +pytest --cov=src --cov-report=html +``` + +### E2E +```bash +# Headless +npm run test:e2e + +# UI mode +npm run test:e2e:ui + +# Specific browser +npx playwright test --project=chromium +``` + +--- + +## 9. Métricas y Umbrales + +| Métrica | Umbral | Medición | +|---------|--------|----------| +| Unit Test Coverage | ≥ 70% | Codecov | +| Integration Test Coverage | ≥ 50% | Codecov | +| E2E Critical Paths | 100% pass | Playwright | +| ML Model Accuracy (OOS) | ≥ 55% | Custom | +| Test Execution Time | < 10 min | CI | + +--- + +## 10. Próximos Pasos + +1. **Implementar tests faltantes por módulo** +2. **Configurar CI/CD con GitHub Actions** +3. **Integrar Codecov para tracking de coverage** +4. **Crear fixtures de datos de mercado** +5. **Implementar backtesting automatizado** + +--- + +**Última actualización:** 2026-01-30 +**Generado por:** Claude Code (Opus 4.5) - Sprint 4