$ErrorActionPreference = "Stop" $root = Split-Path -Parent $MyInvocation.MyCommand.Path $stateDir = Join-Path $root ".orchestration" $pidFile = Join-Path $stateDir "pids.json" $python = Join-Path $root ".venv\\Scripts\\python.exe" $status = [ordered]@{} $engineExternal = $env:ENGINE_EXTERNAL -and $env:ENGINE_EXTERNAL.ToLower() -in @("1", "true", "yes") if (-not $env:DB_HOST) { $env:DB_HOST = "localhost" } if (-not $env:DB_PORT) { $env:DB_PORT = "5432" } if (-not $env:DB_NAME) { $env:DB_NAME = "trading_db" } if (-not $env:DB_USER) { $env:DB_USER = "trader" } if (-not $env:DB_PASSWORD) { $env:DB_PASSWORD = "traderpass" } if (-not $env:PGHOST) { $env:PGHOST = $env:DB_HOST } if (-not $env:PGPORT) { $env:PGPORT = $env:DB_PORT } if (-not $env:PGDATABASE) { $env:PGDATABASE = $env:DB_NAME } if (-not $env:PGUSER) { $env:PGUSER = $env:DB_USER } if (-not $env:PGPASSWORD) { $env:PGPASSWORD = $env:DB_PASSWORD } function Write-Status { param( [string]$Label, [bool]$Ok, [string]$Detail = "" ) if ($Ok) { $status[$Label] = "OK" Write-Host ("[OK] {0} {1}" -f $Label, $Detail) -ForegroundColor Green } else { $status[$Label] = "FAIL" Write-Host ("[FAIL] {0} {1}" -f $Label, $Detail) -ForegroundColor Red } } if (-not (Test-Path $python)) { Write-Status "Python venv" $false $python exit 1 } Write-Status "Python venv" $true $python New-Item -ItemType Directory -Force -Path $stateDir | Out-Null if (Test-Path $pidFile) { try { $pids = Get-Content $pidFile | ConvertFrom-Json $running = $false foreach ($name in @("backend", "engine", "frontend")) { $pid = $pids.$name if ($pid) { $proc = Get-Process -Id $pid -ErrorAction SilentlyContinue if ($proc) { $running = $true } } } if ($running) { Write-Host "Services already running. Use .\\stop_all.ps1 to stop." exit 0 } } catch { Remove-Item $pidFile -Force -ErrorAction SilentlyContinue } } Write-Host "Starting PostgreSQL (docker)..." $pgRunning = "" docker ps --filter "name=trading_postgres" --format "{{.ID}}" | ForEach-Object { $pgRunning = $_ } if ($LASTEXITCODE -ne 0) { Write-Status "Docker" $false "not running" exit 1 } Write-Status "Docker" $true "running" if (-not $pgRunning) { docker compose up -d postgres if ($LASTEXITCODE -ne 0) { Write-Status "PostgreSQL" $false "failed to start" exit 1 } } Write-Status "PostgreSQL" $true "container up" Write-Host "Applying migrations..." if (Test-Path (Join-Path $root "alembic.ini")) { & $python -m alembic upgrade head } elseif (Test-Path (Join-Path $root "backend\\alembic.ini")) { & $python -m alembic -c (Join-Path $root "backend\\alembic.ini") upgrade head } & $python (Join-Path $root "backend\\scripts\\run_migrations.py") if ($LASTEXITCODE -ne 0) { Write-Status "Migrations" $false "failed" exit 1 } Write-Status "Migrations" $true "applied" Write-Host "Starting backend..." $backendCmd = "& { `$env:PYTHONPATH = '$root;$root\\backend'; cd '$root\\backend'; & '$python' -m uvicorn app.main:app --reload }" $backendProc = Start-Process -FilePath "powershell" -ArgumentList @( "-NoExit", "-Command", $backendCmd ) -PassThru Write-Status "Backend" $true "pid $($backendProc.Id)" if ($engineExternal) { Write-Host "Starting engine runner..." $engineCmd = "& { `$env:PYTHONPATH = '$root;$root\\backend'; cd '$root'; & '$python' -m indian_paper_trading_strategy.engine.engine_runner }" $engineProc = Start-Process -FilePath "powershell" -ArgumentList @( "-NoExit", "-Command", $engineCmd ) -PassThru Write-Status "Engine" $true "pid $($engineProc.Id)" } else { Write-Host "Engine runner skipped (ENGINE_EXTERNAL not set). Backend will run engine." Write-Status "Engine" $true "embedded" } Write-Host "Starting frontend..." $frontendProc = Start-Process -FilePath "powershell" -ArgumentList @( "-NoExit", "-Command", "cd '$root\\frontend'; npm run dev" ) -PassThru Write-Status "Frontend" $true "pid $($frontendProc.Id)" @{ backend = $backendProc.Id engine = if ($engineExternal) { $engineProc.Id } else { $null } frontend = $frontendProc.Id started_at = (Get-Date).ToString("o") } | ConvertTo-Json | Set-Content -Encoding ascii $pidFile Write-Host "Waiting for backend health..." $healthUrl = "http://localhost:8000/health" $deadline = [DateTime]::UtcNow.AddMinutes(2) $healthy = $false while ([DateTime]::UtcNow -lt $deadline) { try { $resp = Invoke-WebRequest -Uri $healthUrl -UseBasicParsing -TimeoutSec 2 if ($resp.StatusCode -eq 200) { $healthy = $true break } } catch { Start-Sleep -Seconds 2 } } if ($healthy) { Write-Status "Backend health" $true $healthUrl Write-Host "" Write-Host "System online at http://localhost:3000/admin" Start-Process "http://localhost:3000/admin" } else { Write-Status "Backend health" $false $healthUrl Write-Host "Backend health check failed. Open http://localhost:3000/admin manually." } Write-Host "" Write-Host "Summary:" foreach ($entry in $status.GetEnumerator()) { $label = $entry.Key $value = $entry.Value if ($value -eq "OK") { Write-Host (" [OK] {0}" -f $label) -ForegroundColor Green } else { Write-Host (" [FAIL] {0}" -f $label) -ForegroundColor Red } }