chore: Second cleanup wave - organize root directory
- Archive: 85 agent .txt files → docs/archive/agents/legacy_txt/ - Scripts: Move 110 shell scripts → scripts/ (keep deploy.sh in root) - Models: Move 18 .safetensors → ml/models/checkpoints/training_artifacts/ - Delete: 34 directories (~33GB freed) - target/, coverage_*, test artifacts - Build: Clean 14 build artifacts (.rlib, .o, .pid, binaries) - Tests: Move 14 .rs files → tests/standalone/ - SQL: Move 5 files → sql/ (keep init-db*.sql for Docker) - Wave 153: Archive to docs/archive/historical/wave153/ - Docs: Archive 9 markdown files to wave_d/reports/ and historical/ Total impact: ~34GB freed (both waves), root directory cleaned from 583 to ~40 essential files Directory count reduced from 65 to 31 (52% reduction) All historical data preserved in organized archive structure
This commit is contained in:
41
scripts/DEPLOY_DQN_NOW.sh
Executable file
41
scripts/DEPLOY_DQN_NOW.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
# Quick DQN Deployment Script
|
||||
# Date: 2025-10-24
|
||||
# Purpose: Deploy DQN training to RunPod with correct binary selection
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "=========================================="
|
||||
echo "RunPod DQN Training Deployment"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Using: deploy_runpod_graphql.py (CORRECT script)"
|
||||
echo "Binary: train_dqn"
|
||||
echo "Dataset: ES_FUT_small.parquet (~13K bars)"
|
||||
echo "GPU: RTX A4000 (16GB, $0.25/hr)"
|
||||
echo "Expected time: ~1 minute"
|
||||
echo "Expected cost: ~$0.004"
|
||||
echo ""
|
||||
|
||||
# Deploy DQN training
|
||||
python3 scripts/deploy_runpod_graphql.py \
|
||||
--binary train_dqn \
|
||||
--parquet-file /runpod-volume/test_data/ES_FUT_small.parquet \
|
||||
--epochs 100 \
|
||||
--gpu-type "NVIDIA RTX A4000" \
|
||||
--pod-name foxhunt-dqn-training
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Deployment Complete"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Copy the Pod ID from output above"
|
||||
echo "2. Monitor training:"
|
||||
echo " ssh root@POD_ID.ssh.runpod.io"
|
||||
echo " nvidia-smi -l 1"
|
||||
echo "3. Check models after training:"
|
||||
echo " ls -lh /runpod-volume/models/"
|
||||
echo "4. Terminate pod to stop billing"
|
||||
echo ""
|
||||
169
scripts/HYPEROPT_QUICK_MONITOR.sh
Executable file
169
scripts/HYPEROPT_QUICK_MONITOR.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/bin/bash
|
||||
# Quick Monitoring Script for MAMBA-2 Hyperopt Deployment
|
||||
# Pod ID: qlql87w5avv1q1
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ENV_FILE="$SCRIPT_DIR/.env.runpod"
|
||||
|
||||
# Load environment
|
||||
if [ -f "$ENV_FILE" ]; then
|
||||
export $(grep -v '^#' "$ENV_FILE" | xargs)
|
||||
else
|
||||
echo "ERROR: .env.runpod not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
POD_ID="qlql87w5avv1q1"
|
||||
|
||||
# Function to get pod status
|
||||
check_status() {
|
||||
echo "===================="
|
||||
echo "POD STATUS CHECK"
|
||||
echo "===================="
|
||||
|
||||
curl -s -X GET \
|
||||
"https://rest.runpod.io/v1/pods/$POD_ID" \
|
||||
-H "Authorization: Bearer $RUNPOD_API_KEY" \
|
||||
| python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
print(f\"Status: {data.get('desiredStatus', 'UNKNOWN')}\")
|
||||
print(f\"Cost: \${data.get('costPerHr', 0)}/hr\")
|
||||
print(f\"GPU: {data.get('machine', {}).get('gpuDisplayName', 'TBD')}\")
|
||||
print(f\"Created: {data.get('createdAt', 'N/A')}\")
|
||||
runtime = data.get('runtime', {})
|
||||
if runtime:
|
||||
print(f\"Uptime: {runtime.get('uptimeInSeconds', 0)}s\")
|
||||
print(f\"Ports: {runtime.get('ports', 'N/A')}\")
|
||||
else:
|
||||
print('Runtime: Pod still provisioning...')
|
||||
"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to try SSH
|
||||
try_ssh() {
|
||||
echo "===================="
|
||||
echo "SSH CONNECTION TEST"
|
||||
echo "===================="
|
||||
|
||||
if ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \
|
||||
-p 19735 root@157.157.221.29 "echo 'Connected!'" 2>/dev/null; then
|
||||
echo "✅ SSH is available!"
|
||||
echo ""
|
||||
echo "Connect with:"
|
||||
echo " ssh -p 19735 root@157.157.221.29"
|
||||
echo ""
|
||||
return 0
|
||||
else
|
||||
echo "⏳ SSH not yet available (pod still initializing)"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to show training logs (if SSH available)
|
||||
show_logs() {
|
||||
echo "===================="
|
||||
echo "TRAINING LOGS (last 50 lines)"
|
||||
echo "===================="
|
||||
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
-p 19735 root@157.157.221.29 \
|
||||
"tail -50 /workspace/logs/*.log 2>/dev/null || echo 'No logs yet'" 2>/dev/null || \
|
||||
echo "Cannot access logs (pod not ready)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to check GPU
|
||||
check_gpu() {
|
||||
echo "===================="
|
||||
echo "GPU UTILIZATION"
|
||||
echo "===================="
|
||||
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
-p 19735 root@157.157.221.29 \
|
||||
"nvidia-smi --query-gpu=name,memory.used,memory.total,utilization.gpu,temperature.gpu --format=csv,noheader" 2>/dev/null || \
|
||||
echo "Cannot access GPU info (pod not ready)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to check for critical success metric
|
||||
check_losses() {
|
||||
echo "===================="
|
||||
echo "LOSS VALIDATION (CRITICAL)"
|
||||
echo "===================="
|
||||
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
||||
-p 19735 root@157.157.221.29 \
|
||||
"grep -E 'Feature normalization|Epoch.*Loss' /workspace/logs/*.log 2>/dev/null | tail -20 || echo 'No training logs yet'" 2>/dev/null || \
|
||||
echo "Cannot access logs (pod not ready)"
|
||||
echo ""
|
||||
echo "SUCCESS CRITERIA:"
|
||||
echo " ✅ Train Loss < 1.0"
|
||||
echo " ✅ Val Loss < 1.0"
|
||||
echo " ❌ If losses > 1.0, FIX FAILED"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Main monitoring loop
|
||||
main() {
|
||||
while true; do
|
||||
clear
|
||||
echo "========================================"
|
||||
echo "MAMBA-2 HYPEROPT MONITORING"
|
||||
echo "Pod ID: $POD_ID"
|
||||
echo "Time: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
check_status
|
||||
|
||||
if try_ssh; then
|
||||
check_gpu
|
||||
check_losses
|
||||
show_logs
|
||||
|
||||
echo "===================="
|
||||
echo "NEXT ACTIONS"
|
||||
echo "===================="
|
||||
echo "1. Watch for 'Feature normalization' log line (confirms fix)"
|
||||
echo "2. Verify losses < 1.0 (CRITICAL)"
|
||||
echo "3. Monitor GPU utilization > 85%"
|
||||
echo "4. Check epoch time ~10 min"
|
||||
echo ""
|
||||
echo "Press Ctrl+C to exit, or wait 60s for refresh..."
|
||||
sleep 60
|
||||
else
|
||||
echo "Pod is still provisioning. Checking again in 30 seconds..."
|
||||
sleep 30
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Handle Ctrl+C gracefully
|
||||
trap 'echo ""; echo "Monitoring stopped."; exit 0' INT
|
||||
|
||||
# Parse command line
|
||||
case "${1:-}" in
|
||||
status)
|
||||
check_status
|
||||
;;
|
||||
ssh)
|
||||
try_ssh && echo "To connect:" && echo "ssh -p 19735 root@157.157.221.29"
|
||||
;;
|
||||
logs)
|
||||
show_logs
|
||||
;;
|
||||
gpu)
|
||||
check_gpu
|
||||
;;
|
||||
losses)
|
||||
check_losses
|
||||
;;
|
||||
*)
|
||||
main
|
||||
;;
|
||||
esac
|
||||
184
scripts/LEVEL_1_ROLLBACK_TEST.sh
Executable file
184
scripts/LEVEL_1_ROLLBACK_TEST.sh
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/bin/bash
|
||||
# ================================================================================================
|
||||
# Level 1 Rollback Test: Feature-Only Rollback (Zero Downtime, Target: <1 minute)
|
||||
# Agent R1 - Rollback & Disaster Recovery Specialist
|
||||
# ================================================================================================
|
||||
#
|
||||
# SCENARIO: Disable Wave D features without restarting services or database rollback
|
||||
# EXPECTED: System falls back to Wave C (201 features) with zero downtime
|
||||
#
|
||||
# ================================================================================================
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
echo "===================================================================================================="
|
||||
echo "LEVEL 1 ROLLBACK TEST: Feature-Only Rollback (Zero Downtime)"
|
||||
echo "===================================================================================================="
|
||||
echo ""
|
||||
|
||||
# Start timer
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# Step 1: Verify current system state
|
||||
echo "Step 1: Verifying current system state (Wave D active)"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Check if services are running
|
||||
echo " ✓ Checking service health..."
|
||||
SERVICE_COUNT=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service" | wc -l)
|
||||
if [ "$SERVICE_COUNT" -lt 1 ]; then
|
||||
echo " ⚠ WARNING: No Foxhunt services detected. Skipping live service test."
|
||||
SERVICES_RUNNING=false
|
||||
else
|
||||
echo " ✓ Found $SERVICE_COUNT Foxhunt service process(es) running"
|
||||
SERVICES_RUNNING=true
|
||||
fi
|
||||
|
||||
# Check database for regime tables
|
||||
echo " ✓ Checking database for Wave D tables..."
|
||||
REGIME_TABLES=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_name IN ('regime_states', 'regime_transitions', 'adaptive_strategy_metrics');" 2>/dev/null || echo "0")
|
||||
echo " ✓ Found $REGIME_TABLES Wave D tables in database"
|
||||
|
||||
# Check feature config
|
||||
echo " ✓ Checking current feature configuration..."
|
||||
FEATURE_CONFIG_PATH="./ml/src/features/config.rs"
|
||||
if grep -q "enable_wave_d_regime: true" "$FEATURE_CONFIG_PATH" 2>/dev/null; then
|
||||
echo " ✓ Wave D features currently ENABLED in code"
|
||||
WAVE_D_ENABLED=true
|
||||
else
|
||||
echo " ⚠ Wave D features currently DISABLED in code (already rolled back?)"
|
||||
WAVE_D_ENABLED=false
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 2: Create rollback configuration (disable Wave D features)
|
||||
echo "Step 2: Creating rollback configuration (disable Wave D features)"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Backup current config
|
||||
echo " ✓ Backing up current configuration..."
|
||||
if [ -f "$FEATURE_CONFIG_PATH" ]; then
|
||||
cp "$FEATURE_CONFIG_PATH" "$FEATURE_CONFIG_PATH.backup_$(date +%s)"
|
||||
echo " ✓ Backup created: $FEATURE_CONFIG_PATH.backup_$(date +%s)"
|
||||
fi
|
||||
|
||||
# Modify wave_d() function to return Wave C config (201 features)
|
||||
echo " ✓ Modifying FeatureConfig::wave_d() to disable Wave D features..."
|
||||
cat > /tmp/wave_d_rollback.sed << 'EOF'
|
||||
# Find wave_d() function and change enable_wave_d_regime: true → false
|
||||
/pub fn wave_d\(\) -> Self {/,/enable_wave_d_regime: true,/ {
|
||||
s/enable_wave_d_regime: true,/enable_wave_d_regime: false,/
|
||||
}
|
||||
EOF
|
||||
|
||||
# Apply sed script
|
||||
if [ -f "$FEATURE_CONFIG_PATH" ]; then
|
||||
sed -i.rollback -f /tmp/wave_d_rollback.sed "$FEATURE_CONFIG_PATH" 2>/dev/null || {
|
||||
echo " ⚠ WARNING: sed failed, using manual method"
|
||||
# Fallback: create minimal config change
|
||||
echo " ⚠ Manual rollback required: Set enable_wave_d_regime: false in wave_d() function"
|
||||
}
|
||||
echo " ✓ Configuration modified (Wave D features disabled)"
|
||||
else
|
||||
echo " ⚠ WARNING: $FEATURE_CONFIG_PATH not found, skipping config modification"
|
||||
fi
|
||||
|
||||
# Verify change
|
||||
if grep -q "enable_wave_d_regime: false" "$FEATURE_CONFIG_PATH" 2>/dev/null; then
|
||||
echo " ✅ VERIFIED: Wave D features now DISABLED"
|
||||
else
|
||||
echo " ⚠ WARNING: Could not verify configuration change"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 3: Rebuild services (optional - for hot-reload test)
|
||||
echo "Step 3: Rebuilding services with Wave C configuration"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
echo " ⚠ NOTE: In production, this step would be replaced by hot-reload mechanism"
|
||||
echo " ⚠ For testing, we rebuild services to validate Wave C fallback"
|
||||
echo ""
|
||||
|
||||
REBUILD_START=$(date +%s)
|
||||
echo " ✓ Building workspace (release mode)..."
|
||||
cargo build --workspace --release 2>&1 | grep -E "Compiling|Finished|error" || echo " ✓ Build completed"
|
||||
REBUILD_END=$(date +%s)
|
||||
REBUILD_TIME=$((REBUILD_END - REBUILD_START))
|
||||
echo " ✓ Build completed in ${REBUILD_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 4: Validate rollback
|
||||
echo "Step 4: Validating Wave C fallback (201 features)"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Check feature count via compiled code
|
||||
echo " ✓ Checking feature count in rollback configuration..."
|
||||
FEATURE_COUNT=$(cargo run --release -p ml --example check_feature_count 2>/dev/null | grep -oP 'feature_count: \K\d+' || echo "unknown")
|
||||
if [ "$FEATURE_COUNT" = "201" ]; then
|
||||
echo " ✅ VERIFIED: Feature count = 201 (Wave C)"
|
||||
elif [ "$FEATURE_COUNT" = "225" ]; then
|
||||
echo " ❌ FAILED: Feature count = 225 (Wave D still active)"
|
||||
echo " ⚠ Rollback did not take effect, manual intervention required"
|
||||
else
|
||||
echo " ⚠ WARNING: Could not determine feature count (got: $FEATURE_COUNT)"
|
||||
echo " ⚠ Manual verification required"
|
||||
fi
|
||||
|
||||
# Verify regime detection disabled
|
||||
echo " ✓ Verifying regime detection disabled..."
|
||||
if grep -q "enable_wave_d_regime: false" "$FEATURE_CONFIG_PATH" 2>/dev/null; then
|
||||
echo " ✅ VERIFIED: Regime detection disabled in configuration"
|
||||
else
|
||||
echo " ❌ FAILED: Regime detection still enabled"
|
||||
fi
|
||||
|
||||
# Database remains unchanged (Level 1 does NOT modify database)
|
||||
echo " ✓ Database status: UNCHANGED (Wave D tables still exist)"
|
||||
echo " ⚠ Note: Level 1 rollback preserves Wave D data for recovery"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 5: Calculate rollback time
|
||||
echo "Step 5: Rollback Performance Metrics"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
END_TIME=$(date +%s)
|
||||
TOTAL_TIME=$((END_TIME - START_TIME))
|
||||
|
||||
echo " • Total rollback time: ${TOTAL_TIME}s"
|
||||
echo " • Rebuild time: ${REBUILD_TIME}s"
|
||||
echo " • Target time: <60s"
|
||||
|
||||
if [ $TOTAL_TIME -lt 60 ]; then
|
||||
echo " ✅ PASSED: Rollback completed within 60s target"
|
||||
else
|
||||
echo " ⚠ MISSED TARGET: Rollback took ${TOTAL_TIME}s (>60s)"
|
||||
echo " ⚠ Note: Hot-reload mechanism would reduce this to <10s"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "===================================================================================================="
|
||||
echo "LEVEL 1 ROLLBACK TEST: SUMMARY"
|
||||
echo "===================================================================================================="
|
||||
echo ""
|
||||
echo "Result:"
|
||||
echo " • Wave D features: DISABLED (201 features active)"
|
||||
echo " • Database: UNCHANGED (Wave D tables preserved)"
|
||||
echo " • Services: REBUILD REQUIRED (or hot-reload in production)"
|
||||
echo " • Rollback time: ${TOTAL_TIME}s (target: <60s)"
|
||||
echo ""
|
||||
echo "Recovery Path:"
|
||||
echo " To re-enable Wave D:"
|
||||
echo " 1. Restore configuration: cp $FEATURE_CONFIG_PATH.backup_* $FEATURE_CONFIG_PATH"
|
||||
echo " 2. Rebuild services: cargo build --workspace --release"
|
||||
echo " 3. Restart services"
|
||||
echo ""
|
||||
echo "===================================================================================================="
|
||||
|
||||
# Cleanup
|
||||
rm -f /tmp/wave_d_rollback.sed
|
||||
|
||||
exit 0
|
||||
238
scripts/LEVEL_2_ROLLBACK_TEST.sh
Executable file
238
scripts/LEVEL_2_ROLLBACK_TEST.sh
Executable file
@@ -0,0 +1,238 @@
|
||||
#!/bin/bash
|
||||
# ================================================================================================
|
||||
# Level 2 Rollback Test: Database Rollback (Target: ~5 minutes)
|
||||
# Agent R1 - Rollback & Disaster Recovery Specialist
|
||||
# ================================================================================================
|
||||
#
|
||||
# SCENARIO: Rollback database migration 045 (regime detection tables)
|
||||
# EXPECTED: All Wave D tables removed, services restart with Wave C configuration
|
||||
#
|
||||
# ================================================================================================
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
echo "===================================================================================================="
|
||||
echo "LEVEL 2 ROLLBACK TEST: Database Rollback"
|
||||
echo "===================================================================================================="
|
||||
echo ""
|
||||
|
||||
# Start timer
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# Step 1: Pre-rollback validation
|
||||
echo "Step 1: Pre-rollback validation"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Check current database state
|
||||
echo " ✓ Checking database for Wave D tables..."
|
||||
REGIME_TABLES=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "
|
||||
SELECT COUNT(*) FROM information_schema.tables
|
||||
WHERE table_name IN ('regime_states', 'regime_transitions', 'adaptive_strategy_metrics');
|
||||
" 2>/dev/null || echo "0")
|
||||
|
||||
echo " ✓ Found $REGIME_TABLES Wave D tables before rollback"
|
||||
|
||||
if [ "$REGIME_TABLES" -eq 0 ]; then
|
||||
echo " ⚠ WARNING: No Wave D tables found. Migration 045 may not be applied."
|
||||
echo " ⚠ Skipping Level 2 test (nothing to rollback)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check for existing data
|
||||
echo " ✓ Checking for existing Wave D data..."
|
||||
REGIME_DATA=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "
|
||||
SELECT COUNT(*) FROM regime_states;
|
||||
" 2>/dev/null || echo "0")
|
||||
echo " ✓ Found $REGIME_DATA regime_states records"
|
||||
|
||||
# Backup database (safety measure)
|
||||
echo " ✓ Creating database backup..."
|
||||
BACKUP_FILE="/tmp/foxhunt_backup_$(date +%s).sql"
|
||||
PGPASSWORD=foxhunt_dev_password pg_dump -h localhost -U foxhunt -d foxhunt -f "$BACKUP_FILE" 2>/dev/null || {
|
||||
echo " ⚠ WARNING: Backup failed, continuing anyway"
|
||||
BACKUP_FILE=""
|
||||
}
|
||||
if [ -n "$BACKUP_FILE" ] && [ -f "$BACKUP_FILE" ]; then
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
echo " ✅ Backup created: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 2: Stop services (graceful shutdown)
|
||||
echo "Step 2: Stopping services (graceful shutdown)"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
SHUTDOWN_START=$(date +%s)
|
||||
|
||||
# Find and stop Foxhunt services
|
||||
PIDS=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service" || true)
|
||||
if [ -n "$PIDS" ]; then
|
||||
echo " ✓ Found service PIDs: $PIDS"
|
||||
echo " ✓ Sending SIGTERM for graceful shutdown..."
|
||||
kill -TERM $PIDS 2>/dev/null || true
|
||||
|
||||
# Wait up to 30s for graceful shutdown
|
||||
for i in {1..30}; do
|
||||
REMAINING=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service" | wc -l)
|
||||
if [ "$REMAINING" -eq 0 ]; then
|
||||
echo " ✅ All services stopped gracefully in ${i}s"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Force kill if still running
|
||||
REMAINING=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service" | wc -l)
|
||||
if [ "$REMAINING" -gt 0 ]; then
|
||||
echo " ⚠ WARNING: Forcing shutdown of $REMAINING remaining processes"
|
||||
kill -9 $(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service") 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
echo " ⚠ No services running"
|
||||
fi
|
||||
|
||||
SHUTDOWN_END=$(date +%s)
|
||||
SHUTDOWN_TIME=$((SHUTDOWN_END - SHUTDOWN_START))
|
||||
echo " ✓ Shutdown completed in ${SHUTDOWN_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 3: Apply rollback migration
|
||||
echo "Step 3: Rolling back database migration 045"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
MIGRATION_START=$(date +%s)
|
||||
|
||||
# Method 1: Use sqlx migrate revert (if sqlx-cli is installed)
|
||||
if command -v sqlx &> /dev/null; then
|
||||
echo " ✓ Using sqlx migrate revert..."
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
DATABASE_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt" \
|
||||
sqlx migrate revert --database-url "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt" 2>&1 | grep -E "Applied|Reverted|error" || echo " ✓ Revert completed"
|
||||
else
|
||||
# Method 2: Direct SQL execution (fallback)
|
||||
echo " ✓ Using direct SQL execution (sqlx not found)..."
|
||||
PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -f /home/jgrusewski/Work/foxhunt/migrations/045_wave_d_regime_tracking.down.sql 2>&1 | grep -E "DROP|REVOKE|NOTICE|ERROR" || echo " ✓ Rollback SQL executed"
|
||||
fi
|
||||
|
||||
MIGRATION_END=$(date +%s)
|
||||
MIGRATION_TIME=$((MIGRATION_END - MIGRATION_START))
|
||||
echo " ✓ Migration rollback completed in ${MIGRATION_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 4: Validate rollback
|
||||
echo "Step 4: Validating database rollback"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Check tables removed
|
||||
REMAINING_TABLES=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "
|
||||
SELECT COUNT(*) FROM information_schema.tables
|
||||
WHERE table_name IN ('regime_states', 'regime_transitions', 'adaptive_strategy_metrics');
|
||||
" 2>/dev/null || echo "unknown")
|
||||
|
||||
if [ "$REMAINING_TABLES" = "0" ]; then
|
||||
echo " ✅ VERIFIED: All Wave D tables removed"
|
||||
else
|
||||
echo " ❌ FAILED: $REMAINING_TABLES Wave D tables still exist"
|
||||
echo " ⚠ Rollback did not complete successfully"
|
||||
fi
|
||||
|
||||
# Check functions removed
|
||||
REMAINING_FUNCTIONS=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "
|
||||
SELECT COUNT(*) FROM information_schema.routines
|
||||
WHERE routine_name IN ('get_latest_regime', 'get_regime_transition_matrix', 'get_regime_performance');
|
||||
" 2>/dev/null || echo "unknown")
|
||||
|
||||
if [ "$REMAINING_FUNCTIONS" = "0" ]; then
|
||||
echo " ✅ VERIFIED: All Wave D functions removed"
|
||||
else
|
||||
echo " ❌ FAILED: $REMAINING_FUNCTIONS Wave D functions still exist"
|
||||
fi
|
||||
|
||||
# Check migration history
|
||||
MIGRATION_VERSION=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "
|
||||
SELECT version FROM _sqlx_migrations ORDER BY version DESC LIMIT 1;
|
||||
" 2>/dev/null || echo "unknown")
|
||||
echo " ✓ Latest migration version: $MIGRATION_VERSION (should be 44 after rollback)"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 5: Restart services with Wave C configuration
|
||||
echo "Step 5: Restarting services with Wave C configuration"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
RESTART_START=$(date +%s)
|
||||
|
||||
# Ensure Wave C configuration is active (from Level 1 rollback)
|
||||
echo " ✓ Verifying Wave C configuration..."
|
||||
if grep -q "enable_wave_d_regime: false" /home/jgrusewski/Work/foxhunt/ml/src/features/config.rs 2>/dev/null; then
|
||||
echo " ✅ Wave C configuration active"
|
||||
else
|
||||
echo " ⚠ WARNING: Wave D features still enabled in code"
|
||||
echo " ⚠ Recommendation: Run Level 1 rollback first"
|
||||
fi
|
||||
|
||||
# Rebuild services (if needed)
|
||||
echo " ✓ Rebuilding services..."
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
cargo build --release --workspace 2>&1 | grep -E "Compiling|Finished|error" || echo " ✓ Build completed"
|
||||
|
||||
echo " ⚠ NOTE: Manual service restart required for production"
|
||||
echo " ⚠ Services NOT started automatically by this test script"
|
||||
|
||||
RESTART_END=$(date +%s)
|
||||
RESTART_TIME=$((RESTART_END - RESTART_START))
|
||||
echo " ✓ Rebuild completed in ${RESTART_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 6: Calculate rollback time
|
||||
echo "Step 6: Rollback Performance Metrics"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
END_TIME=$(date +%s)
|
||||
TOTAL_TIME=$((END_TIME - START_TIME))
|
||||
|
||||
echo " • Total rollback time: ${TOTAL_TIME}s"
|
||||
echo " • Shutdown time: ${SHUTDOWN_TIME}s"
|
||||
echo " • Migration time: ${MIGRATION_TIME}s"
|
||||
echo " • Rebuild time: ${RESTART_TIME}s"
|
||||
echo " • Target time: <300s (5 minutes)"
|
||||
|
||||
if [ $TOTAL_TIME -lt 300 ]; then
|
||||
echo " ✅ PASSED: Rollback completed within 5-minute target"
|
||||
else
|
||||
echo " ⚠ MISSED TARGET: Rollback took ${TOTAL_TIME}s (>300s)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "===================================================================================================="
|
||||
echo "LEVEL 2 ROLLBACK TEST: SUMMARY"
|
||||
echo "===================================================================================================="
|
||||
echo ""
|
||||
echo "Result:"
|
||||
echo " • Database tables removed: $REGIME_TABLES → $REMAINING_TABLES"
|
||||
echo " • Database functions removed: ✓ (get_latest_regime, get_regime_transition_matrix, get_regime_performance)"
|
||||
echo " • Migration version: $MIGRATION_VERSION"
|
||||
echo " • Services: STOPPED (manual restart required)"
|
||||
echo " • Rollback time: ${TOTAL_TIME}s (target: <300s)"
|
||||
echo ""
|
||||
echo "Data Loss:"
|
||||
echo " • regime_states: $REGIME_DATA records DELETED"
|
||||
echo " • regime_transitions: DELETED"
|
||||
echo " • adaptive_strategy_metrics: DELETED"
|
||||
if [ -n "$BACKUP_FILE" ] && [ -f "$BACKUP_FILE" ]; then
|
||||
echo " • Backup: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
fi
|
||||
echo ""
|
||||
echo "Recovery Path:"
|
||||
echo " To re-apply Wave D migration:"
|
||||
echo " 1. Restore database: psql < $BACKUP_FILE (if needed)"
|
||||
echo " 2. Re-apply migration: sqlx migrate run"
|
||||
echo " 3. Re-enable Wave D features (reverse Level 1 rollback)"
|
||||
echo " 4. Rebuild services: cargo build --workspace --release"
|
||||
echo " 5. Restart services"
|
||||
echo ""
|
||||
echo "===================================================================================================="
|
||||
|
||||
exit 0
|
||||
297
scripts/LEVEL_3_ROLLBACK_TEST.sh
Executable file
297
scripts/LEVEL_3_ROLLBACK_TEST.sh
Executable file
@@ -0,0 +1,297 @@
|
||||
#!/bin/bash
|
||||
# ================================================================================================
|
||||
# Level 3 Rollback Test: Full Rollback to Wave C (Target: ~15 minutes)
|
||||
# Agent R1 - Rollback & Disaster Recovery Specialist
|
||||
# ================================================================================================
|
||||
#
|
||||
# SCENARIO: Complete redeployment to Wave C baseline (before Wave D)
|
||||
# EXPECTED: System rolled back to last known good Wave C state
|
||||
#
|
||||
# ================================================================================================
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
echo "===================================================================================================="
|
||||
echo "LEVEL 3 ROLLBACK TEST: Full Rollback to Wave C"
|
||||
echo "===================================================================================================="
|
||||
echo ""
|
||||
|
||||
# Start timer
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# Configuration
|
||||
WAVE_C_TAG="wave-c-baseline" # Git tag for Wave C baseline
|
||||
WAVE_D_TAG="wave-d-v1.0" # Git tag for current Wave D deployment
|
||||
BACKUP_DIR="/tmp/foxhunt_rollback_$(date +%s)"
|
||||
|
||||
# Step 1: Pre-rollback preparation
|
||||
echo "Step 1: Pre-rollback preparation"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
echo " ✓ Created backup directory: $BACKUP_DIR"
|
||||
|
||||
# Tag current Wave D state (if not already tagged)
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||||
echo " ✓ Current commit: $CURRENT_COMMIT"
|
||||
|
||||
if ! git tag -l | grep -q "^$WAVE_D_TAG$"; then
|
||||
git tag "$WAVE_D_TAG" "$CURRENT_COMMIT"
|
||||
echo " ✓ Tagged Wave D deployment: $WAVE_D_TAG"
|
||||
else
|
||||
echo " ⚠ Tag $WAVE_D_TAG already exists"
|
||||
fi
|
||||
|
||||
# Backup database
|
||||
echo " ✓ Backing up database..."
|
||||
BACKUP_FILE="$BACKUP_DIR/foxhunt_wave_d_backup.sql"
|
||||
PGPASSWORD=foxhunt_dev_password pg_dump -h localhost -U foxhunt -d foxhunt -f "$BACKUP_FILE" 2>/dev/null || {
|
||||
echo " ⚠ WARNING: Database backup failed"
|
||||
BACKUP_FILE=""
|
||||
}
|
||||
if [ -n "$BACKUP_FILE" ] && [ -f "$BACKUP_FILE" ]; then
|
||||
BACKUP_SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
|
||||
echo " ✅ Database backup created: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
fi
|
||||
|
||||
# Backup .env files
|
||||
echo " ✓ Backing up environment files..."
|
||||
cp /home/jgrusewski/Work/foxhunt/.env "$BACKUP_DIR/.env.wave_d" 2>/dev/null || true
|
||||
cp /home/jgrusewski/Work/foxhunt/.env.production "$BACKUP_DIR/.env.production.wave_d" 2>/dev/null || true
|
||||
echo " ✓ Environment files backed up"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 2: Stop all services
|
||||
echo "Step 2: Stopping all services"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
SHUTDOWN_START=$(date +%s)
|
||||
|
||||
# Stop Foxhunt services
|
||||
PIDS=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service|trading_agent_service" || true)
|
||||
if [ -n "$PIDS" ]; then
|
||||
echo " ✓ Stopping Foxhunt services (PIDs: $PIDS)..."
|
||||
kill -TERM $PIDS 2>/dev/null || true
|
||||
|
||||
# Wait for graceful shutdown
|
||||
for i in {1..30}; do
|
||||
REMAINING=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service|trading_agent_service" | wc -l)
|
||||
if [ "$REMAINING" -eq 0 ]; then
|
||||
echo " ✅ Services stopped in ${i}s"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Force kill if needed
|
||||
REMAINING=$(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service|trading_agent_service" | wc -l)
|
||||
if [ "$REMAINING" -gt 0 ]; then
|
||||
echo " ⚠ Forcing shutdown of $REMAINING processes..."
|
||||
kill -9 $(pgrep -f "api_gateway|trading_service|backtesting_service|ml_training_service|trading_agent_service") 2>/dev/null || true
|
||||
fi
|
||||
else
|
||||
echo " ⚠ No services running"
|
||||
fi
|
||||
|
||||
SHUTDOWN_END=$(date +%s)
|
||||
SHUTDOWN_TIME=$((SHUTDOWN_END - SHUTDOWN_START))
|
||||
echo " ✓ Shutdown completed in ${SHUTDOWN_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 3: Rollback database (Level 2 rollback)
|
||||
echo "Step 3: Rolling back database to Wave C state"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
MIGRATION_START=$(date +%s)
|
||||
|
||||
# Run Level 2 rollback (database migration revert)
|
||||
echo " ✓ Executing database rollback..."
|
||||
if [ -f "/home/jgrusewski/Work/foxhunt/migrations/045_wave_d_regime_tracking.down.sql" ]; then
|
||||
PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt \
|
||||
-f /home/jgrusewski/Work/foxhunt/migrations/045_wave_d_regime_tracking.down.sql \
|
||||
2>&1 | grep -E "DROP|REVOKE|NOTICE|ERROR|Wave D" || echo " ✓ Database rollback completed"
|
||||
else
|
||||
echo " ⚠ WARNING: Down migration not found, attempting alternative method..."
|
||||
# Use migration 046 if available
|
||||
if [ -f "/home/jgrusewski/Work/foxhunt/migrations/046_rollback_regime_detection.sql" ]; then
|
||||
PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt \
|
||||
-f /home/jgrusewski/Work/foxhunt/migrations/046_rollback_regime_detection.sql \
|
||||
2>&1 | grep -E "DROP|REVOKE|NOTICE|ERROR|Wave D" || echo " ✓ Alternative rollback completed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify database state
|
||||
REGIME_TABLES=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -tAc "
|
||||
SELECT COUNT(*) FROM information_schema.tables
|
||||
WHERE table_name IN ('regime_states', 'regime_transitions', 'adaptive_strategy_metrics');
|
||||
" 2>/dev/null || echo "unknown")
|
||||
|
||||
if [ "$REGIME_TABLES" = "0" ]; then
|
||||
echo " ✅ Database rollback successful (Wave D tables removed)"
|
||||
else
|
||||
echo " ❌ Database rollback failed ($REGIME_TABLES Wave D tables remain)"
|
||||
fi
|
||||
|
||||
MIGRATION_END=$(date +%s)
|
||||
MIGRATION_TIME=$((MIGRATION_END - MIGRATION_START))
|
||||
echo " ✓ Database rollback completed in ${MIGRATION_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 4: Checkout Wave C baseline code
|
||||
echo "Step 4: Checking out Wave C baseline code"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
CHECKOUT_START=$(date +%s)
|
||||
|
||||
# Find Wave C baseline commit
|
||||
if git tag -l | grep -q "^$WAVE_C_TAG$"; then
|
||||
echo " ✓ Found Wave C tag: $WAVE_C_TAG"
|
||||
WAVE_C_COMMIT=$(git rev-list -n 1 "$WAVE_C_TAG")
|
||||
else
|
||||
# Fallback: Find commit before Wave D Phase 3 (feature implementation)
|
||||
echo " ⚠ No Wave C tag found, searching for baseline commit..."
|
||||
WAVE_C_COMMIT=$(git log --all --oneline | grep -E "Wave C.*COMPLETE|WAVE_C.*IMPLEMENTATION.*COMPLETE" | head -1 | awk '{print $1}')
|
||||
if [ -z "$WAVE_C_COMMIT" ]; then
|
||||
# Ultimate fallback: commit before "Wave D Phase 3"
|
||||
WAVE_C_COMMIT=$(git log --all --oneline --before="2025-10-17" | head -1 | awk '{print $1}')
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " ✓ Wave C baseline commit: $WAVE_C_COMMIT"
|
||||
|
||||
# Stash current changes (if any)
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
echo " ✓ Stashing uncommitted changes..."
|
||||
git stash push -m "Rollback: Stashing Wave D changes before rollback to Wave C"
|
||||
fi
|
||||
|
||||
# Checkout Wave C baseline
|
||||
echo " ✓ Checking out Wave C baseline..."
|
||||
git checkout "$WAVE_C_COMMIT" 2>&1 | grep -E "HEAD|Previous|error" || echo " ✓ Checkout completed"
|
||||
|
||||
# Verify checkout
|
||||
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||||
if [ "$CURRENT_COMMIT" = "$WAVE_C_COMMIT" ]; then
|
||||
echo " ✅ Successfully checked out Wave C baseline"
|
||||
else
|
||||
echo " ❌ Checkout failed (HEAD: $CURRENT_COMMIT, Expected: $WAVE_C_COMMIT)"
|
||||
fi
|
||||
|
||||
CHECKOUT_END=$(date +%s)
|
||||
CHECKOUT_TIME=$((CHECKOUT_END - CHECKOUT_START))
|
||||
echo " ✓ Git checkout completed in ${CHECKOUT_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 5: Rebuild services with Wave C codebase
|
||||
echo "Step 5: Rebuilding services from Wave C codebase"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
REBUILD_START=$(date +%s)
|
||||
|
||||
# Clean build to ensure no Wave D artifacts
|
||||
echo " ✓ Cleaning previous build artifacts..."
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
cargo clean 2>&1 | grep -E "Removing|error" || echo " ✓ Clean completed"
|
||||
|
||||
# Rebuild entire workspace
|
||||
echo " ✓ Rebuilding workspace (release mode)..."
|
||||
cargo build --workspace --release 2>&1 | grep -E "Compiling|Finished|error" || echo " ✓ Build completed"
|
||||
|
||||
REBUILD_END=$(date +%s)
|
||||
REBUILD_TIME=$((REBUILD_END - REBUILD_START))
|
||||
echo " ✓ Rebuild completed in ${REBUILD_TIME}s"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 6: Smoke test Wave C deployment
|
||||
echo "Step 6: Smoke testing Wave C deployment"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
|
||||
# Check feature count
|
||||
echo " ✓ Checking feature count..."
|
||||
FEATURE_COUNT=$(cargo run --release -p ml --example check_feature_count 2>&1 | grep "Wave C: feature_count:" | grep -oP '\d+' || echo "unknown")
|
||||
if [ "$FEATURE_COUNT" = "201" ]; then
|
||||
echo " ✅ VERIFIED: Wave C feature count = 201"
|
||||
elif [ "$FEATURE_COUNT" = "225" ]; then
|
||||
echo " ❌ FAILED: Feature count = 225 (Wave D still active)"
|
||||
else
|
||||
echo " ⚠ WARNING: Could not verify feature count (got: $FEATURE_COUNT)"
|
||||
fi
|
||||
|
||||
# Check for Wave D code
|
||||
WAVE_D_CODE=$(grep -r "enable_wave_d_regime" /home/jgrusewski/Work/foxhunt/ml/src/features/ 2>/dev/null | wc -l)
|
||||
if [ "$WAVE_D_CODE" -eq 0 ]; then
|
||||
echo " ✅ VERIFIED: No Wave D code present"
|
||||
else
|
||||
echo " ⚠ WARNING: Wave D code references still exist ($WAVE_D_CODE occurrences)"
|
||||
echo " ⚠ This may be expected if Wave C already had placeholders"
|
||||
fi
|
||||
|
||||
# Test basic compilation
|
||||
echo " ✓ Running basic compilation test..."
|
||||
cargo check --workspace 2>&1 | grep -E "Checking|Finished|error" || echo " ✓ Check completed"
|
||||
|
||||
echo " ⚠ NOTE: Services NOT started automatically (manual start required)"
|
||||
|
||||
echo ""
|
||||
|
||||
# Step 7: Calculate rollback time
|
||||
echo "Step 7: Rollback Performance Metrics"
|
||||
echo "------------------------------------------------------------------------------------"
|
||||
END_TIME=$(date +%s)
|
||||
TOTAL_TIME=$((END_TIME - START_TIME))
|
||||
|
||||
echo " • Total rollback time: ${TOTAL_TIME}s"
|
||||
echo " • Shutdown time: ${SHUTDOWN_TIME}s"
|
||||
echo " • Database rollback time: ${MIGRATION_TIME}s"
|
||||
echo " • Git checkout time: ${CHECKOUT_TIME}s"
|
||||
echo " • Rebuild time: ${REBUILD_TIME}s"
|
||||
echo " • Target time: <900s (15 minutes)"
|
||||
|
||||
if [ $TOTAL_TIME -lt 900 ]; then
|
||||
echo " ✅ PASSED: Rollback completed within 15-minute target"
|
||||
else
|
||||
echo " ⚠ MISSED TARGET: Rollback took ${TOTAL_TIME}s (>900s)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "===================================================================================================="
|
||||
echo "LEVEL 3 ROLLBACK TEST: SUMMARY"
|
||||
echo "===================================================================================================="
|
||||
echo ""
|
||||
echo "Result:"
|
||||
echo " • Git state: Wave C baseline ($WAVE_C_COMMIT)"
|
||||
echo " • Database: Wave D tables removed ($REGIME_TABLES tables remain)"
|
||||
echo " • Feature count: $FEATURE_COUNT (expected: 201)"
|
||||
echo " • Services: STOPPED (manual restart required)"
|
||||
echo " • Rollback time: ${TOTAL_TIME}s (~$((TOTAL_TIME / 60)) minutes)"
|
||||
echo ""
|
||||
echo "Data Loss:"
|
||||
echo " • All Wave D regime detection data DELETED"
|
||||
echo " • Wave D code changes REVERTED"
|
||||
if [ -n "$BACKUP_FILE" ] && [ -f "$BACKUP_FILE" ]; then
|
||||
echo " • Full backup: $BACKUP_FILE ($BACKUP_SIZE)"
|
||||
fi
|
||||
echo " • Backup directory: $BACKUP_DIR"
|
||||
echo ""
|
||||
echo "Recovery Path:"
|
||||
echo " To re-deploy Wave D:"
|
||||
echo " 1. Checkout Wave D tag: git checkout $WAVE_D_TAG"
|
||||
echo " 2. Restore database (optional): psql < $BACKUP_FILE"
|
||||
echo " 3. Re-apply migrations: sqlx migrate run"
|
||||
echo " 4. Rebuild services: cargo build --workspace --release"
|
||||
echo " 5. Restart all services"
|
||||
echo ""
|
||||
echo "Manual Steps Required:"
|
||||
echo " 1. Start services: cargo run -p api_gateway &"
|
||||
echo " 2. Verify health checks: curl http://localhost:8080/health"
|
||||
echo " 3. Run integration tests: cargo test --workspace"
|
||||
echo " 4. Monitor Grafana dashboards"
|
||||
echo ""
|
||||
echo "===================================================================================================="
|
||||
|
||||
exit 0
|
||||
169
scripts/MAMBA2_POD_MONITOR.sh
Executable file
169
scripts/MAMBA2_POD_MONITOR.sh
Executable file
@@ -0,0 +1,169 @@
|
||||
#!/bin/bash
|
||||
# MAMBA-2 Fixed Binary Deployment Monitor
|
||||
# Pod ID: 8e6o2r2snavgzf
|
||||
# Expected completion: 2025-10-27 10:21 UTC (~81 minutes from 09:00)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
POD_ID="8e6o2r2snavgzf"
|
||||
CHECKPOINT_DIR="/runpod-volume/models/mamba2_FIXED_sgd_bs512_lr5e4_shuffle_50ep"
|
||||
|
||||
echo "=================================="
|
||||
echo "MAMBA-2 FIXED BINARY MONITOR"
|
||||
echo "=================================="
|
||||
echo "Pod ID: $POD_ID"
|
||||
echo "GPU: RTX 4090 (24GB VRAM)"
|
||||
echo "Cost: \$0.59/hr"
|
||||
echo "Expected runtime: ~81 minutes"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
|
||||
# Load RunPod credentials
|
||||
if [ ! -f ".env.runpod" ]; then
|
||||
echo "ERROR: .env.runpod not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source .env.runpod
|
||||
|
||||
if [ -z "$RUNPOD_API_KEY" ]; then
|
||||
echo "ERROR: RUNPOD_API_KEY not set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Function to check pod status
|
||||
check_status() {
|
||||
echo "Checking pod status..."
|
||||
curl -s -H "Authorization: Bearer $RUNPOD_API_KEY" \
|
||||
"https://rest.runpod.io/v1/pods/$POD_ID" | python3 -m json.tool
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to download results
|
||||
download_results() {
|
||||
echo "Downloading results from S3..."
|
||||
aws s3 sync "s3://se3zdnb5o4/models/mamba2_FIXED_sgd_bs512_lr5e4_shuffle_50ep" \
|
||||
"./local_models/mamba2_FIXED" \
|
||||
--profile runpod \
|
||||
--endpoint-url https://s3api-eur-is-1.runpod.io
|
||||
echo ""
|
||||
echo "Results downloaded to ./local_models/mamba2_FIXED/"
|
||||
ls -lh "./local_models/mamba2_FIXED/"
|
||||
}
|
||||
|
||||
# Function to verify training success
|
||||
verify_training() {
|
||||
echo "Verifying training success..."
|
||||
|
||||
if [ ! -d "./local_models/mamba2_FIXED" ]; then
|
||||
echo "ERROR: Results not downloaded yet. Run with 'download' first."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check for model checkpoint
|
||||
if [ -f "./local_models/mamba2_FIXED/mamba2_model_epoch_50.safetensors" ]; then
|
||||
echo "✅ Model checkpoint found"
|
||||
ls -lh "./local_models/mamba2_FIXED/mamba2_model_epoch_50.safetensors"
|
||||
else
|
||||
echo "❌ Model checkpoint NOT found"
|
||||
fi
|
||||
|
||||
# Check for metrics
|
||||
if [ -f "./local_models/mamba2_FIXED/training_metrics.json" ]; then
|
||||
echo "✅ Training metrics found"
|
||||
cat "./local_models/mamba2_FIXED/training_metrics.json"
|
||||
else
|
||||
echo "❌ Training metrics NOT found"
|
||||
fi
|
||||
|
||||
# Check for loss history
|
||||
if [ -f "./local_models/mamba2_FIXED/loss_history.csv" ]; then
|
||||
echo "✅ Loss history found"
|
||||
echo "Last 10 epochs:"
|
||||
tail -n 10 "./local_models/mamba2_FIXED/loss_history.csv"
|
||||
else
|
||||
echo "❌ Loss history NOT found"
|
||||
fi
|
||||
|
||||
# Check training log for key indicators
|
||||
if [ -f "./local_models/mamba2_FIXED/training.log" ]; then
|
||||
echo "✅ Training log found"
|
||||
echo ""
|
||||
echo "Checking for success indicators..."
|
||||
|
||||
# Check optimizer
|
||||
if grep -q "Optimizer: SGD" "./local_models/mamba2_FIXED/training.log"; then
|
||||
echo "✅ SGD optimizer confirmed (not Adam)"
|
||||
else
|
||||
echo "❌ SGD optimizer NOT found (check for Adam)"
|
||||
fi
|
||||
|
||||
# Check for zero gradients
|
||||
if grep -q "grad: 0.0000" "./local_models/mamba2_FIXED/training.log"; then
|
||||
echo "❌ Zero gradients detected (P0 fix failed)"
|
||||
else
|
||||
echo "✅ No zero gradients detected"
|
||||
fi
|
||||
|
||||
# Check for E11 spike
|
||||
if grep -q "E11" "./local_models/mamba2_FIXED/training.log" || \
|
||||
grep -q "1e+11" "./local_models/mamba2_FIXED/training.log"; then
|
||||
echo "❌ E11 spike detected (numerical instability)"
|
||||
else
|
||||
echo "✅ No E11 spike detected"
|
||||
fi
|
||||
|
||||
else
|
||||
echo "❌ Training log NOT found"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main menu
|
||||
case "${1:-status}" in
|
||||
status)
|
||||
check_status
|
||||
;;
|
||||
download)
|
||||
download_results
|
||||
;;
|
||||
verify)
|
||||
verify_training
|
||||
;;
|
||||
ssh)
|
||||
echo "SSH to pod $POD_ID..."
|
||||
echo "ssh root@$POD_ID.ssh.runpod.io"
|
||||
echo ""
|
||||
echo "Once connected, check training status:"
|
||||
echo " cd $CHECKPOINT_DIR"
|
||||
echo " tail -f training.log"
|
||||
;;
|
||||
jupyter)
|
||||
echo "Jupyter URL: https://$POD_ID-8888.proxy.runpod.net"
|
||||
echo ""
|
||||
echo "Navigate to: $CHECKPOINT_DIR/training.log"
|
||||
;;
|
||||
all)
|
||||
check_status
|
||||
echo ""
|
||||
echo "=================================="
|
||||
read -p "Download results? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
download_results
|
||||
echo ""
|
||||
verify_training
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {status|download|verify|ssh|jupyter|all}"
|
||||
echo ""
|
||||
echo "Commands:"
|
||||
echo " status - Check pod status via API"
|
||||
echo " download - Download results from S3"
|
||||
echo " verify - Verify training success (requires download first)"
|
||||
echo " ssh - Show SSH command"
|
||||
echo " jupyter - Show Jupyter URL"
|
||||
echo " all - Status + download + verify (interactive)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
56
scripts/QUICK_FIX_COMMANDS.sh
Executable file
56
scripts/QUICK_FIX_COMMANDS.sh
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
# Quick Fix Commands - Foxhunt Codebase Cleanup
|
||||
# Total Estimated Time: 3.5 minutes
|
||||
# Impact: Cosmetic improvements only, zero functional changes
|
||||
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "Foxhunt Codebase Quick Fixes"
|
||||
echo "Total Time: ~3.5 minutes"
|
||||
echo "=========================================="
|
||||
|
||||
echo ""
|
||||
echo "[1/3] Fixing 4 Clippy Deny-Level Errors (35 seconds)..."
|
||||
echo "Files affected:"
|
||||
echo " - services/stress_tests/src/metrics.rs"
|
||||
echo " - trading-data/src/models.rs"
|
||||
echo " - trading_engine/src/types/events.rs"
|
||||
|
||||
# Manual fixes required (clippy --fix may not handle all):
|
||||
echo ""
|
||||
echo "Manual fixes needed:"
|
||||
echo "1. stress_tests/src/metrics.rs:144"
|
||||
echo " BEFORE: let mean_u64 = (mean_micros as u64).min(u64::MAX);"
|
||||
echo " AFTER: let mean_u64 = (mean_micros as u64);"
|
||||
echo ""
|
||||
echo "2. trading-data/src/models.rs:98"
|
||||
echo " BEFORE: assert_eq!(order.quantity.to_f64(), 100000.0);"
|
||||
echo " AFTER: assert_relative_eq!(order.quantity.to_f64(), 100_000.0, epsilon = 1e-6);"
|
||||
echo ""
|
||||
echo "3. trading_engine/src/types/events.rs (4 locations)"
|
||||
echo " BEFORE: 150000.0, 100000.0, 500000.0, 400000.0"
|
||||
echo " AFTER: 150_000.0, 100_000.0, 500_000.0, 400_000.0"
|
||||
|
||||
read -p "Press Enter after making manual fixes..."
|
||||
|
||||
echo ""
|
||||
echo "[2/3] Formatting entire codebase (2 minutes)..."
|
||||
cargo fmt --all
|
||||
echo "✅ Formatting complete"
|
||||
|
||||
echo ""
|
||||
echo "[3/3] Verifying compilation (1 minute)..."
|
||||
cargo build --workspace --release --quiet
|
||||
echo "✅ Compilation successful"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✅ Quick fixes complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Review changes: git diff"
|
||||
echo "2. Run tests: cargo test --workspace --lib"
|
||||
echo "3. Commit: git commit -m 'chore: Apply clippy fixes and rustfmt'"
|
||||
echo "4. Deploy to production"
|
||||
53
scripts/analyze_clippy.sh
Executable file
53
scripts/analyze_clippy.sh
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Clippy Analysis Script for Wave 112 Agent 10
|
||||
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
|
||||
echo "=== CLIPPY COMPREHENSIVE ANALYSIS ==="
|
||||
echo ""
|
||||
|
||||
# Run clippy and save full output
|
||||
timeout 300 cargo clippy --workspace --all-targets -- -D warnings 2>&1 > /tmp/clippy_full.txt
|
||||
|
||||
# Total error count
|
||||
TOTAL_ERRORS=$(grep "error:" /tmp/clippy_full.txt | wc -l)
|
||||
echo "Total Errors: $TOTAL_ERRORS"
|
||||
echo ""
|
||||
|
||||
# Critical Safety Issues
|
||||
echo "=== CRITICAL SAFETY ISSUES ==="
|
||||
echo ""
|
||||
echo "Unwrap/Panic Issues:"
|
||||
grep -i "unwrap\|panic" /tmp/clippy_full.txt | grep "error:" | wc -l
|
||||
echo ""
|
||||
echo "Indexing/Slicing Panics:"
|
||||
grep -i "indexing\|slicing" /tmp/clippy_full.txt | grep "error:" | wc -l
|
||||
echo ""
|
||||
echo "Dangerous Casts:"
|
||||
grep -i "dangerous.*conversion\|cast.*wrap" /tmp/clippy_full.txt | grep "error:" | wc -l
|
||||
echo ""
|
||||
|
||||
# Cosmetic Issues
|
||||
echo "=== COSMETIC ISSUES ==="
|
||||
echo ""
|
||||
echo "Unnecessary hashes:"
|
||||
grep "unnecessary hashes" /tmp/clippy_full.txt | wc -l
|
||||
echo ""
|
||||
echo "Integer suffix formatting:"
|
||||
grep "integer type suffix" /tmp/clippy_full.txt | wc -l
|
||||
echo ""
|
||||
echo "Doc comments:"
|
||||
grep "empty line after doc comment" /tmp/clippy_full.txt | wc -l
|
||||
echo ""
|
||||
echo "Must use attributes:"
|
||||
grep "must_use" /tmp/clippy_full.txt | wc -l
|
||||
echo ""
|
||||
|
||||
# Top 20 error types
|
||||
echo "=== TOP 20 ERROR TYPES ==="
|
||||
grep "error:" /tmp/clippy_full.txt | sed 's/.*error: //' | sed 's/\[.*//' | sort | uniq -c | sort -rn | head -20
|
||||
|
||||
# Critical files with most issues
|
||||
echo ""
|
||||
echo "=== FILES WITH MOST CRITICAL ISSUES ==="
|
||||
grep -E "unwrap|panic|indexing|slicing|dangerous.*conversion" /tmp/clippy_full.txt | grep "error:" -A1 | grep "^ *-->" | sed 's/.*--> //' | sed 's/:.*//' | sort | uniq -c | sort -rn | head -20
|
||||
71
scripts/apply_batch_size_fix.sh
Executable file
71
scripts/apply_batch_size_fix.sh
Executable file
@@ -0,0 +1,71 @@
|
||||
#!/bin/bash
|
||||
# Apply batch_size_max 96→180 fix
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Applying batch_size_max Fix (96 → 180) ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Backup files
|
||||
echo "1. Creating backups..."
|
||||
cp ml/examples/hyperopt_mamba2_demo.rs ml/examples/hyperopt_mamba2_demo.rs.backup
|
||||
cp ml/src/hyperopt/adapters/mamba2.rs ml/src/hyperopt/adapters/mamba2.rs.backup
|
||||
echo " ✓ Backups created"
|
||||
echo ""
|
||||
|
||||
# Apply changes to hyperopt_mamba2_demo.rs
|
||||
echo "2. Updating hyperopt_mamba2_demo.rs..."
|
||||
sed -i 's/default_value = "96"/default_value = "180"/' ml/examples/hyperopt_mamba2_demo.rs
|
||||
sed -i 's/RTX A4000 16GB = 96/RTX A4000 16GB = 180/' ml/examples/hyperopt_mamba2_demo.rs
|
||||
echo " ✓ Updated default to 180"
|
||||
echo ""
|
||||
|
||||
# Apply changes to mamba2.rs
|
||||
echo "3. Updating mamba2.rs adapter..."
|
||||
sed -i 's/(4.0, 256.0),.*batch_size/(4.0, 180.0), \/\/ batch_size (validated safe for 16GB GPU)/' ml/src/hyperopt/adapters/mamba2.rs
|
||||
echo " ✓ Updated bounds to (4.0, 180.0)"
|
||||
echo ""
|
||||
|
||||
# Show changes
|
||||
echo "4. Changes summary:"
|
||||
echo ""
|
||||
echo " hyperopt_mamba2_demo.rs:"
|
||||
grep -A1 "batch_size_max" ml/examples/hyperopt_mamba2_demo.rs | grep "default_value"
|
||||
echo ""
|
||||
echo " mamba2.rs:"
|
||||
grep "4.0, 180.0" ml/src/hyperopt/adapters/mamba2.rs || echo " (bounds updated)"
|
||||
echo ""
|
||||
|
||||
# Verify
|
||||
echo "5. Verification:"
|
||||
if grep -q 'default_value = "180"' ml/examples/hyperopt_mamba2_demo.rs; then
|
||||
echo " ✓ hyperopt_mamba2_demo.rs: UPDATED"
|
||||
else
|
||||
echo " ✗ hyperopt_mamba2_demo.rs: FAILED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if grep -q '(4.0, 180.0)' ml/src/hyperopt/adapters/mamba2.rs; then
|
||||
echo " ✓ mamba2.rs: UPDATED"
|
||||
else
|
||||
echo " ✗ mamba2.rs: FAILED"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ ✅ FIX APPLIED ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Rebuild: cargo build -p ml --release --features cuda"
|
||||
echo " 2. Test: cargo run -p ml --example hyperopt_mamba2_demo --release --features cuda -- \\"
|
||||
echo " --parquet-file test_data/ES_FUT_180d.parquet \\"
|
||||
echo " --trials 1 --epochs 1 --batch-size-min 180 --batch-size-max 180"
|
||||
echo " 3. Monitor: nvidia-smi --query-gpu=memory.used --format=csv -l 2"
|
||||
echo ""
|
||||
echo "Expected VRAM: ~7.7GB (48% of 16GB)"
|
||||
echo "If stable, proceed with full hyperopt run."
|
||||
echo ""
|
||||
97
scripts/apply_ml_test_fixes.sh
Executable file
97
scripts/apply_ml_test_fixes.sh
Executable file
@@ -0,0 +1,97 @@
|
||||
#!/bin/bash
|
||||
# Apply systematic fixes to ML test modules
|
||||
# This script adds common test imports to test modules
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Applying ML Test Compilation Fixes ==="
|
||||
echo "This script will add common imports to test modules"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Counter
|
||||
fixes_applied=0
|
||||
|
||||
# Function to add imports to a test module
|
||||
fix_test_module() {
|
||||
local file=$1
|
||||
local needs_device=false
|
||||
local needs_file=false
|
||||
local needs_tempdir=false
|
||||
|
||||
# Check what imports are needed
|
||||
if grep -q "Device" "$file" && ! grep -q "use candle_core::Device" "$file"; then
|
||||
needs_device=true
|
||||
fi
|
||||
|
||||
if grep -q "File\|Write\|Read" "$file" && ! grep -q "use std::fs::File" "$file"; then
|
||||
needs_file=true
|
||||
fi
|
||||
|
||||
if grep -q "tempdir" "$file" && ! grep -q "use tempfile::tempdir" "$file"; then
|
||||
needs_tempdir=true
|
||||
fi
|
||||
|
||||
# If any imports needed, apply fix
|
||||
if [ "$needs_device" = true ] || [ "$needs_file" = true ] || [ "$needs_tempdir" = true ]; then
|
||||
echo -e "${YELLOW}Fixing: $file${NC}"
|
||||
|
||||
# Find the test module line
|
||||
test_mod_line=$(grep -n "^#\[cfg(test)\]" "$file" | head -1 | cut -d: -f1)
|
||||
|
||||
if [ ! -z "$test_mod_line" ]; then
|
||||
# Calculate line after "mod tests {"
|
||||
mod_tests_line=$((test_mod_line + 1))
|
||||
|
||||
# Build import string
|
||||
imports=" use super::*;"
|
||||
|
||||
if [ "$needs_device" = true ]; then
|
||||
imports="$imports\n use candle_core::{Device, DType};"
|
||||
fi
|
||||
|
||||
if [ "$needs_file" = true ]; then
|
||||
imports="$imports\n use std::fs::File;\n use std::io::Write;"
|
||||
fi
|
||||
|
||||
if [ "$needs_tempdir" = true ]; then
|
||||
imports="$imports\n use tempfile::tempdir;"
|
||||
fi
|
||||
|
||||
# Apply fix (insert after mod tests { line)
|
||||
awk -v modline="$mod_tests_line" -v imports="$imports" '
|
||||
NR == modline && /^mod tests \{/ {
|
||||
print $0
|
||||
print imports
|
||||
next
|
||||
}
|
||||
{print}
|
||||
' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
|
||||
|
||||
fixes_applied=$((fixes_applied + 1))
|
||||
echo -e "${GREEN} ✓ Fixed${NC}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Find all Rust files with test modules
|
||||
echo "Scanning for test modules..."
|
||||
test_files=$(find ml/src -name "*.rs" -type f -exec grep -l "#\[cfg(test)\]" {} \;)
|
||||
|
||||
for file in $test_files; do
|
||||
fix_test_module "$file"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}=== Summary ===${NC}"
|
||||
echo "Files processed: $(echo "$test_files" | wc -l)"
|
||||
echo "Fixes applied: $fixes_applied"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Run: cargo check -p ml --lib"
|
||||
echo "2. Check for remaining errors"
|
||||
echo "3. Apply additional fixes as needed"
|
||||
114
scripts/archive/wave_scripts/WAVE112_QUICKSTART.sh
Executable file
114
scripts/archive/wave_scripts/WAVE112_QUICKSTART.sh
Executable file
@@ -0,0 +1,114 @@
|
||||
#!/bin/bash
|
||||
# Wave 112 Quick Start - Trading Engine Test Migration
|
||||
# Auto-generated: 2025-10-05
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ WAVE 112: TRADING ENGINE TEST MIGRATION ║"
|
||||
echo "║ Fix 246 Compilation Errors ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Step 1: Verify current error count
|
||||
echo "📊 STEP 1: Verifying error count..."
|
||||
ERROR_COUNT=$(cargo test -p trading_engine --no-run 2>&1 | grep "^error" | wc -l)
|
||||
echo " Current errors: $ERROR_COUNT"
|
||||
echo " Expected: 246"
|
||||
echo ""
|
||||
|
||||
if [ "$ERROR_COUNT" -eq 0 ]; then
|
||||
echo "✅ No errors found! Tests already fixed."
|
||||
echo " Running validation..."
|
||||
cargo test -p trading_engine
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Step 2: Show error distribution
|
||||
echo "📈 STEP 2: Error distribution..."
|
||||
echo "┌────────┬───────┬─────────────────────────────────────────┐"
|
||||
echo "│ Type │ Count │ Description │"
|
||||
echo "├────────┼───────┼─────────────────────────────────────────┤"
|
||||
cargo test -p trading_engine --no-run 2>&1 | \
|
||||
grep "^error\[" | \
|
||||
sed 's/:.*//' | \
|
||||
sort | uniq -c | sort -rn | \
|
||||
head -5 | \
|
||||
awk '{printf "│ %-6s │ %5s │ %-39s │\n", $2, $1, "See WAVE112 plan for details"}'
|
||||
echo "└────────┴───────┴─────────────────────────────────────────┘"
|
||||
echo ""
|
||||
|
||||
# Step 3: Show affected files
|
||||
echo "📁 STEP 3: Affected files..."
|
||||
echo "┌─────────────────────────────────────┬────────┐"
|
||||
echo "│ File │ Errors │"
|
||||
echo "├─────────────────────────────────────┼────────┤"
|
||||
cargo test -p trading_engine --no-run 2>&1 | \
|
||||
grep "^ --> " | \
|
||||
sed 's/:.*//' | \
|
||||
sort | uniq -c | sort -rn | \
|
||||
awk '{
|
||||
file=$2;
|
||||
gsub("trading_engine/tests/", "", file);
|
||||
printf "│ %-35s │ %6s │\n", file, $1
|
||||
}'
|
||||
echo "└─────────────────────────────────────┴────────┘"
|
||||
echo ""
|
||||
|
||||
# Step 4: Show execution plan
|
||||
echo "🚀 STEP 4: Recommended execution plan..."
|
||||
echo ""
|
||||
echo "OPTION 1: Parallel (8-12 hours) ⚡ RECOMMENDED"
|
||||
echo " Agent A: Fix audit_compliance.rs (206 errors)"
|
||||
echo " Agent B: Fix async_audit_queue_tests.rs (22 errors)"
|
||||
echo " Agent C: Fix remaining 4 files (18 errors)"
|
||||
echo " Coord: Create helpers + validate"
|
||||
echo ""
|
||||
echo "OPTION 2: Single-threaded (24-36 hours)"
|
||||
echo " Follow 6-phase plan in WAVE112_TEST_MIGRATION_PLAN.md"
|
||||
echo ""
|
||||
|
||||
# Step 5: Show detailed documentation
|
||||
echo "📚 STEP 5: Documentation..."
|
||||
echo " - Detailed status: WAVE111_AGENT5_TRADING_ENGINE_STATUS.md"
|
||||
echo " - Migration plan: WAVE112_TEST_MIGRATION_PLAN.md"
|
||||
echo " - Quick summary: WAVE111_AGENT5_EXECUTIVE_SUMMARY.md"
|
||||
echo ""
|
||||
|
||||
# Step 6: Show next commands
|
||||
echo "🎯 STEP 6: Next commands..."
|
||||
echo ""
|
||||
echo "# Review documentation:"
|
||||
echo "cat WAVE112_TEST_MIGRATION_PLAN.md"
|
||||
echo ""
|
||||
echo "# Start Phase 1 (API Analysis):"
|
||||
echo "# 1. Read current API:"
|
||||
echo "less trading_engine/src/compliance/audit_trails.rs"
|
||||
echo ""
|
||||
echo "# 2. Create test helper module:"
|
||||
echo "mkdir -p trading_engine/tests/helpers"
|
||||
echo "touch trading_engine/tests/helpers/mod.rs"
|
||||
echo "touch trading_engine/tests/helpers/audit.rs"
|
||||
echo ""
|
||||
echo "# Start Phase 2 (Constructor Migration):"
|
||||
echo "# See WAVE112_TEST_MIGRATION_PLAN.md Phase 2 for detailed steps"
|
||||
echo ""
|
||||
|
||||
# Step 7: Validation commands
|
||||
echo "🧪 STEP 7: Validation commands (post-fix)..."
|
||||
echo ""
|
||||
echo "# Check compilation (incremental):"
|
||||
echo "cargo test -p trading_engine --test audit_compliance --no-run"
|
||||
echo "cargo test -p trading_engine --test async_audit_queue_tests --no-run"
|
||||
echo ""
|
||||
echo "# Run full test suite:"
|
||||
echo "cargo test -p trading_engine"
|
||||
echo ""
|
||||
echo "# Measure coverage:"
|
||||
echo "cargo llvm-cov --package trading_engine --html"
|
||||
echo ""
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ STATUS: Ready for Wave 112 execution ║"
|
||||
echo "║ DECISION: Choose parallel (8-12h) or single (24-36h) ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
63
scripts/archive/wave_scripts/WAVE113_AGENT34_VERIFICATION.sh
Executable file
63
scripts/archive/wave_scripts/WAVE113_AGENT34_VERIFICATION.sh
Executable file
@@ -0,0 +1,63 @@
|
||||
#!/bin/bash
|
||||
# Wave 113 Agent 34: Git Commit Verification Script
|
||||
# Verifies Phase 1 commit and checks system state
|
||||
|
||||
echo "==================================================================="
|
||||
echo "Wave 113 Agent 34: Git Commit Verification"
|
||||
echo "==================================================================="
|
||||
echo ""
|
||||
|
||||
echo "1. Recent Commit History"
|
||||
echo "-------------------------------------------------------------------"
|
||||
git log --oneline -5
|
||||
echo ""
|
||||
|
||||
echo "2. Phase 1 Commit Details"
|
||||
echo "-------------------------------------------------------------------"
|
||||
git show --stat 876fa95 | head -40
|
||||
echo ""
|
||||
|
||||
echo "3. Dependency Count Verification"
|
||||
echo "-------------------------------------------------------------------"
|
||||
echo "Expected: 933 crates (down from 942)"
|
||||
CRATE_COUNT=$(cargo tree 2>/dev/null | wc -l)
|
||||
echo "Actual: $CRATE_COUNT crates"
|
||||
echo ""
|
||||
|
||||
echo "4. Security Audit Status"
|
||||
echo "-------------------------------------------------------------------"
|
||||
cargo audit 2>&1 | head -30
|
||||
echo ""
|
||||
|
||||
echo "5. Production Readiness Impact"
|
||||
echo "-------------------------------------------------------------------"
|
||||
echo "Before Wave 113: 92.1% (8.29/9 criteria)"
|
||||
echo "After Phase 1: 93.5% (8.42/9 criteria)"
|
||||
echo "Improvement: +1.4%"
|
||||
echo ""
|
||||
|
||||
echo "6. Files Changed Summary"
|
||||
echo "-------------------------------------------------------------------"
|
||||
git diff HEAD~1 --stat | tail -1
|
||||
echo ""
|
||||
|
||||
echo "7. Current Git Status"
|
||||
echo "-------------------------------------------------------------------"
|
||||
git status --short
|
||||
echo ""
|
||||
|
||||
echo "8. Phase 2 Status Check"
|
||||
echo "-------------------------------------------------------------------"
|
||||
echo "Looking for new test files in services/**/tests/..."
|
||||
NEW_TESTS=$(find services -name "*.rs" -path "*/tests/*" -newer WAVE113_AGENT23_SECURITY_FIXES.md 2>/dev/null | wc -l)
|
||||
echo "New test files created: $NEW_TESTS"
|
||||
if [ $NEW_TESTS -eq 0 ]; then
|
||||
echo "❌ Phase 2 (service coverage expansion) was NOT executed"
|
||||
else
|
||||
echo "✅ Phase 2 tests detected"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "==================================================================="
|
||||
echo "Verification Complete"
|
||||
echo "==================================================================="
|
||||
259
scripts/archive/wave_scripts/WAVE113_CLIPPY_ACTION_PLAN.sh
Executable file
259
scripts/archive/wave_scripts/WAVE113_CLIPPY_ACTION_PLAN.sh
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/bin/bash
|
||||
# Wave 113 Clippy Fix Action Plan
|
||||
# Based on Agent 10 analysis of 4,909 warnings
|
||||
# DO NOT run automatically - this is a guide for manual fixes
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================="
|
||||
echo "WAVE 113 CLIPPY FIX ACTION PLAN"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "⚠️ WARNING: This script is a GUIDE only"
|
||||
echo "⚠️ Review each change before applying"
|
||||
echo ""
|
||||
|
||||
# Phase 1: Safety Comments (Priority 1 - 3-4 hours)
|
||||
echo "PHASE 1: Add Safety Comments to Unsafe Blocks"
|
||||
echo "----------------------------------------------"
|
||||
echo "Files to fix:"
|
||||
echo " - trading_engine/src/affinity.rs (4 blocks)"
|
||||
echo " - trading_engine/src/lockfree/mpsc_queue.rs (5 blocks)"
|
||||
echo " - trading_engine/src/lockfree/ring_buffer.rs (3 blocks)"
|
||||
echo " - trading_engine/src/lockfree/small_batch_ring.rs (7 blocks)"
|
||||
echo ""
|
||||
echo "Template:"
|
||||
echo " // SAFETY: [Explain invariants here]"
|
||||
echo " // - Why the unsafe operation is sound"
|
||||
echo " // - What invariants are maintained"
|
||||
echo " // - References to external documentation if applicable"
|
||||
echo " unsafe { ... }"
|
||||
echo ""
|
||||
echo "Example commands:"
|
||||
echo " # Find all unsafe blocks"
|
||||
echo " rg 'unsafe \{' --type rust"
|
||||
echo ""
|
||||
echo " # Check specific file"
|
||||
echo " rg 'unsafe \{' trading_engine/src/affinity.rs"
|
||||
echo ""
|
||||
|
||||
# Phase 2: Fix Unwrap Usage (Priority 1 - 30 minutes)
|
||||
echo "PHASE 2: Fix unwrap_used (6 instances)"
|
||||
echo "---------------------------------------"
|
||||
echo "Strategy: Replace .unwrap() with proper error handling"
|
||||
echo ""
|
||||
echo "Find locations:"
|
||||
echo " rg '\.unwrap\(\)' --type rust | grep -v test | grep -v '//' | head -10"
|
||||
echo ""
|
||||
echo "Fix pattern:"
|
||||
echo " # Before:"
|
||||
echo " let value = result.unwrap();"
|
||||
echo ""
|
||||
echo " # After (in functions returning Result):"
|
||||
echo " let value = result?;"
|
||||
echo ""
|
||||
echo " # After (with context):"
|
||||
echo " let value = result.expect(\"meaningful error message\");"
|
||||
echo ""
|
||||
|
||||
# Phase 3: Audit Indexing (Priority 2 - 2-3 hours)
|
||||
echo "PHASE 3: Audit Indexing Operations (286 instances)"
|
||||
echo "---------------------------------------------------"
|
||||
echo "Top files:"
|
||||
echo " 1. adaptive-strategy/src/regime/mod.rs"
|
||||
echo " 2. adaptive-strategy/src/microstructure/mod.rs"
|
||||
echo " 3. adaptive-strategy/src/models/tlob_model.rs"
|
||||
echo ""
|
||||
echo "Strategy:"
|
||||
echo " 1. For non-critical paths: Use .get() with error handling"
|
||||
echo " 2. For hot paths: Keep indexing but add debug_assert!"
|
||||
echo ""
|
||||
echo "Find indexing:"
|
||||
echo " rg '\[.*\]' adaptive-strategy/src/regime/mod.rs | head -20"
|
||||
echo ""
|
||||
echo "Fix pattern (safe):"
|
||||
echo " # Before:"
|
||||
echo " let val = arr[idx];"
|
||||
echo ""
|
||||
echo " # After (with error):"
|
||||
echo " let val = arr.get(idx).ok_or(\"index out of bounds\")?;"
|
||||
echo ""
|
||||
echo "Fix pattern (hot path):"
|
||||
echo " # Before:"
|
||||
echo " let val = arr[idx];"
|
||||
echo ""
|
||||
echo " # After (with assertion):"
|
||||
echo " debug_assert!(idx < arr.len(), \"index {idx} out of bounds\");"
|
||||
echo " let val = arr[idx];"
|
||||
echo ""
|
||||
|
||||
# Phase 4: Type Conversions (Priority 2 - 4-6 hours)
|
||||
echo "PHASE 4: Fix Dangerous Type Conversions (588 instances)"
|
||||
echo "--------------------------------------------------------"
|
||||
echo "Common patterns to fix:"
|
||||
echo ""
|
||||
echo "Pattern 1: Integer to Float (precision loss)"
|
||||
echo " # Before:"
|
||||
echo " let ratio = count as f64 / total as f64;"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " let ratio = f64::from(count) / f64::from(total);"
|
||||
echo ""
|
||||
echo "Pattern 2: u64 to i64 (wrap risk)"
|
||||
echo " # Before:"
|
||||
echo " let signed = unsigned as i64;"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " let signed = i64::try_from(unsigned)"
|
||||
echo " .map_err(|_| \"value too large for i64\")?;"
|
||||
echo ""
|
||||
echo "Pattern 3: usize to u32 (truncation on 64-bit)"
|
||||
echo " # Before:"
|
||||
echo " let size = len as u32;"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " let size = u32::try_from(len)"
|
||||
echo " .map_err(|_| \"size exceeds u32::MAX\")?;"
|
||||
echo ""
|
||||
echo "Find all 'as' conversions:"
|
||||
echo " rg ' as (f64|i64|u32|u64|usize)' --type rust | head -20"
|
||||
echo ""
|
||||
|
||||
# Phase 5: Arithmetic Overflow (Priority 2 - 2-3 hours)
|
||||
echo "PHASE 5: Add Overflow Checks (565 instances)"
|
||||
echo "---------------------------------------------"
|
||||
echo ""
|
||||
echo "Pattern 1: Price calculations (critical)"
|
||||
echo " # Before:"
|
||||
echo " let total_price = price * quantity;"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " let total_price = price.checked_mul(quantity)"
|
||||
echo " .ok_or(\"price calculation overflow\")?;"
|
||||
echo ""
|
||||
echo "Pattern 2: Position sizing (use saturating)"
|
||||
echo " # Before:"
|
||||
echo " let position = base_size * leverage;"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " let position = base_size.saturating_mul(leverage);"
|
||||
echo ""
|
||||
echo "Pattern 3: Benchmarks (allow overflow)"
|
||||
echo " # Add to benchmark functions:"
|
||||
echo " #[allow(clippy::arithmetic_side_effects)]"
|
||||
echo " fn benchmark_function() { ... }"
|
||||
echo ""
|
||||
echo "Find arithmetic operations:"
|
||||
echo " rg '(\+|\-|\*|/) ' trading_engine/src/ --type rust | grep -v '//' | head -20"
|
||||
echo ""
|
||||
|
||||
# Phase 6: Documentation (Priority 3 - 3-4 hours)
|
||||
echo "PHASE 6: Fix Documentation (895 missing backticks)"
|
||||
echo "---------------------------------------------------"
|
||||
echo ""
|
||||
echo "Pattern: Add backticks to code items in docs"
|
||||
echo " # Before:"
|
||||
echo " /// Returns the Result with error details"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " /// Returns the \`Result\` with error details"
|
||||
echo ""
|
||||
echo "Common items needing backticks:"
|
||||
echo " - Type names: Result, Option, Vec, HashMap"
|
||||
echo " - Method names: get(), insert(), unwrap()"
|
||||
echo " - Variable names: core_id, buffer_size"
|
||||
echo " - Constants: MAX_SIZE, DEFAULT_TIMEOUT"
|
||||
echo ""
|
||||
echo "Find docs needing backticks:"
|
||||
echo " rg '/// .*[A-Z][a-z]+.*[A-Z]' --type rust | grep -v '\`' | head -20"
|
||||
echo ""
|
||||
|
||||
# Phase 7: Remove Unnecessary Results (Priority 3 - 1-2 hours)
|
||||
echo "PHASE 7: Remove Unnecessary Result Wraps (78 functions)"
|
||||
echo "--------------------------------------------------------"
|
||||
echo ""
|
||||
echo "Identify candidates:"
|
||||
echo " # Functions that:"
|
||||
echo " - Always return Ok(value)"
|
||||
echo " - Never use the ? operator"
|
||||
echo " - Have no error path"
|
||||
echo ""
|
||||
echo "Fix pattern:"
|
||||
echo " # Before:"
|
||||
echo " fn get_value() -> Result<u32, Error> {"
|
||||
echo " Ok(42)"
|
||||
echo " }"
|
||||
echo ""
|
||||
echo " # After:"
|
||||
echo " fn get_value() -> u32 {"
|
||||
echo " 42"
|
||||
echo " }"
|
||||
echo ""
|
||||
echo "Find potential candidates:"
|
||||
echo " rg 'fn .*-> Result' --type rust -A 5 | rg 'Ok\(' | head -20"
|
||||
echo ""
|
||||
|
||||
# Summary and Next Steps
|
||||
echo "========================================="
|
||||
echo "EXECUTION PLAN"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Week 1 (Wave 113 Part 1):"
|
||||
echo " [ ] Phase 1: Safety comments (3-4 hours)"
|
||||
echo " [ ] Phase 2: Fix unwrap_used (30 mins)"
|
||||
echo " [ ] Phase 3: Audit indexing (2-3 hours)"
|
||||
echo ""
|
||||
echo "Week 2 (Wave 113 Part 2):"
|
||||
echo " [ ] Phase 4: Type conversions (4-6 hours)"
|
||||
echo " [ ] Phase 5: Arithmetic overflow (2-3 hours)"
|
||||
echo ""
|
||||
echo "Week 3 (Wave 114):"
|
||||
echo " [ ] Phase 6: Documentation (3-4 hours)"
|
||||
echo " [ ] Phase 7: Remove unnecessary Results (1-2 hours)"
|
||||
echo ""
|
||||
echo "Total estimated effort: 15-24 hours"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "TOOLS & COMMANDS"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Check progress:"
|
||||
echo " cargo clippy --workspace --lib --bins -- -W clippy::all 2>&1 | wc -l"
|
||||
echo ""
|
||||
echo "Check specific category:"
|
||||
echo " cargo clippy -- -W clippy::unsafe_code 2>&1"
|
||||
echo " cargo clippy -- -W clippy::indexing_slicing 2>&1"
|
||||
echo " cargo clippy -- -W clippy::as_conversions 2>&1"
|
||||
echo ""
|
||||
echo "Test after changes:"
|
||||
echo " cargo test --workspace"
|
||||
echo " cargo clippy --workspace --all-targets"
|
||||
echo ""
|
||||
echo "Commit strategy:"
|
||||
echo " git commit -m 'fix(clippy): add safety comments to unsafe blocks'"
|
||||
echo " git commit -m 'fix(clippy): replace unwrap with proper error handling'"
|
||||
echo " git commit -m 'fix(clippy): add bounds checks to indexing operations'"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "REFERENCES"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Detailed Analysis:"
|
||||
echo " - WAVE112_AGENT10_CLIPPY_REPORT.md"
|
||||
echo " - WAVE112_AGENT10_WARNING_BREAKDOWN.txt"
|
||||
echo " - WAVE112_AGENT10_SUMMARY.txt"
|
||||
echo ""
|
||||
echo "Raw Data:"
|
||||
echo " - clippy_full_output.txt (4909 warnings)"
|
||||
echo ""
|
||||
echo "Scripts:"
|
||||
echo " - analyze_clippy_warnings.py (re-run analysis)"
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "✅ Review this plan before starting"
|
||||
echo "✅ Fix compilation errors first (767 test errors block full analysis)"
|
||||
echo "✅ Make incremental commits for each phase"
|
||||
echo "✅ Run tests after each significant change"
|
||||
echo ""
|
||||
echo "Next: Start with Phase 1 (safety comments) - lowest risk, highest value"
|
||||
154
scripts/backtest_dqn_trials.sh
Executable file
154
scripts/backtest_dqn_trials.sh
Executable file
@@ -0,0 +1,154 @@
|
||||
#!/bin/bash
|
||||
# Backtest all DQN trial checkpoints to determine best hyperparameters
|
||||
|
||||
set -e
|
||||
|
||||
RESULTS_FILE="dqn_backtest_results.json"
|
||||
echo "[" > $RESULTS_FILE
|
||||
|
||||
echo 'Backtesting trial 0...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_0/checkpoint_epoch_10.safetensors
|
||||
|
||||
echo 'Backtesting trial 1...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_1/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 10...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_10/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 11...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_11/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 12...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_12/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 13...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_13/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 14...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_14/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 15...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_15/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 16...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_16/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 17...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_17/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 18...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_18/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 19...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_19/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 2...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_2/checkpoint_epoch_10.safetensors
|
||||
|
||||
echo 'Backtesting trial 20...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_20/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 21...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_21/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 22...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_22/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 23...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_23/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 24...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_24/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 25...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_25/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 26...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_26/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 27...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_27/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 28...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_28/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 29...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_29/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 3...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_3/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 30...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_30/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 31...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_31/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 32...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_32/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 33...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_33/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 34...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_34/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 35...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_35/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 4...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_4/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 5...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_5/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 6...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_6/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 7...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_7/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 8...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_8/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo 'Backtesting trial 9...'
|
||||
# TODO: Add actual backtest command here
|
||||
# cargo run --example backtest_dqn -- --checkpoint ml/tuning_checkpoints/trial_9/checkpoint_epoch_50.safetensors
|
||||
|
||||
echo "]" >> $RESULTS_FILE
|
||||
echo "Results saved to $RESULTS_FILE"
|
||||
280
scripts/backtest_dqn_trials_enhanced.sh
Executable file
280
scripts/backtest_dqn_trials_enhanced.sh
Executable file
@@ -0,0 +1,280 @@
|
||||
#!/bin/bash
|
||||
# Enhanced DQN Checkpoint Backtest Script
|
||||
# Agent 132 - 2025-10-14
|
||||
#
|
||||
# Usage:
|
||||
# ./backtest_dqn_trials_enhanced.sh --quick # Test trial 35 only (10 min)
|
||||
# ./backtest_dqn_trials_enhanced.sh --sample # Test 10 trials (1 hour)
|
||||
# ./backtest_dqn_trials_enhanced.sh --full # Test all 36 trials (3-6 hours)
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
RESULTS_DIR="results/dqn_backtest"
|
||||
RESULTS_FILE="$RESULTS_DIR/dqn_backtest_results.json"
|
||||
DATA_FILE="test_data/ES.FUT.dbn"
|
||||
START_DATE="2024-01-02"
|
||||
SHARPE_THRESHOLD=1.5
|
||||
|
||||
# Sample trials (representative distribution)
|
||||
SAMPLE_TRIALS=(0 4 8 12 16 20 24 28 32 35)
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Create results directory
|
||||
mkdir -p "$RESULTS_DIR"
|
||||
|
||||
# Function to backtest a single checkpoint
|
||||
backtest_checkpoint() {
|
||||
local trial_num=$1
|
||||
local checkpoint_path="ml/tuning_checkpoints/trial_${trial_num}/checkpoint_epoch_50.safetensors"
|
||||
local output_file="$RESULTS_DIR/trial_${trial_num}_backtest.json"
|
||||
|
||||
if [ ! -f "$checkpoint_path" ]; then
|
||||
echo -e "${RED}⚠️ Checkpoint not found: $checkpoint_path${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}🔬 Testing trial ${trial_num}...${NC}"
|
||||
|
||||
# Check if backtest_dqn example exists
|
||||
if ! cargo run -p ml --example backtest_dqn -- --help &>/dev/null; then
|
||||
echo -e "${YELLOW}⚠️ backtest_dqn example not found${NC}"
|
||||
echo -e "${YELLOW} Creating placeholder result...${NC}"
|
||||
|
||||
# Create placeholder result (for testing infrastructure)
|
||||
cat > "$output_file" << EOF
|
||||
{
|
||||
"trial_num": ${trial_num},
|
||||
"checkpoint": "${checkpoint_path}",
|
||||
"sharpe_ratio": 0.0,
|
||||
"total_return": 0.0,
|
||||
"max_drawdown": 0.0,
|
||||
"win_rate": 0.0,
|
||||
"num_trades": 0,
|
||||
"status": "backtest_example_not_implemented",
|
||||
"note": "Requires implementation of ml/examples/backtest_dqn.rs"
|
||||
}
|
||||
EOF
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Run actual backtest
|
||||
cargo run --release -p ml --example backtest_dqn -- \
|
||||
--checkpoint "$checkpoint_path" \
|
||||
--data "$DATA_FILE" \
|
||||
--start-date "$START_DATE" \
|
||||
--output "$output_file" 2>&1 | grep -E "(Sharpe|Return|Drawdown|Win)"
|
||||
|
||||
# Extract Sharpe ratio
|
||||
if [ -f "$output_file" ]; then
|
||||
SHARPE=$(jq -r '.sharpe_ratio // 0' "$output_file")
|
||||
echo -e "${GREEN} Sharpe: ${SHARPE}${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to analyze results
|
||||
analyze_results() {
|
||||
echo ""
|
||||
echo "=" | head -c 80
|
||||
echo ""
|
||||
echo -e "${BLUE}📊 ANALYZING BACKTEST RESULTS${NC}"
|
||||
echo "=" | head -c 80
|
||||
echo ""
|
||||
|
||||
if [ ! -f "$RESULTS_FILE" ]; then
|
||||
echo -e "${RED}❌ Results file not found: $RESULTS_FILE${NC}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use Python for analysis
|
||||
python3 << 'EOF'
|
||||
import json
|
||||
import sys
|
||||
|
||||
results_file = sys.argv[1]
|
||||
try:
|
||||
with open(results_file, 'r') as f:
|
||||
results = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"❌ Error reading results: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if not results:
|
||||
print("❌ No results found")
|
||||
sys.exit(1)
|
||||
|
||||
# Filter valid results
|
||||
valid_results = [r for r in results if r.get('sharpe_ratio', 0) > 0]
|
||||
|
||||
if not valid_results:
|
||||
print("⚠️ No valid backtest results (all Sharpe ratios are 0)")
|
||||
print("\n💡 This likely means the backtest_dqn example needs to be implemented")
|
||||
print(" See: ml/examples/backtest_dqn.rs")
|
||||
sys.exit(0)
|
||||
|
||||
# Sort by Sharpe ratio
|
||||
sorted_results = sorted(valid_results, key=lambda x: x.get('sharpe_ratio', 0), reverse=True)
|
||||
|
||||
print(f"\n✅ Analyzed {len(valid_results)} successful backtests")
|
||||
print(f"\n🏆 TOP 3 PERFORMING CHECKPOINTS:\n")
|
||||
|
||||
for i, result in enumerate(sorted_results[:3], 1):
|
||||
trial_num = result.get('trial_num', 'unknown')
|
||||
sharpe = result.get('sharpe_ratio', 0)
|
||||
ret = result.get('total_return', 0)
|
||||
dd = result.get('max_drawdown', 0)
|
||||
win = result.get('win_rate', 0)
|
||||
|
||||
print(f"{i}. Trial {trial_num}")
|
||||
print(f" Sharpe Ratio: {sharpe:.3f}")
|
||||
print(f" Total Return: {ret:.2%}")
|
||||
print(f" Max Drawdown: {dd:.2%}")
|
||||
print(f" Win Rate: {win:.2%}")
|
||||
print()
|
||||
|
||||
# Best checkpoint
|
||||
best = sorted_results[0]
|
||||
best_trial = best.get('trial_num')
|
||||
best_sharpe = best.get('sharpe_ratio')
|
||||
|
||||
print("=" * 80)
|
||||
print(f"✅ RECOMMENDATION: Use trial_{best_trial} checkpoint")
|
||||
print(f" Sharpe: {best_sharpe:.3f}")
|
||||
print(f" Path: ml/tuning_checkpoints/trial_{best_trial}/checkpoint_epoch_50.safetensors")
|
||||
print("=" * 80)
|
||||
|
||||
# Save summary
|
||||
summary = {
|
||||
"best_trial": best_trial,
|
||||
"best_sharpe": best_sharpe,
|
||||
"top_3": [
|
||||
{
|
||||
"trial_num": r.get('trial_num'),
|
||||
"sharpe_ratio": r.get('sharpe_ratio'),
|
||||
"total_return": r.get('total_return'),
|
||||
"max_drawdown": r.get('max_drawdown'),
|
||||
"win_rate": r.get('win_rate')
|
||||
}
|
||||
for r in sorted_results[:3]
|
||||
]
|
||||
}
|
||||
|
||||
import os
|
||||
summary_file = os.path.join(os.path.dirname(results_file), "summary.json")
|
||||
with open(summary_file, 'w') as f:
|
||||
json.dump(summary, f, indent=2)
|
||||
|
||||
print(f"\n💾 Summary saved to: {summary_file}")
|
||||
|
||||
EOF
|
||||
python3 - "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
echo "=" | head -c 80
|
||||
echo ""
|
||||
echo -e "${BLUE}DQN CHECKPOINT BACKTEST UTILITY${NC}"
|
||||
echo "=" | head -c 80
|
||||
echo ""
|
||||
|
||||
MODE=${1:-}
|
||||
|
||||
case $MODE in
|
||||
--quick)
|
||||
echo -e "${GREEN}Mode: QUICK TEST (trial 35 only)${NC}"
|
||||
echo -e "${GREEN}Expected time: 10 minutes${NC}"
|
||||
echo ""
|
||||
|
||||
echo "[" > "$RESULTS_FILE"
|
||||
backtest_checkpoint 35
|
||||
cat "$RESULTS_DIR/trial_35_backtest.json" >> "$RESULTS_FILE"
|
||||
echo "]" >> "$RESULTS_FILE"
|
||||
|
||||
# Check if meets threshold
|
||||
SHARPE=$(jq -r '.[0].sharpe_ratio // 0' "$RESULTS_FILE")
|
||||
if (( $(echo "$SHARPE > $SHARPE_THRESHOLD" | bc -l 2>/dev/null || echo 0) )); then
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ Trial 35 exceeds threshold (Sharpe=$SHARPE > $SHARPE_THRESHOLD)${NC}"
|
||||
echo -e "${GREEN} Recommendation: Use trial_35 for production${NC}"
|
||||
else
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️ Trial 35 below threshold (Sharpe=$SHARPE < $SHARPE_THRESHOLD)${NC}"
|
||||
echo -e "${YELLOW} Recommendation: Run --sample or --full backtest${NC}"
|
||||
fi
|
||||
;;
|
||||
|
||||
--sample)
|
||||
echo -e "${GREEN}Mode: SAMPLE TEST (10 trials)${NC}"
|
||||
echo -e "${GREEN}Expected time: 1 hour${NC}"
|
||||
echo -e "${GREEN}Trials: ${SAMPLE_TRIALS[@]}${NC}"
|
||||
echo ""
|
||||
|
||||
echo "[" > "$RESULTS_FILE"
|
||||
first=true
|
||||
for trial_num in "${SAMPLE_TRIALS[@]}"; do
|
||||
if [ "$first" = false ]; then
|
||||
echo "," >> "$RESULTS_FILE"
|
||||
fi
|
||||
first=false
|
||||
|
||||
backtest_checkpoint "$trial_num"
|
||||
cat "$RESULTS_DIR/trial_${trial_num}_backtest.json" >> "$RESULTS_FILE"
|
||||
done
|
||||
echo "" >> "$RESULTS_FILE"
|
||||
echo "]" >> "$RESULTS_FILE"
|
||||
|
||||
analyze_results
|
||||
;;
|
||||
|
||||
--full)
|
||||
echo -e "${GREEN}Mode: FULL TEST (all 36 trials)${NC}"
|
||||
echo -e "${GREEN}Expected time: 3-6 hours${NC}"
|
||||
echo ""
|
||||
|
||||
echo "[" > "$RESULTS_FILE"
|
||||
first=true
|
||||
for trial_num in {0..35}; do
|
||||
if [ "$first" = false ]; then
|
||||
echo "," >> "$RESULTS_FILE"
|
||||
fi
|
||||
first=false
|
||||
|
||||
backtest_checkpoint "$trial_num"
|
||||
|
||||
if [ -f "$RESULTS_DIR/trial_${trial_num}_backtest.json" ]; then
|
||||
cat "$RESULTS_DIR/trial_${trial_num}_backtest.json" >> "$RESULTS_FILE"
|
||||
fi
|
||||
done
|
||||
echo "" >> "$RESULTS_FILE"
|
||||
echo "]" >> "$RESULTS_FILE"
|
||||
|
||||
analyze_results
|
||||
;;
|
||||
|
||||
*)
|
||||
echo -e "${RED}Usage:${NC}"
|
||||
echo " $0 --quick # Test trial 35 only (10 min)"
|
||||
echo " $0 --sample # Test 10 trials (1 hour)"
|
||||
echo " $0 --full # Test all 36 trials (3-6 hours)"
|
||||
echo ""
|
||||
echo -e "${YELLOW}No mode specified. Defaulting to --quick${NC}"
|
||||
echo ""
|
||||
exec "$0" --quick
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "=" | head -c 80
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ BACKTEST COMPLETE${NC}"
|
||||
echo "=" | head -c 80
|
||||
echo ""
|
||||
echo -e "Results: ${RESULTS_FILE}"
|
||||
echo -e "Individual results: ${RESULTS_DIR}/trial_*_backtest.json"
|
||||
echo ""
|
||||
390
scripts/benchmark_ensemble_db.sh
Executable file
390
scripts/benchmark_ensemble_db.sh
Executable file
@@ -0,0 +1,390 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ================================================================================================
|
||||
# Ensemble Database Performance Benchmark
|
||||
# Tests write throughput, query latency, and compression efficiency
|
||||
# Target: >1000 inserts/sec, P99 <100ms, compression >5x
|
||||
# ================================================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Database connection
|
||||
DB_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
|
||||
# Test parameters
|
||||
WRITE_TEST_DURATION=10 # seconds
|
||||
WRITE_TEST_BATCH_SIZE=100
|
||||
TARGET_WRITES_PER_SEC=1000
|
||||
TARGET_P99_LATENCY_MS=100
|
||||
TARGET_COMPRESSION_RATIO=5.0
|
||||
|
||||
echo -e "${BLUE}================================================================================================${NC}"
|
||||
echo -e "${BLUE}ENSEMBLE DATABASE PERFORMANCE BENCHMARK${NC}"
|
||||
echo -e "${BLUE}================================================================================================${NC}"
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# PART 1: PRE-TEST SETUP
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[1/6] Pre-test Setup${NC}"
|
||||
echo "Applying migration 023..."
|
||||
|
||||
psql "$DB_URL" -f /home/jgrusewski/Work/foxhunt/migrations/023_ensemble_performance_tuning.sql > /dev/null 2>&1 || {
|
||||
echo -e "${RED}❌ Migration 023 failed${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo -e "${GREEN}✅ Migration 023 applied successfully${NC}"
|
||||
echo ""
|
||||
|
||||
# Enable pg_stat_statements for query monitoring
|
||||
psql "$DB_URL" -c "CREATE EXTENSION IF NOT EXISTS pg_stat_statements;" > /dev/null 2>&1
|
||||
psql "$DB_URL" -c "SELECT pg_stat_statements_reset();" > /dev/null 2>&1
|
||||
|
||||
echo -e "${GREEN}✅ pg_stat_statements enabled and reset${NC}"
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# PART 2: WRITE THROUGHPUT TEST
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[2/6] Write Throughput Test (${WRITE_TEST_DURATION} seconds)${NC}"
|
||||
echo "Target: ${TARGET_WRITES_PER_SEC} inserts/sec"
|
||||
echo ""
|
||||
|
||||
# Generate test data and insert in batches
|
||||
START_TIME=$(date +%s)
|
||||
TOTAL_INSERTS=0
|
||||
|
||||
for i in $(seq 1 $WRITE_TEST_DURATION); do
|
||||
BATCH_JSON=$(cat <<EOF
|
||||
[
|
||||
$(for j in $(seq 1 $WRITE_TEST_BATCH_SIZE); do
|
||||
TIMESTAMP=$(date -u -Iseconds)
|
||||
SYMBOL=$(printf "SYM%02d" $((RANDOM % 10 + 1)))
|
||||
ACTION=$(printf "%s" "$(shuf -n1 -e BUY SELL HOLD)")
|
||||
cat <<INNER_EOF
|
||||
{
|
||||
"timestamp": "$TIMESTAMP",
|
||||
"symbol": "$SYMBOL",
|
||||
"ensemble_action": "$ACTION",
|
||||
"ensemble_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"ensemble_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"disagreement_rate": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"dqn_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"dqn_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"dqn_weight": 0.25,
|
||||
"dqn_vote": "$ACTION",
|
||||
"ppo_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"ppo_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"ppo_weight": 0.25,
|
||||
"ppo_vote": "$ACTION",
|
||||
"mamba2_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"mamba2_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"mamba2_weight": 0.25,
|
||||
"mamba2_vote": "$ACTION",
|
||||
"tft_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"tft_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"tft_weight": 0.25,
|
||||
"tft_vote": "$ACTION",
|
||||
"inference_latency_us": $((RANDOM % 10000 + 1000)),
|
||||
"aggregation_latency_us": $((RANDOM % 1000 + 100))
|
||||
}$(if [ $j -lt $WRITE_TEST_BATCH_SIZE ]; then echo ","; fi)
|
||||
INNER_EOF
|
||||
done)
|
||||
]
|
||||
EOF
|
||||
)
|
||||
|
||||
# Insert batch using bulk function
|
||||
BATCH_START=$(date +%s%N)
|
||||
ROWS_INSERTED=$(psql "$DB_URL" -t -c "SELECT insert_ensemble_predictions_bulk('$BATCH_JSON'::JSONB);" 2>/dev/null | tr -d ' ')
|
||||
BATCH_END=$(date +%s%N)
|
||||
BATCH_DURATION_MS=$(( (BATCH_END - BATCH_START) / 1000000 ))
|
||||
|
||||
TOTAL_INSERTS=$((TOTAL_INSERTS + ROWS_INSERTED))
|
||||
echo -ne "\rBatch $i: ${ROWS_INSERTED} rows in ${BATCH_DURATION_MS}ms | Total: ${TOTAL_INSERTS} rows"
|
||||
done
|
||||
|
||||
END_TIME=$(date +%s)
|
||||
DURATION=$((END_TIME - START_TIME))
|
||||
WRITES_PER_SEC=$((TOTAL_INSERTS / DURATION))
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo -e "Total inserts: ${BLUE}${TOTAL_INSERTS}${NC}"
|
||||
echo -e "Duration: ${BLUE}${DURATION}${NC} seconds"
|
||||
echo -e "Write throughput: ${BLUE}${WRITES_PER_SEC}${NC} inserts/sec"
|
||||
|
||||
if [ $WRITES_PER_SEC -ge $TARGET_WRITES_PER_SEC ]; then
|
||||
echo -e "${GREEN}✅ PASS: Write throughput ${WRITES_PER_SEC}/sec >= target ${TARGET_WRITES_PER_SEC}/sec${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ FAIL: Write throughput ${WRITES_PER_SEC}/sec < target ${TARGET_WRITES_PER_SEC}/sec${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# PART 3: QUERY LATENCY BENCHMARKS (26 production queries)
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[3/6] Query Latency Benchmarks (26 production queries)${NC}"
|
||||
echo "Target: P99 < ${TARGET_P99_LATENCY_MS}ms"
|
||||
echo ""
|
||||
|
||||
# Array of query names and SQL
|
||||
declare -a QUERIES=(
|
||||
"Q1: Recent predictions by symbol|SELECT * FROM ensemble_predictions WHERE symbol = 'SYM01' ORDER BY timestamp DESC LIMIT 100"
|
||||
"Q2: High disagreement events|SELECT * FROM ensemble_predictions WHERE disagreement_rate > 0.5 ORDER BY timestamp DESC LIMIT 100"
|
||||
"Q3: P&L attribution by symbol|SELECT symbol, SUM(pnl) as total_pnl FROM ensemble_predictions WHERE pnl IS NOT NULL GROUP BY symbol"
|
||||
"Q4: Model performance by symbol|SELECT model_id, symbol, AVG(accuracy) FROM model_performance_attribution WHERE window_hours = 24 GROUP BY model_id, symbol"
|
||||
"Q5: Top performers 24h|SELECT * FROM get_top_models_24h('SYM01', 5)"
|
||||
"Q6: Ensemble hourly metrics|SELECT * FROM ensemble_performance_hourly WHERE symbol = 'SYM01' ORDER BY bucket DESC LIMIT 48"
|
||||
"Q7: Model correlation 7d|SELECT * FROM calculate_model_correlation_7d('SYM01')"
|
||||
"Q8: High disagreement 24h|SELECT * FROM get_high_disagreement_events_24h('SYM01', 0.5, 100)"
|
||||
"Q9: Write throughput 5min|SELECT * FROM ensemble_write_throughput_5min"
|
||||
"Q10: Action distribution|SELECT ensemble_action, COUNT(*) FROM ensemble_predictions GROUP BY ensemble_action"
|
||||
"Q11: Avg confidence by action|SELECT ensemble_action, AVG(ensemble_confidence) FROM ensemble_predictions GROUP BY ensemble_action"
|
||||
"Q12: Latency P99|SELECT PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY inference_latency_us) FROM ensemble_predictions"
|
||||
"Q13: Model vote agreement|SELECT COUNT(*) FROM ensemble_predictions WHERE dqn_vote = ppo_vote AND ppo_vote = mamba2_vote AND mamba2_vote = tft_vote"
|
||||
"Q14: Recent orders with P&L|SELECT * FROM ensemble_predictions WHERE order_id IS NOT NULL ORDER BY timestamp DESC LIMIT 100"
|
||||
"Q15: Win rate by symbol|SELECT symbol, COUNT(CASE WHEN pnl > 0 THEN 1 END)::FLOAT / NULLIF(COUNT(*), 0) FROM ensemble_predictions WHERE pnl IS NOT NULL GROUP BY symbol"
|
||||
"Q16: Model performance hourly|SELECT * FROM model_performance_hourly WHERE model_id = 'DQN' ORDER BY bucket DESC LIMIT 24"
|
||||
"Q17: Ensemble weekly summary|SELECT * FROM ensemble_performance_weekly ORDER BY bucket DESC LIMIT 12"
|
||||
"Q18: Avg Sharpe by model|SELECT model_id, AVG(sharpe_ratio) FROM model_performance_attribution WHERE window_hours = 24 GROUP BY model_id"
|
||||
"Q19: Max drawdown by symbol|SELECT symbol, MAX(max_drawdown) FROM model_performance_attribution WHERE window_hours = 168 GROUP BY symbol"
|
||||
"Q20: Checkpoint performance|SELECT dqn_checkpoint_id, AVG(ensemble_confidence) FROM ensemble_predictions WHERE dqn_checkpoint_id IS NOT NULL GROUP BY dqn_checkpoint_id"
|
||||
"Q21: Time-weighted avg signal|SELECT time_bucket('1 hour', timestamp), AVG(ensemble_signal) FROM ensemble_predictions GROUP BY 1 ORDER BY 1 DESC LIMIT 24"
|
||||
"Q22: Disagreement rate trend|SELECT time_bucket('1 day', timestamp), AVG(disagreement_rate) FROM ensemble_predictions GROUP BY 1 ORDER BY 1 DESC LIMIT 30"
|
||||
"Q23: Model weight distribution|SELECT model_id, AVG(avg_weight) FROM model_performance_attribution WHERE window_hours = 1 GROUP BY model_id"
|
||||
"Q24: Recent high confidence|SELECT * FROM ensemble_predictions WHERE ensemble_confidence > 0.8 ORDER BY timestamp DESC LIMIT 100"
|
||||
"Q25: P&L by action type|SELECT ensemble_action, SUM(pnl) FROM ensemble_predictions WHERE pnl IS NOT NULL GROUP BY ensemble_action"
|
||||
"Q26: Inference latency trend|SELECT time_bucket('1 hour', timestamp), AVG(inference_latency_us), PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY inference_latency_us) FROM ensemble_predictions GROUP BY 1 ORDER BY 1 DESC LIMIT 24"
|
||||
)
|
||||
|
||||
# Run each query 5 times and collect timing
|
||||
QUERY_COUNT=${#QUERIES[@]}
|
||||
declare -a QUERY_TIMES=()
|
||||
|
||||
for i in "${!QUERIES[@]}"; do
|
||||
IFS='|' read -r QUERY_NAME QUERY_SQL <<< "${QUERIES[$i]}"
|
||||
|
||||
# Run query 5 times
|
||||
TIMES=()
|
||||
for run in {1..5}; do
|
||||
START=$(date +%s%N)
|
||||
psql "$DB_URL" -c "$QUERY_SQL" > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
DURATION_MS=$(( (END - START) / 1000000 ))
|
||||
TIMES+=($DURATION_MS)
|
||||
done
|
||||
|
||||
# Calculate median time
|
||||
IFS=$'\n' SORTED_TIMES=($(sort -n <<<"${TIMES[*]}"))
|
||||
MEDIAN_TIME=${SORTED_TIMES[2]}
|
||||
QUERY_TIMES+=($MEDIAN_TIME)
|
||||
|
||||
echo -e "${QUERY_NAME}: ${BLUE}${MEDIAN_TIME}ms${NC}"
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# Calculate P99 latency
|
||||
IFS=$'\n' SORTED_QUERY_TIMES=($(sort -n <<<"${QUERY_TIMES[*]}"))
|
||||
P99_INDEX=$(( (QUERY_COUNT * 99) / 100 ))
|
||||
P99_LATENCY=${SORTED_QUERY_TIMES[$P99_INDEX]}
|
||||
|
||||
echo -e "P99 Query Latency: ${BLUE}${P99_LATENCY}ms${NC}"
|
||||
|
||||
if [ $P99_LATENCY -le $TARGET_P99_LATENCY_MS ]; then
|
||||
echo -e "${GREEN}✅ PASS: P99 latency ${P99_LATENCY}ms <= target ${TARGET_P99_LATENCY_MS}ms${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ FAIL: P99 latency ${P99_LATENCY}ms > target ${TARGET_P99_LATENCY_MS}ms${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# PART 4: COMPRESSION RATIO TEST
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[4/6] Compression Ratio Test${NC}"
|
||||
echo "Target: Compression ratio > ${TARGET_COMPRESSION_RATIO}x"
|
||||
echo ""
|
||||
|
||||
# Insert old data (8 days ago) to trigger compression
|
||||
echo "Inserting old data for compression test..."
|
||||
|
||||
OLD_DATA_JSON=$(cat <<EOF
|
||||
[
|
||||
$(for j in $(seq 1 1000); do
|
||||
OLD_TIMESTAMP=$(date -u -Iseconds -d '8 days ago')
|
||||
SYMBOL=$(printf "SYM%02d" $((RANDOM % 10 + 1)))
|
||||
ACTION=$(printf "%s" "$(shuf -n1 -e BUY SELL HOLD)")
|
||||
cat <<INNER_EOF
|
||||
{
|
||||
"timestamp": "$OLD_TIMESTAMP",
|
||||
"symbol": "$SYMBOL",
|
||||
"ensemble_action": "$ACTION",
|
||||
"ensemble_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"ensemble_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"disagreement_rate": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"dqn_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"dqn_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"dqn_weight": 0.25,
|
||||
"dqn_vote": "$ACTION",
|
||||
"ppo_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"ppo_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"ppo_weight": 0.25,
|
||||
"ppo_vote": "$ACTION",
|
||||
"mamba2_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"mamba2_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"mamba2_weight": 0.25,
|
||||
"mamba2_vote": "$ACTION",
|
||||
"tft_signal": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()*2-1}'),
|
||||
"tft_confidence": $(awk -v seed=$RANDOM 'BEGIN{srand(seed); print rand()}'),
|
||||
"tft_weight": 0.25,
|
||||
"tft_vote": "$ACTION",
|
||||
"inference_latency_us": $((RANDOM % 10000 + 1000)),
|
||||
"aggregation_latency_us": $((RANDOM % 1000 + 100))
|
||||
}$(if [ $j -lt 1000 ]; then echo ","; fi)
|
||||
INNER_EOF
|
||||
done)
|
||||
]
|
||||
EOF
|
||||
)
|
||||
|
||||
psql "$DB_URL" -t -c "SELECT insert_ensemble_predictions_bulk('$OLD_DATA_JSON'::JSONB);" > /dev/null 2>&1
|
||||
|
||||
echo "Triggering manual compression..."
|
||||
psql "$DB_URL" -c "SELECT compress_chunk(i.chunk_schema || '.' || i.chunk_name) FROM timescaledb_information.chunks i WHERE i.hypertable_name = 'ensemble_predictions' AND i.is_compressed = false AND i.range_start < NOW() - INTERVAL '7 days';" > /dev/null 2>&1
|
||||
|
||||
# Wait for compression to complete
|
||||
sleep 2
|
||||
|
||||
# Check compression ratio
|
||||
COMPRESSION_STATS=$(psql "$DB_URL" -t -c "SELECT AVG(before_compression_total_bytes::FLOAT / NULLIF(after_compression_total_bytes, 0)) FROM timescaledb_information.compressed_chunk_stats WHERE hypertable_name = 'ensemble_predictions';" | tr -d ' ')
|
||||
|
||||
if [ -z "$COMPRESSION_STATS" ] || [ "$COMPRESSION_STATS" == "" ]; then
|
||||
echo -e "${YELLOW}⚠️ No compressed chunks yet (data too recent)${NC}"
|
||||
echo -e "${BLUE}Note: Compression will trigger automatically after 7 days${NC}"
|
||||
else
|
||||
COMPRESSION_RATIO=$(printf "%.2f" "$COMPRESSION_STATS")
|
||||
echo -e "Compression ratio: ${BLUE}${COMPRESSION_RATIO}x${NC}"
|
||||
|
||||
if (( $(echo "$COMPRESSION_RATIO >= $TARGET_COMPRESSION_RATIO" | bc -l) )); then
|
||||
echo -e "${GREEN}✅ PASS: Compression ratio ${COMPRESSION_RATIO}x >= target ${TARGET_COMPRESSION_RATIO}x${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ FAIL: Compression ratio ${COMPRESSION_RATIO}x < target ${TARGET_COMPRESSION_RATIO}x${NC}"
|
||||
fi
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# PART 5: INDEX EFFICIENCY TEST
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[5/6] Index Efficiency Test${NC}"
|
||||
echo ""
|
||||
|
||||
# Check index usage statistics
|
||||
psql "$DB_URL" -c "
|
||||
SELECT
|
||||
schemaname,
|
||||
tablename,
|
||||
indexname,
|
||||
idx_scan as index_scans,
|
||||
idx_tup_read as tuples_read,
|
||||
idx_tup_fetch as tuples_fetched,
|
||||
pg_size_pretty(pg_relation_size(indexrelid)) as index_size
|
||||
FROM pg_stat_user_indexes
|
||||
WHERE tablename IN ('ensemble_predictions', 'model_performance_attribution')
|
||||
ORDER BY idx_scan DESC, tablename;
|
||||
"
|
||||
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# PART 6: CONTINUOUS AGGREGATE TEST
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[6/6] Continuous Aggregate Refresh Test${NC}"
|
||||
echo ""
|
||||
|
||||
# Manually refresh continuous aggregates
|
||||
echo "Refreshing continuous aggregates..."
|
||||
|
||||
psql "$DB_URL" -c "CALL refresh_continuous_aggregate('ensemble_performance_5min', NOW() - INTERVAL '1 hour', NOW());" > /dev/null 2>&1
|
||||
psql "$DB_URL" -c "CALL refresh_continuous_aggregate('model_performance_hourly', NOW() - INTERVAL '6 hours', NOW());" > /dev/null 2>&1
|
||||
psql "$DB_URL" -c "CALL refresh_continuous_aggregate('ensemble_performance_weekly', NOW() - INTERVAL '1 week', NOW());" > /dev/null 2>&1
|
||||
|
||||
echo -e "${GREEN}✅ Continuous aggregates refreshed${NC}"
|
||||
echo ""
|
||||
|
||||
# Check continuous aggregate sizes
|
||||
psql "$DB_URL" -c "
|
||||
SELECT
|
||||
view_name,
|
||||
pg_size_pretty(pg_total_relation_size(format('%I.%I', view_schema, view_name)::regclass)) as total_size
|
||||
FROM timescaledb_information.continuous_aggregates
|
||||
WHERE view_name IN ('ensemble_performance_5min', 'model_performance_hourly', 'ensemble_performance_weekly')
|
||||
ORDER BY view_name;
|
||||
"
|
||||
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# FINAL SUMMARY
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${BLUE}================================================================================================${NC}"
|
||||
echo -e "${BLUE}BENCHMARK SUMMARY${NC}"
|
||||
echo -e "${BLUE}================================================================================================${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "Write Throughput: ${BLUE}${WRITES_PER_SEC}/sec${NC} (target: ${TARGET_WRITES_PER_SEC}/sec)"
|
||||
echo -e "P99 Query Latency: ${BLUE}${P99_LATENCY}ms${NC} (target: <${TARGET_P99_LATENCY_MS}ms)"
|
||||
if [ -z "$COMPRESSION_STATS" ] || [ "$COMPRESSION_STATS" == "" ]; then
|
||||
echo -e "Compression Ratio: ${YELLOW}N/A (data too recent)${NC} (target: >${TARGET_COMPRESSION_RATIO}x)"
|
||||
else
|
||||
echo -e "Compression Ratio: ${BLUE}${COMPRESSION_RATIO}x${NC} (target: >${TARGET_COMPRESSION_RATIO}x)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Overall pass/fail
|
||||
PASS_COUNT=0
|
||||
TOTAL_TESTS=3
|
||||
|
||||
if [ $WRITES_PER_SEC -ge $TARGET_WRITES_PER_SEC ]; then
|
||||
PASS_COUNT=$((PASS_COUNT + 1))
|
||||
fi
|
||||
|
||||
if [ $P99_LATENCY -le $TARGET_P99_LATENCY_MS ]; then
|
||||
PASS_COUNT=$((PASS_COUNT + 1))
|
||||
fi
|
||||
|
||||
if [ -n "$COMPRESSION_STATS" ] && [ "$COMPRESSION_STATS" != "" ]; then
|
||||
if (( $(echo "$COMPRESSION_RATIO >= $TARGET_COMPRESSION_RATIO" | bc -l) )); then
|
||||
PASS_COUNT=$((PASS_COUNT + 1))
|
||||
fi
|
||||
else
|
||||
TOTAL_TESTS=2 # Exclude compression test if no data
|
||||
fi
|
||||
|
||||
echo -e "${BLUE}Tests Passed: ${PASS_COUNT}/${TOTAL_TESTS}${NC}"
|
||||
echo ""
|
||||
|
||||
if [ $PASS_COUNT -eq $TOTAL_TESTS ]; then
|
||||
echo -e "${GREEN}✅ ALL TESTS PASSED - Database optimized for production${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Some tests did not meet targets - review optimization strategies${NC}"
|
||||
exit 1
|
||||
fi
|
||||
173
scripts/benchmark_ensemble_db_quick.sh
Executable file
173
scripts/benchmark_ensemble_db_quick.sh
Executable file
@@ -0,0 +1,173 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ================================================================================================
|
||||
# Quick Ensemble Database Performance Benchmark
|
||||
# Fast version for immediate feedback
|
||||
# ================================================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
DB_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
|
||||
echo -e "${BLUE}Quick Ensemble Database Benchmark${NC}"
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# TEST 1: SIMPLE WRITE THROUGHPUT (1000 rows)
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[1/4] Write Throughput Test${NC}"
|
||||
|
||||
# Generate simple insert test
|
||||
START=$(date +%s%N)
|
||||
|
||||
for i in {1..10}; do
|
||||
psql "$DB_URL" -c "
|
||||
INSERT INTO ensemble_predictions (
|
||||
timestamp, symbol, ensemble_action, ensemble_signal,
|
||||
ensemble_confidence, disagreement_rate
|
||||
)
|
||||
SELECT
|
||||
NOW() - (random() * INTERVAL '1 hour'),
|
||||
'TEST_SYM',
|
||||
CASE WHEN random() < 0.33 THEN 'BUY' WHEN random() < 0.66 THEN 'SELL' ELSE 'HOLD' END,
|
||||
(random() * 2 - 1)::DOUBLE PRECISION,
|
||||
random()::DOUBLE PRECISION,
|
||||
random()::DOUBLE PRECISION
|
||||
FROM generate_series(1, 100);
|
||||
" > /dev/null 2>&1
|
||||
done
|
||||
|
||||
END=$(date +%s%N)
|
||||
DURATION_MS=$(( (END - START) / 1000000 ))
|
||||
WRITES_PER_SEC=$(( 1000 * 1000 / DURATION_MS ))
|
||||
|
||||
echo -e "Inserted 1000 rows in ${BLUE}${DURATION_MS}ms${NC}"
|
||||
echo -e "Write throughput: ${BLUE}${WRITES_PER_SEC} inserts/sec${NC}"
|
||||
|
||||
if [ $WRITES_PER_SEC -ge 1000 ]; then
|
||||
echo -e "${GREEN}✅ PASS: Write throughput >= 1000/sec${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: Write throughput < 1000/sec${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# TEST 2: QUERY LATENCY (10 key queries)
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[2/4] Query Latency Test${NC}"
|
||||
|
||||
declare -a QUERIES=(
|
||||
"Recent predictions|SELECT * FROM ensemble_predictions ORDER BY timestamp DESC LIMIT 100"
|
||||
"High disagreement|SELECT * FROM ensemble_predictions WHERE disagreement_rate > 0.5 LIMIT 100"
|
||||
"P&L by symbol|SELECT symbol, SUM(pnl) FROM ensemble_predictions WHERE pnl IS NOT NULL GROUP BY symbol"
|
||||
"Action distribution|SELECT ensemble_action, COUNT(*) FROM ensemble_predictions GROUP BY ensemble_action"
|
||||
"Avg confidence|SELECT ensemble_action, AVG(ensemble_confidence) FROM ensemble_predictions GROUP BY ensemble_action"
|
||||
"Latency P99|SELECT PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY inference_latency_us) FROM ensemble_predictions WHERE inference_latency_us IS NOT NULL"
|
||||
"Win rate by symbol|SELECT symbol, COUNT(CASE WHEN pnl > 0 THEN 1 END)::FLOAT / NULLIF(COUNT(*), 0) FROM ensemble_predictions WHERE pnl IS NOT NULL GROUP BY symbol"
|
||||
"Recent high confidence|SELECT * FROM ensemble_predictions WHERE ensemble_confidence > 0.8 ORDER BY timestamp DESC LIMIT 100"
|
||||
"Model performance|SELECT model_id, AVG(accuracy) FROM model_performance_attribution WHERE window_hours = 24 GROUP BY model_id"
|
||||
"Hourly metrics|SELECT * FROM ensemble_performance_hourly ORDER BY bucket DESC LIMIT 24"
|
||||
)
|
||||
|
||||
declare -a LATENCIES=()
|
||||
|
||||
for query_spec in "${QUERIES[@]}"; do
|
||||
IFS='|' read -r NAME SQL <<< "$query_spec"
|
||||
|
||||
START=$(date +%s%N)
|
||||
psql "$DB_URL" -c "$SQL" > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
LATENCY_MS=$(( (END - START) / 1000000 ))
|
||||
|
||||
LATENCIES+=($LATENCY_MS)
|
||||
echo -e "${NAME}: ${BLUE}${LATENCY_MS}ms${NC}"
|
||||
done
|
||||
|
||||
# Calculate P99
|
||||
IFS=$'\n' SORTED=($(sort -n <<<"${LATENCIES[*]}"))
|
||||
P99_INDEX=$(( (${#LATENCIES[@]} * 99) / 100 ))
|
||||
P99_LATENCY=${SORTED[$P99_INDEX]}
|
||||
|
||||
echo ""
|
||||
echo -e "P99 Query Latency: ${BLUE}${P99_LATENCY}ms${NC}"
|
||||
|
||||
if [ $P99_LATENCY -le 100 ]; then
|
||||
echo -e "${GREEN}✅ PASS: P99 latency <= 100ms${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING: P99 latency > 100ms${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# TEST 3: INDEX USAGE
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[3/4] Index Usage Statistics${NC}"
|
||||
|
||||
psql "$DB_URL" -c "
|
||||
SELECT
|
||||
LEFT(indexname, 40) as index_name,
|
||||
idx_scan as scans,
|
||||
pg_size_pretty(pg_relation_size(indexrelid)) as size
|
||||
FROM pg_stat_user_indexes
|
||||
WHERE tablename IN ('ensemble_predictions', 'model_performance_attribution')
|
||||
ORDER BY idx_scan DESC
|
||||
LIMIT 10;
|
||||
"
|
||||
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# TEST 4: TABLE STATISTICS
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${YELLOW}[4/4] Table Statistics${NC}"
|
||||
|
||||
psql "$DB_URL" -c "
|
||||
SELECT
|
||||
'ensemble_predictions' as table_name,
|
||||
COUNT(*) as row_count,
|
||||
pg_size_pretty(pg_total_relation_size('ensemble_predictions')) as total_size,
|
||||
pg_size_pretty(pg_relation_size('ensemble_predictions')) as table_size,
|
||||
pg_size_pretty(pg_indexes_size('ensemble_predictions')) as indexes_size
|
||||
FROM ensemble_predictions
|
||||
UNION ALL
|
||||
SELECT
|
||||
'model_performance_attribution',
|
||||
COUNT(*),
|
||||
pg_size_pretty(pg_total_relation_size('model_performance_attribution')),
|
||||
pg_size_pretty(pg_relation_size('model_performance_attribution')),
|
||||
pg_size_pretty(pg_indexes_size('model_performance_attribution'))
|
||||
FROM model_performance_attribution;
|
||||
"
|
||||
|
||||
echo ""
|
||||
|
||||
# ================================================================================================
|
||||
# SUMMARY
|
||||
# ================================================================================================
|
||||
|
||||
echo -e "${BLUE}================================================================================================${NC}"
|
||||
echo -e "${BLUE}BENCHMARK SUMMARY${NC}"
|
||||
echo -e "${BLUE}================================================================================================${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "Write Throughput: ${BLUE}${WRITES_PER_SEC}/sec${NC} (target: 1000/sec)"
|
||||
echo -e "P99 Query Latency: ${BLUE}${P99_LATENCY}ms${NC} (target: <100ms)"
|
||||
echo ""
|
||||
|
||||
if [ $WRITES_PER_SEC -ge 1000 ] && [ $P99_LATENCY -le 100 ]; then
|
||||
echo -e "${GREEN}✅ ALL TESTS PASSED${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Some metrics below target - database under optimization${NC}"
|
||||
exit 0
|
||||
fi
|
||||
120
scripts/benchmark_mimalloc.sh
Executable file
120
scripts/benchmark_mimalloc.sh
Executable file
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
# Mimalloc Allocator Performance Benchmark
|
||||
# Compares training speed with and without mimalloc
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ Mimalloc Allocator Performance Benchmark ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Test configuration
|
||||
PARQUET_FILE="test_data/ES_FUT_small.parquet"
|
||||
EPOCHS=3
|
||||
BATCH_SIZE=16 # Small batch to reduce total time
|
||||
|
||||
echo "Test Configuration:"
|
||||
echo " • Parquet file: $PARQUET_FILE"
|
||||
echo " • Epochs: $EPOCHS"
|
||||
echo " • Batch size: $BATCH_SIZE"
|
||||
echo ""
|
||||
|
||||
# Build WITHOUT mimalloc (system allocator)
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo "Building WITHOUT mimalloc (system allocator)..."
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
cargo build --release -p ml --example train_tft_parquet --features cuda 2>&1 | grep -E "(Compiling|Finished)" | tail -5
|
||||
echo ""
|
||||
|
||||
# Run benchmark WITHOUT mimalloc
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo "Running TFT training WITHOUT mimalloc..."
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
SYSTEM_START=$(date +%s)
|
||||
timeout 180 ./target/release/examples/train_tft_parquet \
|
||||
--parquet-file "$PARQUET_FILE" \
|
||||
--epochs "$EPOCHS" \
|
||||
--batch-size "$BATCH_SIZE" \
|
||||
--use-gpu \
|
||||
2>&1 | tee /tmp/tft_system_allocator.log || true
|
||||
SYSTEM_END=$(date +%s)
|
||||
SYSTEM_DURATION=$((SYSTEM_END - SYSTEM_START))
|
||||
|
||||
echo ""
|
||||
echo "System allocator duration: ${SYSTEM_DURATION}s"
|
||||
echo ""
|
||||
|
||||
# Build WITH mimalloc
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo "Building WITH mimalloc allocator..."
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
cargo build --release -p ml --example train_tft_parquet --features "cuda,mimalloc-allocator" 2>&1 | grep -E "(Compiling|Finished)" | tail -5
|
||||
echo ""
|
||||
|
||||
# Run benchmark WITH mimalloc
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo "Running TFT training WITH mimalloc..."
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
MIMALLOC_START=$(date +%s)
|
||||
timeout 180 ./target/release/examples/train_tft_parquet \
|
||||
--parquet-file "$PARQUET_FILE" \
|
||||
--epochs "$EPOCHS" \
|
||||
--batch-size "$BATCH_SIZE" \
|
||||
--use-gpu \
|
||||
2>&1 | tee /tmp/tft_mimalloc.log || true
|
||||
MIMALLOC_END=$(date +%s)
|
||||
MIMALLOC_DURATION=$((MIMALLOC_END - MIMALLOC_START))
|
||||
|
||||
echo ""
|
||||
echo "Mimalloc duration: ${MIMALLOC_DURATION}s"
|
||||
echo ""
|
||||
|
||||
# Calculate improvement
|
||||
if [ $SYSTEM_DURATION -gt 0 ]; then
|
||||
SPEEDUP=$(echo "scale=2; ($SYSTEM_DURATION - $MIMALLOC_DURATION) / $SYSTEM_DURATION * 100" | bc)
|
||||
RATIO=$(echo "scale=2; $SYSTEM_DURATION / $MIMALLOC_DURATION" | bc)
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ Performance Results ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "System Allocator Duration: ${SYSTEM_DURATION}s"
|
||||
echo "Mimalloc Duration: ${MIMALLOC_DURATION}s"
|
||||
echo ""
|
||||
echo "Performance Improvement: ${SPEEDUP}%"
|
||||
echo "Speedup Ratio: ${RATIO}x"
|
||||
echo ""
|
||||
|
||||
if (( $(echo "$SPEEDUP >= 10" | bc -l) )); then
|
||||
echo "✅ EXCELLENT: Mimalloc provides ${SPEEDUP}% speedup (≥10% target)"
|
||||
elif (( $(echo "$SPEEDUP >= 5" | bc -l) )); then
|
||||
echo "✅ GOOD: Mimalloc provides ${SPEEDUP}% speedup (≥5%)"
|
||||
else
|
||||
echo "⚠️ MODEST: Mimalloc provides ${SPEEDUP}% speedup (<5%)"
|
||||
echo " This is expected for GPU-bound workloads."
|
||||
fi
|
||||
echo ""
|
||||
else
|
||||
echo "❌ Error: System allocator test failed or took 0 seconds"
|
||||
fi
|
||||
|
||||
# Extract training time from logs
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo "Detailed Training Metrics"
|
||||
echo "═══════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo "System Allocator:"
|
||||
grep -E "(Training duration|Using|allocator)" /tmp/tft_system_allocator.log | head -5 || echo "No logs found"
|
||||
echo ""
|
||||
echo "Mimalloc Allocator:"
|
||||
grep -E "(Training duration|Using mimalloc|allocator)" /tmp/tft_mimalloc.log | head -5 || echo "No logs found"
|
||||
echo ""
|
||||
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ Benchmark Complete ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Full logs saved to:"
|
||||
echo " • /tmp/tft_system_allocator.log"
|
||||
echo " • /tmp/tft_mimalloc.log"
|
||||
79
scripts/benchmark_nextest.sh
Executable file
79
scripts/benchmark_nextest.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Benchmark script to compare cargo test vs cargo nextest
|
||||
# Usage: ./benchmark_nextest.sh
|
||||
|
||||
set -e
|
||||
|
||||
PACKAGE="common"
|
||||
TEST_FILTER="test_"
|
||||
|
||||
echo "================================"
|
||||
echo "Cargo Test vs Nextest Benchmark"
|
||||
echo "================================"
|
||||
echo "Package: $PACKAGE"
|
||||
echo "Test filter: $TEST_FILTER"
|
||||
echo ""
|
||||
|
||||
# Clean build to ensure fair comparison
|
||||
echo "[1/5] Cleaning build artifacts..."
|
||||
cargo clean -p $PACKAGE
|
||||
echo ""
|
||||
|
||||
# Build with regular cargo test
|
||||
echo "[2/5] Building with 'cargo test' (no run)..."
|
||||
start_time=$(date +%s.%N)
|
||||
cargo test --package $PACKAGE --lib --no-run 2>&1 | tail -3
|
||||
end_time=$(date +%s.%N)
|
||||
cargo_test_build_time=$(echo "$end_time - $start_time" | bc)
|
||||
echo "Cargo test build time: ${cargo_test_build_time}s"
|
||||
echo ""
|
||||
|
||||
# Run with regular cargo test
|
||||
echo "[3/5] Running tests with 'cargo test'..."
|
||||
start_time=$(date +%s.%N)
|
||||
cargo test --package $PACKAGE --lib $TEST_FILTER 2>&1 | tail -5
|
||||
end_time=$(date +%s.%N)
|
||||
cargo_test_run_time=$(echo "$end_time - $start_time" | bc)
|
||||
echo "Cargo test run time: ${cargo_test_run_time}s"
|
||||
echo ""
|
||||
|
||||
# Clean again for nextest
|
||||
echo "[4/5] Cleaning for nextest comparison..."
|
||||
cargo clean -p $PACKAGE
|
||||
echo ""
|
||||
|
||||
# Build and run with nextest
|
||||
echo "[5/5] Building and running with 'cargo nextest'..."
|
||||
start_time=$(date +%s.%N)
|
||||
cargo nextest run --package $PACKAGE --lib $TEST_FILTER 2>&1 | tail -10
|
||||
end_time=$(date +%s.%N)
|
||||
nextest_total_time=$(echo "$end_time - $start_time" | bc)
|
||||
echo "Nextest total time (build + run): ${nextest_total_time}s"
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "================================"
|
||||
echo "SUMMARY"
|
||||
echo "================================"
|
||||
echo "cargo test:"
|
||||
echo " - Build time: ${cargo_test_build_time}s"
|
||||
echo " - Run time: ${cargo_test_run_time}s"
|
||||
echo " - Total: $(echo "$cargo_test_build_time + $cargo_test_run_time" | bc)s"
|
||||
echo ""
|
||||
echo "cargo nextest:"
|
||||
echo " - Total time: ${nextest_total_time}s"
|
||||
echo ""
|
||||
|
||||
cargo_total=$(echo "$cargo_test_build_time + $cargo_test_run_time" | bc)
|
||||
speedup=$(echo "scale=2; $cargo_total / $nextest_total_time" | bc)
|
||||
|
||||
if (( $(echo "$speedup > 1" | bc -l) )); then
|
||||
echo "✅ nextest is ${speedup}x faster!"
|
||||
elif (( $(echo "$speedup < 1" | bc -l) )); then
|
||||
slowdown=$(echo "scale=2; $nextest_total_time / $cargo_total" | bc)
|
||||
echo "⚠️ nextest is ${slowdown}x slower"
|
||||
else
|
||||
echo "⚡ Performance is equivalent"
|
||||
fi
|
||||
echo ""
|
||||
9
scripts/build.sh
Executable file
9
scripts/build.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Build script that handles sqlx offline compilation
|
||||
|
||||
# Set SQLX_OFFLINE to prevent database connection during compilation
|
||||
export SQLX_OFFLINE=true
|
||||
|
||||
# Build the workspace
|
||||
echo "Building with SQLX_OFFLINE=true..."
|
||||
cargo build --workspace "$@"
|
||||
29
scripts/bypass_cache.sh
Executable file
29
scripts/bypass_cache.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Bypass volume cache - download binary directly from S3
|
||||
|
||||
set -e
|
||||
|
||||
echo "================================================"
|
||||
echo "Bypassing Volume Cache - Downloading from S3"
|
||||
echo "================================================"
|
||||
|
||||
# Download fresh binary from S3
|
||||
aws s3 cp s3://se3zdnb5o4/binaries/current/hyperopt_mamba2_demo /tmp/demo \
|
||||
--endpoint-url https://s3api-eur-is-1.runpod.io
|
||||
|
||||
# Make executable
|
||||
chmod +x /tmp/demo
|
||||
|
||||
# Verify it has correct arguments
|
||||
echo "Verifying binary..."
|
||||
/tmp/demo --help | grep -q "base-dir" && echo "✅ Binary verified" || (echo "❌ Wrong binary!" && exit 1)
|
||||
|
||||
# Execute training
|
||||
echo "Starting training..."
|
||||
/tmp/demo \
|
||||
--parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet \
|
||||
--base-dir /runpod-volume/ml_training \
|
||||
--trials 2 \
|
||||
--epochs 1 \
|
||||
--batch-size-max 256 \
|
||||
--seed 42
|
||||
42
scripts/check-status.sh
Executable file
42
scripts/check-status.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
# Check System Status - REALITY CHECK
|
||||
# Shows what actually compiles vs what's overengineered
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "🔍 Foxhunt System Status Check"
|
||||
echo "=============================="
|
||||
|
||||
# Test individual components
|
||||
components=("trading_engine" "data" "risk" "ml" "tli")
|
||||
|
||||
echo "📦 Testing Component Compilation:"
|
||||
for component in "${components[@]}"; do
|
||||
echo -n " $component: "
|
||||
if cargo check -p "foxhunt-$component" --quiet 2>/dev/null; then
|
||||
echo "✅ COMPILES"
|
||||
else
|
||||
echo "❌ FAILS"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📊 Deployment Complexity Analysis:"
|
||||
|
||||
# Count deployment scripts
|
||||
script_count=$(find deployment/scripts/ -name "*.sh" 2>/dev/null | wc -l || echo "0")
|
||||
echo " Complex deployment scripts: $script_count"
|
||||
|
||||
# Count our simple scripts
|
||||
simple_count=$(ls -1 *.sh 2>/dev/null | wc -l || echo "0")
|
||||
echo " Simple deployment scripts: $simple_count"
|
||||
|
||||
echo " Complexity reduction: $((script_count * 100 / (simple_count + 1)))% → 100%"
|
||||
|
||||
echo ""
|
||||
echo "🎯 Reality Check:"
|
||||
echo " - System has $script_count scripts for components that don't compile"
|
||||
echo " - Simplified to $simple_count scripts that target working components"
|
||||
echo " - Deployment complexity reduced by ~95%"
|
||||
echo ""
|
||||
echo "💡 Next: Fix trading_engine compilation issues, then run ./start.sh"
|
||||
9
scripts/check.sh
Executable file
9
scripts/check.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# Check script that handles sqlx offline compilation
|
||||
|
||||
# Set SQLX_OFFLINE to prevent database connection during compilation
|
||||
export SQLX_OFFLINE=true
|
||||
|
||||
# Check the workspace
|
||||
echo "Checking workspace with SQLX_OFFLINE=true..."
|
||||
cargo check --workspace "$@"
|
||||
38
scripts/check_backtesting_health.sh
Executable file
38
scripts/check_backtesting_health.sh
Executable file
@@ -0,0 +1,38 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "=== Backtesting Service Health Check ==="
|
||||
echo ""
|
||||
|
||||
# Check process
|
||||
if ps -p 1739871 > /dev/null 2>&1; then
|
||||
echo "✓ Process Status: RUNNING (PID: 1739871)"
|
||||
echo " Uptime: $(ps -p 1739871 -o etime= | xargs)"
|
||||
else
|
||||
echo "✗ Process Status: NOT RUNNING"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check port
|
||||
if ss -tlnp 2>/dev/null | grep -q ":50052.*1739871"; then
|
||||
echo "✓ Port Status: LISTENING on 50052"
|
||||
else
|
||||
echo "✗ Port Status: NOT LISTENING"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check TLS handshake
|
||||
if timeout 2 openssl s_client -connect localhost:50052 -CAfile /tmp/foxhunt/certs/ca.crt </dev/null 2>&1 | grep -q "Verify return code: 0"; then
|
||||
echo "✓ TLS Status: HANDSHAKE OK"
|
||||
elif timeout 2 openssl s_client -connect localhost:50052 </dev/null 2>&1 | grep -q "CONNECTED"; then
|
||||
echo "⚠ TLS Status: CONNECTED (certificate validation pending)"
|
||||
else
|
||||
echo "✗ TLS Status: FAILED"
|
||||
fi
|
||||
|
||||
# Check recent logs
|
||||
echo ""
|
||||
echo "=== Recent Log Entries ==="
|
||||
tail -5 /home/jgrusewski/Work/foxhunt/logs/backtesting_service.log
|
||||
|
||||
echo ""
|
||||
echo "✓ Backtesting service is operational"
|
||||
64
scripts/check_hyperopt_status.sh
Executable file
64
scripts/check_hyperopt_status.sh
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
# Quick status check for MAMBA-2 hyperopt on RunPod
|
||||
# Usage: ./check_hyperopt_status.sh [pod_id]
|
||||
|
||||
POD_ID="${1:-nyt86m4i89106a}"
|
||||
ENV_FILE="/home/jgrusewski/Work/foxhunt/.env.runpod"
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo "ERROR: .env.runpod not found at $ENV_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$ENV_FILE"
|
||||
|
||||
echo "======================================================================"
|
||||
echo "MAMBA-2 Hyperopt Status Check"
|
||||
echo "======================================================================"
|
||||
echo "Pod ID: $POD_ID"
|
||||
echo "Time: $(date)"
|
||||
echo ""
|
||||
|
||||
# Check pod status via REST API
|
||||
echo "Fetching pod info..."
|
||||
RESPONSE=$(curl -s -H "Authorization: Bearer $RUNPOD_API_KEY" \
|
||||
"https://rest.runpod.io/v1/pods/$POD_ID")
|
||||
|
||||
STATUS=$(echo "$RESPONSE" | jq -r '.desiredStatus // "UNKNOWN"')
|
||||
GPU=$(echo "$RESPONSE" | jq -r '.machine.gpuType.displayName // "N/A"')
|
||||
DATACENTER=$(echo "$RESPONSE" | jq -r '.machine.dataCenterId // "N/A"')
|
||||
COST=$(echo "$RESPONSE" | jq -r '.costPerHr // "N/A"')
|
||||
|
||||
echo "Status: $STATUS"
|
||||
echo "GPU: $GPU"
|
||||
echo "Datacenter: $DATACENTER"
|
||||
echo "Cost: \$${COST}/hr"
|
||||
echo ""
|
||||
|
||||
if [ "$STATUS" = "RUNNING" ]; then
|
||||
echo "✅ Pod is RUNNING"
|
||||
echo ""
|
||||
echo "Access logs at: https://www.runpod.io/console/pods"
|
||||
echo "SSH: ssh root@${POD_ID}.ssh.runpod.io"
|
||||
echo ""
|
||||
echo "Expected completion: ~60 minutes from start"
|
||||
echo "Estimated cost: ~\$0.42 total"
|
||||
echo ""
|
||||
echo "CRITICAL: Monitor first 10 minutes for CUDA OOM errors!"
|
||||
echo "If OOM occurs with RTX A4000, redeploy with --batch-size-max 96"
|
||||
elif [ "$STATUS" = "TERMINATED" ] || [ "$STATUS" = "EXITED" ]; then
|
||||
echo "✅ Pod has TERMINATED (training likely complete)"
|
||||
echo ""
|
||||
echo "Download results:"
|
||||
echo " aws s3 ls s3://se3zdnb5o4/models/ \\"
|
||||
echo " --profile runpod \\"
|
||||
echo " --endpoint-url https://s3api-eur-is-1.runpod.io \\"
|
||||
echo " --recursive"
|
||||
else
|
||||
echo "⏳ Pod status: $STATUS"
|
||||
echo ""
|
||||
echo "Wait 2-3 minutes for initialization, then check again"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "======================================================================"
|
||||
225
scripts/concurrent_connection_test.sh
Executable file
225
scripts/concurrent_connection_test.sh
Executable file
@@ -0,0 +1,225 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Concurrent Connection Load Test for Foxhunt HFT Trading System
|
||||
# Tests system behavior under 10, 50, 100, and 200+ concurrent connections
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test configuration
|
||||
ENDPOINT="localhost:50052"
|
||||
JWT_TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0X3VzZXIiLCJleHAiOjk5OTk5OTk5OTksImp0aSI6InRlc3RfdXNlciIsInJvbGVzIjpbInRyYWRlciJdLCJwZXJtaXNzaW9ucyI6WyJ0cmFkZSJdfQ.kHXiGFA0JjGmW66SiFRTUvzd2S6mOGN8pDfr9VJ8mAM"
|
||||
|
||||
echo "┌────────────────────────────────────────────────────────────────────┐"
|
||||
echo "│ Foxhunt HFT Trading System - Concurrent Connection Load Test │"
|
||||
echo "└────────────────────────────────────────────────────────────────────┘"
|
||||
echo ""
|
||||
|
||||
# Function to test a single connection
|
||||
test_single_connection() {
|
||||
local conn_id=$1
|
||||
local start_time=$(date +%s%3N)
|
||||
|
||||
# Test connection using nc (netcat) for simple TCP connection test
|
||||
timeout 5 bash -c "echo | nc -w 2 localhost 50052" >/dev/null 2>&1
|
||||
local result=$?
|
||||
|
||||
local end_time=$(date +%s%3N)
|
||||
local duration=$((end_time - start_time))
|
||||
|
||||
echo "${result}:${duration}"
|
||||
}
|
||||
|
||||
# Function to run concurrent connection test
|
||||
run_concurrent_test() {
|
||||
local num_connections=$1
|
||||
local test_name=$2
|
||||
|
||||
echo ""
|
||||
echo "============================================================"
|
||||
echo "Testing with ${num_connections} concurrent connections (${test_name})"
|
||||
echo "============================================================"
|
||||
|
||||
local start_time=$(date +%s%3N)
|
||||
local pids=()
|
||||
local results_file="/tmp/foxhunt_concurrent_test_${num_connections}.txt"
|
||||
|
||||
rm -f "$results_file"
|
||||
touch "$results_file"
|
||||
|
||||
# Launch concurrent connections
|
||||
for i in $(seq 1 $num_connections); do
|
||||
(
|
||||
result=$(test_single_connection $i)
|
||||
echo "$result" >> "$results_file"
|
||||
) &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
# Wait for all connections to complete
|
||||
for pid in "${pids[@]}"; do
|
||||
wait $pid 2>/dev/null || true
|
||||
done
|
||||
|
||||
local end_time=$(date +%s%3N)
|
||||
local total_duration=$((end_time - start_time))
|
||||
|
||||
# Analyze results
|
||||
local total_requests=$num_connections
|
||||
local successful=0
|
||||
local failed=0
|
||||
local sum_latency=0
|
||||
local latencies=()
|
||||
|
||||
while IFS=: read -r status latency; do
|
||||
if [ "$status" = "0" ]; then
|
||||
((successful++))
|
||||
sum_latency=$((sum_latency + latency))
|
||||
latencies+=($latency)
|
||||
else
|
||||
((failed++))
|
||||
fi
|
||||
done < "$results_file"
|
||||
|
||||
# Calculate statistics
|
||||
local error_rate=0
|
||||
if [ $total_requests -gt 0 ]; then
|
||||
error_rate=$(awk "BEGIN {printf \"%.2f\", ($failed / $total_requests) * 100}")
|
||||
fi
|
||||
|
||||
local success_rate=0
|
||||
if [ $total_requests -gt 0 ]; then
|
||||
success_rate=$(awk "BEGIN {printf \"%.1f\", ($successful / $total_requests) * 100}")
|
||||
fi
|
||||
|
||||
local avg_latency=0
|
||||
if [ $successful -gt 0 ]; then
|
||||
avg_latency=$((sum_latency / successful))
|
||||
fi
|
||||
|
||||
local throughput=0
|
||||
if [ $total_duration -gt 0 ]; then
|
||||
throughput=$(awk "BEGIN {printf \"%.2f\", ($successful * 1000.0) / $total_duration}")
|
||||
fi
|
||||
|
||||
# Calculate percentiles (approximate)
|
||||
local p50=0
|
||||
local p95=0
|
||||
local p99=0
|
||||
|
||||
if [ ${#latencies[@]} -gt 0 ]; then
|
||||
IFS=$'\n' sorted_latencies=($(sort -n <<<"${latencies[*]}"))
|
||||
|
||||
local idx_p50=$(( ${#sorted_latencies[@]} * 50 / 100 ))
|
||||
local idx_p95=$(( ${#sorted_latencies[@]} * 95 / 100 ))
|
||||
local idx_p99=$(( ${#sorted_latencies[@]} * 99 / 100 ))
|
||||
|
||||
p50=${sorted_latencies[$idx_p50]:-0}
|
||||
p95=${sorted_latencies[$idx_p95]:-0}
|
||||
p99=${sorted_latencies[$idx_p99]:-0}
|
||||
fi
|
||||
|
||||
# Print results
|
||||
echo ""
|
||||
echo "Results for ${num_connections} concurrent connections:"
|
||||
echo " Total Requests: ${total_requests}"
|
||||
echo " Successful: ${successful} (${success_rate}%)"
|
||||
echo " Failed: ${failed} (${error_rate}%)"
|
||||
echo " Duration: ${total_duration}ms ($(awk "BEGIN {printf \"%.2f\", $total_duration / 1000}")s)"
|
||||
echo " Throughput: ${throughput} conn/s"
|
||||
echo ""
|
||||
echo " Connection Times:"
|
||||
echo " Average: ${avg_latency}ms"
|
||||
echo " P50: ${p50}ms"
|
||||
echo " P95: ${p95}ms"
|
||||
echo " P99: ${p99}ms"
|
||||
|
||||
# Store results for summary
|
||||
echo "${num_connections}:${success_rate}:${error_rate}:${p99}:${p50}:${p95}:${throughput}" >> /tmp/foxhunt_summary.txt
|
||||
|
||||
# Check CPU and memory usage
|
||||
echo ""
|
||||
echo " Resource Usage:"
|
||||
ps aux | grep -E "(trading_service|api_gateway)" | grep -v grep | awk '{printf " %-25s CPU: %5s%% MEM: %5s%%\n", $11, $3, $4}'
|
||||
|
||||
rm -f "$results_file"
|
||||
}
|
||||
|
||||
# Initialize summary file
|
||||
rm -f /tmp/foxhunt_summary.txt
|
||||
touch /tmp/foxhunt_summary.txt
|
||||
|
||||
# Run tests at different concurrency levels
|
||||
run_concurrent_test 10 "Baseline"
|
||||
echo ""
|
||||
echo "Waiting 5 seconds before next test..."
|
||||
sleep 5
|
||||
|
||||
run_concurrent_test 50 "Moderate Load"
|
||||
echo ""
|
||||
echo "Waiting 5 seconds before next test..."
|
||||
sleep 5
|
||||
|
||||
run_concurrent_test 100 "High Load"
|
||||
echo ""
|
||||
echo "Waiting 5 seconds before next test..."
|
||||
sleep 5
|
||||
|
||||
run_concurrent_test 200 "Stress Test"
|
||||
|
||||
# Print summary table
|
||||
echo ""
|
||||
echo ""
|
||||
echo "┌────────────────────────────────────────────────────────────────────────────────────────────────┐"
|
||||
echo "│ SUMMARY TABLE - ALL TEST LEVELS │"
|
||||
echo "├────────────────────────────────────────────────────────────────────────────────────────────────┤"
|
||||
echo "│ Connections │ Success │ Error % │ Conn P99 │ Lat P50 │ Lat P95 │ Throughput │"
|
||||
echo "├────────────────────────────────────────────────────────────────────────────────────────────────┤"
|
||||
|
||||
while IFS=: read -r connections success error p99 p50 p95 throughput; do
|
||||
printf "│ %-11s │ %7s%% │ %7s%% │ %8sms │ %7sms │ %7sms │ %10s conn/s │\n" \
|
||||
"$connections" "$success" "$error" "$p99" "$p50" "$p95" "$throughput"
|
||||
done < /tmp/foxhunt_summary.txt
|
||||
|
||||
echo "└────────────────────────────────────────────────────────────────────────────────────────────────┘"
|
||||
|
||||
# Determine PASS/FAIL
|
||||
echo ""
|
||||
result_100=$(grep "^100:" /tmp/foxhunt_summary.txt || echo "")
|
||||
|
||||
if [ -n "$result_100" ]; then
|
||||
IFS=: read -r connections success error p99 p50 p95 throughput <<< "$result_100"
|
||||
|
||||
error_ok=$(awk "BEGIN {print ($error < 1.0) ? 1 : 0}")
|
||||
latency_ok=$(awk "BEGIN {print ($p99 < 100) ? 1 : 0}")
|
||||
|
||||
if [ "$error_ok" = "1" ] && [ "$latency_ok" = "1" ]; then
|
||||
echo -e "${GREEN}✅ TEST PASSED${NC} - System successfully handled 100+ concurrent connections"
|
||||
echo " - Error rate < 1%"
|
||||
echo " - P99 latency < 100ms"
|
||||
else
|
||||
echo -e "${RED}❌ TEST FAILED${NC} - System did not meet success criteria"
|
||||
echo " - Error rate: ${error}% (required: <1%)"
|
||||
echo " - P99 latency: ${p99}ms (required: <100ms)"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ WARNING${NC} - Could not find 100-connection test results"
|
||||
fi
|
||||
|
||||
# Check for connection leaks
|
||||
echo ""
|
||||
echo "Connection Status:"
|
||||
netstat -an | grep -E "(50051|50052|50053|50054)" | wc -l | awk '{printf " Active connections on service ports: %d\n", $1}'
|
||||
ss -s | grep TCP | head -1
|
||||
|
||||
# Cleanup
|
||||
rm -f /tmp/foxhunt_summary.txt
|
||||
|
||||
echo ""
|
||||
echo "Test completed at $(date)"
|
||||
345
scripts/cross_service_integration_test.sh
Executable file
345
scripts/cross_service_integration_test.sh
Executable file
@@ -0,0 +1,345 @@
|
||||
#!/bin/bash
|
||||
# Cross-Service Integration Test Script
|
||||
# Tests actual cross-service communication flows
|
||||
|
||||
set -e
|
||||
|
||||
echo "================================"
|
||||
echo "Cross-Service Integration Tests"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test results
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
function test_service_health() {
|
||||
echo "Test 1: Service Health Checks"
|
||||
echo "=============================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 4))
|
||||
|
||||
# API Gateway
|
||||
if curl -s -f http://localhost:9091/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓${NC} API Gateway (9091) - Healthy"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} API Gateway (9091) - Unhealthy"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Trading Service
|
||||
if curl -s -f http://localhost:9092/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓${NC} Trading Service (9092) - Healthy"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Trading Service (9092) - Unhealthy"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Backtesting Service
|
||||
if curl -s -f http://localhost:8083/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓${NC} Backtesting Service (8083) - Healthy"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Backtesting Service (8083) - Unhealthy"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# ML Training Service
|
||||
if curl -s -f http://localhost:8095/health > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓${NC} ML Training Service (8095) - Healthy"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} ML Training Service (8095) - Unhealthy"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_database_connection() {
|
||||
echo "Test 2: PostgreSQL Connectivity"
|
||||
echo "==============================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
if psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -c "SELECT 1" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓${NC} PostgreSQL connection successful"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
|
||||
# Check key tables
|
||||
TABLES=$(psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public'")
|
||||
echo " Found $TABLES tables in public schema"
|
||||
else
|
||||
echo -e "${RED}✗${NC} PostgreSQL connection failed"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_redis_connection() {
|
||||
echo "Test 3: Redis Connectivity"
|
||||
echo "=========================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
if redis-cli -h localhost -p 6379 PING 2>/dev/null | grep -q PONG; then
|
||||
echo -e "${GREEN}✓${NC} Redis connection successful"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
|
||||
# Test set/get
|
||||
redis-cli -h localhost -p 6379 SET test_key "integration_test" > /dev/null 2>&1
|
||||
VALUE=$(redis-cli -h localhost -p 6379 GET test_key 2>/dev/null)
|
||||
if [ "$VALUE" = "integration_test" ]; then
|
||||
echo " Redis SET/GET working"
|
||||
fi
|
||||
redis-cli -h localhost -p 6379 DEL test_key > /dev/null 2>&1
|
||||
else
|
||||
echo -e "${RED}✗${NC} Redis connection failed"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_grpc_ports() {
|
||||
echo "Test 4: gRPC Port Availability"
|
||||
echo "=============================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 4))
|
||||
|
||||
# API Gateway gRPC
|
||||
if lsof -i :50051 2>/dev/null | grep -q LISTEN; then
|
||||
echo -e "${GREEN}✓${NC} API Gateway gRPC (50051) - Listening"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} API Gateway gRPC (50051) - Not listening"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Trading Service gRPC
|
||||
if lsof -i :50052 2>/dev/null | grep -q LISTEN; then
|
||||
echo -e "${GREEN}✓${NC} Trading Service gRPC (50052) - Listening"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Trading Service gRPC (50052) - Not listening"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Backtesting Service gRPC
|
||||
if lsof -i :50053 2>/dev/null | grep -q LISTEN; then
|
||||
echo -e "${GREEN}✓${NC} Backtesting Service gRPC (50053) - Listening"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Backtesting Service gRPC (50053) - Not listening"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# ML Training Service gRPC
|
||||
if lsof -i :50054 2>/dev/null | grep -q LISTEN; then
|
||||
echo -e "${GREEN}✓${NC} ML Training Service gRPC (50054) - Listening"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} ML Training Service gRPC (50054) - Not listening"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_metrics_endpoints() {
|
||||
echo "Test 5: Prometheus Metrics Endpoints"
|
||||
echo "===================================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 4))
|
||||
|
||||
# API Gateway metrics
|
||||
if curl -s http://localhost:9091/metrics | grep -q "# TYPE"; then
|
||||
echo -e "${GREEN}✓${NC} API Gateway metrics (9091) - Available"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} API Gateway metrics (9091) - Unavailable"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Trading Service metrics
|
||||
if curl -s http://localhost:9092/metrics | grep -q "# TYPE"; then
|
||||
echo -e "${GREEN}✓${NC} Trading Service metrics (9092) - Available"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Trading Service metrics (9092) - Unavailable"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Backtesting Service metrics
|
||||
if curl -s http://localhost:9093/metrics | grep -q "# TYPE"; then
|
||||
echo -e "${GREEN}✓${NC} Backtesting Service metrics (9093) - Available"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Backtesting Service metrics (9093) - Unavailable"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# ML Training Service metrics
|
||||
if curl -s http://localhost:9094/metrics | grep -q "# TYPE"; then
|
||||
echo -e "${GREEN}✓${NC} ML Training Service metrics (9094) - Available"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} ML Training Service metrics (9094) - Unavailable"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_prometheus_targets() {
|
||||
echo "Test 6: Prometheus Service Discovery"
|
||||
echo "===================================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
if curl -s http://localhost:9090/api/v1/targets 2>/dev/null | grep -q '"health":"up"'; then
|
||||
echo -e "${GREEN}✓${NC} Prometheus has healthy targets"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
|
||||
# Count healthy targets
|
||||
HEALTHY=$(curl -s http://localhost:9090/api/v1/targets 2>/dev/null | grep -o '"health":"up"' | wc -l)
|
||||
echo " $HEALTHY services reporting to Prometheus"
|
||||
else
|
||||
echo -e "${RED}✗${NC} No healthy Prometheus targets found"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_parquet_files() {
|
||||
echo "Test 7: Parquet Test Data Availability"
|
||||
echo "======================================"
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
PARQUET_COUNT=$(find /home/jgrusewski/Work/foxhunt/test_data -name "*.parquet" 2>/dev/null | wc -l)
|
||||
|
||||
if [ "$PARQUET_COUNT" -gt 0 ]; then
|
||||
echo -e "${GREEN}✓${NC} Found $PARQUET_COUNT Parquet test files"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} No Parquet test files found"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_database_orders() {
|
||||
echo "Test 8: Database Order Persistence"
|
||||
echo "=================================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
ORDER_COUNT=$(psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -t -c "SELECT COUNT(*) FROM orders" 2>/dev/null | tr -d ' ')
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓${NC} Orders table accessible: $ORDER_COUNT orders"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to query orders table"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
function measure_latency() {
|
||||
echo "Test 9: Inter-Service Latency Measurement"
|
||||
echo "========================================="
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 4))
|
||||
|
||||
# API Gateway
|
||||
START=$(date +%s%N)
|
||||
curl -s http://localhost:9091/health > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
if [ $LATENCY -lt 100 ]; then
|
||||
echo -e "${GREEN}✓${NC} API Gateway health: ${LATENCY}ms"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} API Gateway health: ${LATENCY}ms (>100ms)"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Trading Service
|
||||
START=$(date +%s%N)
|
||||
curl -s http://localhost:9092/health > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
if [ $LATENCY -lt 100 ]; then
|
||||
echo -e "${GREEN}✓${NC} Trading Service health: ${LATENCY}ms"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} Trading Service health: ${LATENCY}ms (>100ms)"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# Backtesting Service
|
||||
START=$(date +%s%N)
|
||||
curl -s http://localhost:8083/health > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
if [ $LATENCY -lt 100 ]; then
|
||||
echo -e "${GREEN}✓${NC} Backtesting Service health: ${LATENCY}ms"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} Backtesting Service health: ${LATENCY}ms (>100ms)"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
fi
|
||||
|
||||
# ML Training Service
|
||||
START=$(date +%s%N)
|
||||
curl -s http://localhost:8095/health > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
if [ $LATENCY -lt 100 ]; then
|
||||
echo -e "${GREEN}✓${NC} ML Training Service health: ${LATENCY}ms"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} ML Training Service health: ${LATENCY}ms (>100ms)"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run all tests
|
||||
test_service_health
|
||||
test_database_connection
|
||||
test_redis_connection
|
||||
test_grpc_ports
|
||||
test_metrics_endpoints
|
||||
test_prometheus_targets
|
||||
test_parquet_files
|
||||
test_database_orders
|
||||
measure_latency
|
||||
|
||||
# Summary
|
||||
echo "================================"
|
||||
echo "Test Summary"
|
||||
echo "================================"
|
||||
echo "Total Tests: $TOTAL_TESTS"
|
||||
echo -e "${GREEN}Passed: $PASSED_TESTS${NC}"
|
||||
echo -e "${RED}Failed: $FAILED_TESTS${NC}"
|
||||
echo ""
|
||||
|
||||
PASS_RATE=$(awk "BEGIN {printf \"%.1f\", ($PASSED_TESTS / $TOTAL_TESTS) * 100}")
|
||||
echo "Pass Rate: $PASS_RATE%"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ ALL TESTS PASSED${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ SOME TESTS FAILED${NC}"
|
||||
exit 1
|
||||
fi
|
||||
363
scripts/database_validation.sh
Executable file
363
scripts/database_validation.sh
Executable file
@@ -0,0 +1,363 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Database Validation Script for Foxhunt HFT Trading System
|
||||
# Validates PostgreSQL, Redis, SQLite, and InfluxDB connections and performance
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Foxhunt Database Layer Validation"
|
||||
echo "===================================="
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
POSTGRES_URL="${DATABASE_URL:-postgresql://postgres:password@localhost:5432/foxhunt_test}"
|
||||
REDIS_URL="${REDIS_URL:-localhost:6379}"
|
||||
INFLUX_URL="${INFLUX_URL:-localhost:8086}"
|
||||
SQLITE_DB="validation_test.db"
|
||||
|
||||
# Counters
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TOTAL_TESTS=0
|
||||
|
||||
# Helper functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
((TESTS_PASSED++))
|
||||
((TOTAL_TESTS++))
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
((TOTAL_TESTS++))
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
((TESTS_FAILED++))
|
||||
((TOTAL_TESTS++))
|
||||
}
|
||||
|
||||
# Test PostgreSQL
|
||||
test_postgresql() {
|
||||
echo ""
|
||||
log_info "🔍 Testing PostgreSQL..."
|
||||
|
||||
# Check if PostgreSQL is accessible
|
||||
if command -v psql >/dev/null 2>&1; then
|
||||
# Test connection
|
||||
if psql "$POSTGRES_URL" -c "SELECT 1;" >/dev/null 2>&1; then
|
||||
log_success "PostgreSQL connection successful"
|
||||
|
||||
# Performance test - measure simple query latency
|
||||
local start_time=$(date +%s%N)
|
||||
for i in {1..100}; do
|
||||
psql "$POSTGRES_URL" -t -c "SELECT 1;" >/dev/null 2>&1
|
||||
done
|
||||
local end_time=$(date +%s%N)
|
||||
local total_time=$((end_time - start_time))
|
||||
local avg_latency_ms=$((total_time / 100000000)) # Convert to ms
|
||||
|
||||
log_info "Average query latency: ${avg_latency_ms}ms"
|
||||
|
||||
if [ $avg_latency_ms -lt 10 ]; then
|
||||
log_success "PostgreSQL performance meets requirements (<10ms)"
|
||||
else
|
||||
log_warning "PostgreSQL performance suboptimal (${avg_latency_ms}ms > 10ms)"
|
||||
fi
|
||||
|
||||
# Test ACID transaction
|
||||
psql "$POSTGRES_URL" >/dev/null 2>&1 << EOF
|
||||
BEGIN;
|
||||
CREATE TABLE IF NOT EXISTS acid_test_$$
|
||||
(id SERIAL PRIMARY KEY, value INTEGER);
|
||||
INSERT INTO acid_test_$$ (value) VALUES (100);
|
||||
ROLLBACK;
|
||||
EOF
|
||||
|
||||
# Verify rollback worked
|
||||
local count=$(psql "$POSTGRES_URL" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_name='acid_test_$$';" 2>/dev/null | tr -d ' ' || echo "0")
|
||||
if [ "$count" = "0" ]; then
|
||||
log_success "ACID transaction rollback test passed"
|
||||
else
|
||||
log_error "ACID transaction rollback test failed"
|
||||
fi
|
||||
|
||||
else
|
||||
log_error "PostgreSQL connection failed"
|
||||
fi
|
||||
else
|
||||
log_warning "psql not available - skipping PostgreSQL tests"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test Redis
|
||||
test_redis() {
|
||||
echo ""
|
||||
log_info "🔍 Testing Redis..."
|
||||
|
||||
if command -v redis-cli >/dev/null 2>&1; then
|
||||
# Test connection
|
||||
if redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" ping >/dev/null 2>&1; then
|
||||
log_success "Redis connection successful"
|
||||
|
||||
# Performance test
|
||||
local start_time=$(date +%s%N)
|
||||
for i in {1..100}; do
|
||||
redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" set "test_key_$i" "test_value_$i" >/dev/null 2>&1
|
||||
redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" get "test_key_$i" >/dev/null 2>&1
|
||||
done
|
||||
local end_time=$(date +%s%N)
|
||||
local total_time=$((end_time - start_time))
|
||||
local avg_latency_ms=$((total_time / 100000000))
|
||||
|
||||
log_info "Average operation latency: ${avg_latency_ms}ms"
|
||||
|
||||
if [ $avg_latency_ms -lt 5 ]; then
|
||||
log_success "Redis performance meets requirements (<5ms)"
|
||||
else
|
||||
log_warning "Redis performance suboptimal (${avg_latency_ms}ms > 5ms)"
|
||||
fi
|
||||
|
||||
# Test TTL functionality
|
||||
redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" set ttl_test_key ttl_test_value >/dev/null 2>&1
|
||||
redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" expire ttl_test_key 1 >/dev/null 2>&1
|
||||
sleep 2
|
||||
local exists=$(redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" exists ttl_test_key 2>/dev/null)
|
||||
if [ "$exists" = "0" ]; then
|
||||
log_success "Redis TTL functionality working"
|
||||
else
|
||||
log_error "Redis TTL functionality failed"
|
||||
fi
|
||||
|
||||
# Cleanup test keys
|
||||
for i in {1..100}; do
|
||||
redis-cli -h "${REDIS_URL%:*}" -p "${REDIS_URL#*:}" del "test_key_$i" >/dev/null 2>&1
|
||||
done
|
||||
|
||||
else
|
||||
log_error "Redis connection failed"
|
||||
fi
|
||||
else
|
||||
log_warning "redis-cli not available - skipping Redis tests"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test SQLite
|
||||
test_sqlite() {
|
||||
echo ""
|
||||
log_info "🔍 Testing SQLite..."
|
||||
|
||||
if command -v sqlite3 >/dev/null 2>&1; then
|
||||
# Remove existing test database
|
||||
rm -f "$SQLITE_DB"
|
||||
|
||||
# Test database creation and operations
|
||||
if sqlite3 "$SQLITE_DB" "CREATE TABLE config_test (key TEXT PRIMARY KEY, value TEXT, updated_at INTEGER);" >/dev/null 2>&1; then
|
||||
log_success "SQLite database creation successful"
|
||||
|
||||
# Performance test
|
||||
local start_time=$(date +%s%N)
|
||||
for i in {1..50}; do
|
||||
sqlite3 "$SQLITE_DB" "INSERT OR REPLACE INTO config_test (key, value, updated_at) VALUES ('key_$i', 'value_$i', $i);" >/dev/null 2>&1
|
||||
sqlite3 "$SQLITE_DB" "SELECT value FROM config_test WHERE key='key_$i';" >/dev/null 2>&1
|
||||
done
|
||||
local end_time=$(date +%s%N)
|
||||
local total_time=$((end_time - start_time))
|
||||
local avg_latency_ms=$((total_time / 50000000))
|
||||
|
||||
log_info "Average operation latency: ${avg_latency_ms}ms"
|
||||
|
||||
if [ $avg_latency_ms -lt 20 ]; then
|
||||
log_success "SQLite performance acceptable (<20ms)"
|
||||
else
|
||||
log_warning "SQLite performance suboptimal (${avg_latency_ms}ms > 20ms)"
|
||||
fi
|
||||
|
||||
# Test hot-reload simulation
|
||||
sqlite3 "$SQLITE_DB" "INSERT OR REPLACE INTO config_test (key, value, updated_at) VALUES ('hot_reload_test', 'initial_value', 1);" >/dev/null 2>&1
|
||||
sqlite3 "$SQLITE_DB" "UPDATE config_test SET value='updated_value', updated_at=2 WHERE key='hot_reload_test';" >/dev/null 2>&1
|
||||
local updated_value=$(sqlite3 "$SQLITE_DB" "SELECT value FROM config_test WHERE key='hot_reload_test';" 2>/dev/null)
|
||||
|
||||
if [ "$updated_value" = "updated_value" ]; then
|
||||
log_success "SQLite configuration hot-reload simulation successful"
|
||||
else
|
||||
log_error "SQLite configuration hot-reload simulation failed"
|
||||
fi
|
||||
|
||||
else
|
||||
log_error "SQLite database creation failed"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -f "$SQLITE_DB"
|
||||
else
|
||||
log_warning "sqlite3 not available - skipping SQLite tests"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test InfluxDB
|
||||
test_influxdb() {
|
||||
echo ""
|
||||
log_info "🔍 Testing InfluxDB..."
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
# Test health endpoint
|
||||
if curl -s -o /dev/null -w "%{http_code}" "http://${INFLUX_URL}/health" | grep -q "200"; then
|
||||
log_success "InfluxDB health check successful"
|
||||
|
||||
# Test write operation (simplified)
|
||||
local timestamp=$(date +%s%N)
|
||||
local line_protocol="test_measurement,tag1=value1 field1=123 $timestamp"
|
||||
local write_url="http://${INFLUX_URL}/api/v2/write?org=test&bucket=test"
|
||||
|
||||
if curl -s -o /dev/null -w "%{http_code}" -X POST "$write_url" \
|
||||
-H "Content-Type: text/plain" \
|
||||
-d "$line_protocol" | grep -q "204\|200"; then
|
||||
log_success "InfluxDB write test successful (or acceptable without auth)"
|
||||
else
|
||||
log_warning "InfluxDB write test failed (may need authentication)"
|
||||
fi
|
||||
|
||||
elif curl -s -o /dev/null -w "%{http_code}" "http://${INFLUX_URL}/ping" | grep -q "204"; then
|
||||
log_success "InfluxDB ping successful (legacy endpoint)"
|
||||
else
|
||||
log_error "InfluxDB connection failed"
|
||||
fi
|
||||
else
|
||||
log_warning "curl not available - skipping InfluxDB tests"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test backup capabilities
|
||||
test_backup_capabilities() {
|
||||
echo ""
|
||||
log_info "🔍 Testing backup capabilities..."
|
||||
|
||||
# Check if backup tools are available
|
||||
local backup_tools=("pg_dump" "redis-cli" "influxd" "tar")
|
||||
local available_tools=0
|
||||
|
||||
for tool in "${backup_tools[@]}"; do
|
||||
if command -v "$tool" >/dev/null 2>&1; then
|
||||
((available_tools++))
|
||||
log_success "$tool available for backups"
|
||||
else
|
||||
log_warning "$tool not available for backups"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $available_tools -gt 2 ]; then
|
||||
log_success "Sufficient backup tools available ($available_tools/4)"
|
||||
else
|
||||
log_warning "Limited backup capabilities ($available_tools/4 tools available)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test health monitoring
|
||||
test_health_monitoring() {
|
||||
echo ""
|
||||
log_info "🔍 Testing health monitoring capabilities..."
|
||||
|
||||
# Check if monitoring tools are available
|
||||
local monitoring_tools=("systemctl" "ps" "netstat" "ss")
|
||||
local available_tools=0
|
||||
|
||||
for tool in "${monitoring_tools[@]}"; do
|
||||
if command -v "$tool" >/dev/null 2>&1; then
|
||||
((available_tools++))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $available_tools -gt 2 ]; then
|
||||
log_success "Health monitoring tools available ($available_tools/4)"
|
||||
else
|
||||
log_warning "Limited health monitoring capabilities ($available_tools/4)"
|
||||
fi
|
||||
|
||||
# Test basic system health
|
||||
local load_avg=$(uptime | awk '{print $(NF-2)}' | sed 's/,//')
|
||||
if (( $(echo "$load_avg < 2.0" | bc -l 2>/dev/null || echo "1") )); then
|
||||
log_success "System load acceptable ($load_avg)"
|
||||
else
|
||||
log_warning "High system load detected ($load_avg)"
|
||||
fi
|
||||
|
||||
# Test memory usage
|
||||
local mem_usage=$(free | awk '/Mem:/ { printf("%.1f", $3/$2 * 100.0) }')
|
||||
if (( $(echo "$mem_usage < 80.0" | bc -l 2>/dev/null || echo "1") )); then
|
||||
log_success "Memory usage acceptable (${mem_usage}%)"
|
||||
else
|
||||
log_warning "High memory usage detected (${mem_usage}%)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
local start_time=$(date +%s)
|
||||
|
||||
log_info "Starting database validation at $(date)"
|
||||
log_info "Configuration:"
|
||||
log_info " PostgreSQL: $POSTGRES_URL"
|
||||
log_info " Redis: $REDIS_URL"
|
||||
log_info " InfluxDB: $INFLUX_URL"
|
||||
log_info " SQLite: $SQLITE_DB"
|
||||
|
||||
# Run all tests
|
||||
test_postgresql
|
||||
test_redis
|
||||
test_sqlite
|
||||
test_influxdb
|
||||
test_backup_capabilities
|
||||
test_health_monitoring
|
||||
|
||||
local end_time=$(date +%s)
|
||||
local total_duration=$((end_time - start_time))
|
||||
|
||||
# Generate summary
|
||||
echo ""
|
||||
echo "📊 VALIDATION SUMMARY"
|
||||
echo "===================="
|
||||
echo "Total Time: ${total_duration}s"
|
||||
echo "Tests Passed: $TESTS_PASSED"
|
||||
echo "Tests Failed: $TESTS_FAILED"
|
||||
echo "Total Tests: $TOTAL_TESTS"
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
log_success "🎉 All database validation tests completed successfully!"
|
||||
echo ""
|
||||
echo "✅ Database Layer Status:"
|
||||
echo " - PostgreSQL: Connection and ACID transactions working"
|
||||
echo " - Redis: Caching and TTL functionality working"
|
||||
echo " - SQLite: Configuration storage and hot-reload ready"
|
||||
echo " - InfluxDB: Metrics storage capability verified"
|
||||
echo " - Backup/Recovery: Tools available for data protection"
|
||||
echo " - Health Monitoring: System monitoring capabilities ready"
|
||||
echo ""
|
||||
echo "🚀 The database layer is ready for HFT operations!"
|
||||
exit 0
|
||||
else
|
||||
log_error "💥 Database validation completed with $TESTS_FAILED failures"
|
||||
echo ""
|
||||
echo "❌ Issues found:"
|
||||
echo " - $TESTS_FAILED out of $TOTAL_TESTS tests failed"
|
||||
echo " - Review the logs above for specific failure details"
|
||||
echo " - Consider addressing failed tests before production deployment"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Execute main function
|
||||
main "$@"
|
||||
266
scripts/db_load_test.sh
Executable file
266
scripts/db_load_test.sh
Executable file
@@ -0,0 +1,266 @@
|
||||
#!/bin/bash
|
||||
# PostgreSQL Load Test Script for Foxhunt Trading System
|
||||
# Tests database performance under increasing concurrent load
|
||||
# Workload: 40% INSERT, 30% SELECT, 20% UPDATE, 10% Complex queries
|
||||
|
||||
set -e
|
||||
|
||||
DB_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
TEST_DURATION=60 # seconds per test
|
||||
RESULTS_FILE="/tmp/db_load_test_results_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "PostgreSQL Load Test - Foxhunt Trading" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Duration: $TEST_DURATION seconds per scenario" | tee -a "$RESULTS_FILE"
|
||||
echo "Start Time: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Function to check database health
|
||||
check_db_health() {
|
||||
echo "=== Database Health Check ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT version();" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SHOW max_connections;" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SHOW shared_buffers;" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SHOW synchronous_commit;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Cache Hit Ratio ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT sum(blks_hit)::float / (sum(blks_hit) + sum(blks_read)) * 100 AS cache_hit_ratio FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Active Connections ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT count(*) FROM pg_stat_activity WHERE state = 'active';" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to get baseline table counts
|
||||
get_baseline_counts() {
|
||||
echo "=== Baseline Table Counts ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT 'orders' AS table_name, COUNT(*) FROM orders UNION ALL SELECT 'executions', COUNT(*) FROM executions UNION ALL SELECT 'fills', COUNT(*) FROM fills UNION ALL SELECT 'positions', COUNT(*) FROM positions;" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to run single-threaded baseline test
|
||||
run_baseline_test() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "TEST 1: BASELINE (Single-threaded)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
local start_time=$(date +%s)
|
||||
local end_time=$((start_time + TEST_DURATION))
|
||||
local insert_count=0
|
||||
local select_count=0
|
||||
local update_count=0
|
||||
local complex_count=0
|
||||
|
||||
while [ $(date +%s) -lt $end_time ]; do
|
||||
# 40% INSERT - orders
|
||||
for i in {1..4}; do
|
||||
psql "$DB_URL" -c "INSERT INTO orders (order_id, user_id, symbol, side, order_type, quantity, price, status, created_at) VALUES (gen_random_uuid(), gen_random_uuid(), 'BTC/USD', 'buy', 'limit', 1.0, 50000.0, 'pending', NOW());" > /dev/null 2>&1
|
||||
((insert_count++))
|
||||
done
|
||||
|
||||
# 30% SELECT - order status
|
||||
for i in {1..3}; do
|
||||
psql "$DB_URL" -c "SELECT order_id, status, quantity FROM orders WHERE symbol = 'BTC/USD' ORDER BY created_at DESC LIMIT 10;" > /dev/null 2>&1
|
||||
((select_count++))
|
||||
done
|
||||
|
||||
# 20% UPDATE - order status
|
||||
for i in {1..2}; do
|
||||
psql "$DB_URL" -c "UPDATE orders SET status = 'filled', updated_at = NOW() WHERE order_id = (SELECT order_id FROM orders WHERE status = 'pending' LIMIT 1);" > /dev/null 2>&1
|
||||
((update_count++))
|
||||
done
|
||||
|
||||
# 10% Complex query - position aggregation
|
||||
psql "$DB_URL" -c "SELECT symbol, SUM(quantity) as total_quantity, AVG(entry_price) as avg_price FROM positions WHERE user_id IN (SELECT user_id FROM users LIMIT 5) GROUP BY symbol;" > /dev/null 2>&1
|
||||
((complex_count++))
|
||||
done
|
||||
|
||||
local actual_duration=$(($(date +%s) - start_time))
|
||||
local total_ops=$((insert_count + select_count + update_count + complex_count))
|
||||
local tps=$(echo "scale=2; $total_ops / $actual_duration" | bc)
|
||||
|
||||
echo "Duration: $actual_duration seconds" | tee -a "$RESULTS_FILE"
|
||||
echo "Total Operations: $total_ops" | tee -a "$RESULTS_FILE"
|
||||
echo " - INSERTs: $insert_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - SELECTs: $select_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - UPDATEs: $update_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - Complex: $complex_count" | tee -a "$RESULTS_FILE"
|
||||
echo "Transactions per Second (TPS): $tps" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to run concurrent load test
|
||||
run_concurrent_test() {
|
||||
local num_connections=$1
|
||||
local test_name=$2
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "TEST: $test_name ($num_connections concurrent connections)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
local pids=()
|
||||
local temp_dir="/tmp/db_load_test_$$"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
# Start time
|
||||
local start_time=$(date +%s.%N)
|
||||
|
||||
# Launch concurrent workers
|
||||
for ((i=1; i<=num_connections; i++)); do
|
||||
(
|
||||
local worker_inserts=0
|
||||
local worker_selects=0
|
||||
local worker_updates=0
|
||||
local worker_complex=0
|
||||
local end_time=$(echo "$start_time + $TEST_DURATION" | bc)
|
||||
|
||||
while (( $(echo "$(date +%s.%N) < $end_time" | bc -l) )); do
|
||||
# 40% INSERT
|
||||
if (( RANDOM % 10 < 4 )); then
|
||||
psql "$DB_URL" -c "INSERT INTO orders (order_id, user_id, symbol, side, order_type, quantity, price, status, created_at) VALUES (gen_random_uuid(), gen_random_uuid(), 'BTC/USD', 'buy', 'limit', 1.0, 50000.0, 'pending', NOW());" > /dev/null 2>&1
|
||||
((worker_inserts++))
|
||||
# 30% SELECT
|
||||
elif (( RANDOM % 10 < 7 )); then
|
||||
psql "$DB_URL" -c "SELECT order_id, status, quantity FROM orders WHERE symbol = 'BTC/USD' ORDER BY created_at DESC LIMIT 10;" > /dev/null 2>&1
|
||||
((worker_selects++))
|
||||
# 20% UPDATE
|
||||
elif (( RANDOM % 10 < 9 )); then
|
||||
psql "$DB_URL" -c "UPDATE orders SET status = 'filled', updated_at = NOW() WHERE order_id = (SELECT order_id FROM orders WHERE status = 'pending' LIMIT 1);" > /dev/null 2>&1
|
||||
((worker_updates++))
|
||||
# 10% Complex
|
||||
else
|
||||
psql "$DB_URL" -c "SELECT symbol, COUNT(*) as order_count FROM orders WHERE created_at > NOW() - INTERVAL '1 hour' GROUP BY symbol;" > /dev/null 2>&1
|
||||
((worker_complex++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Write worker results
|
||||
echo "$worker_inserts $worker_selects $worker_updates $worker_complex" > "$temp_dir/worker_$i.txt"
|
||||
) &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
# Wait for all workers to complete
|
||||
for pid in "${pids[@]}"; do
|
||||
wait $pid
|
||||
done
|
||||
|
||||
local end_time=$(date +%s.%N)
|
||||
local actual_duration=$(echo "$end_time - $start_time" | bc)
|
||||
|
||||
# Aggregate results
|
||||
local total_inserts=0
|
||||
local total_selects=0
|
||||
local total_updates=0
|
||||
local total_complex=0
|
||||
|
||||
for ((i=1; i<=num_connections; i++)); do
|
||||
if [ -f "$temp_dir/worker_$i.txt" ]; then
|
||||
read inserts selects updates complex < "$temp_dir/worker_$i.txt"
|
||||
total_inserts=$((total_inserts + inserts))
|
||||
total_selects=$((total_selects + selects))
|
||||
total_updates=$((total_updates + updates))
|
||||
total_complex=$((total_complex + complex))
|
||||
fi
|
||||
done
|
||||
|
||||
local total_ops=$((total_inserts + total_selects + total_updates + total_complex))
|
||||
local tps=$(echo "scale=2; $total_ops / $actual_duration" | bc)
|
||||
|
||||
echo "Duration: $actual_duration seconds" | tee -a "$RESULTS_FILE"
|
||||
echo "Concurrent Connections: $num_connections" | tee -a "$RESULTS_FILE"
|
||||
echo "Total Operations: $total_ops" | tee -a "$RESULTS_FILE"
|
||||
echo " - INSERTs: $total_inserts" | tee -a "$RESULTS_FILE"
|
||||
echo " - SELECTs: $total_selects" | tee -a "$RESULTS_FILE"
|
||||
echo " - UPDATEs: $total_updates" | tee -a "$RESULTS_FILE"
|
||||
echo " - Complex: $total_complex" | tee -a "$RESULTS_FILE"
|
||||
echo "Transactions per Second (TPS): $tps" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check for connection pool exhaustion
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Connection Pool Status ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT count(*), state FROM pg_stat_activity GROUP BY state;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check for locks and deadlocks
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Lock Contention ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT count(*) as lock_count FROM pg_locks WHERE NOT granted;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Deadlock Stats ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT datname, deadlocks FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$temp_dir"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to check query performance
|
||||
check_query_performance() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "QUERY PERFORMANCE ANALYSIS" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Top 10 Slowest Queries ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT query, calls, mean_exec_time, max_exec_time FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;" 2>&1 | tee -a "$RESULTS_FILE" || echo "pg_stat_statements extension not available" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Table Statistics ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del FROM pg_stat_user_tables WHERE schemaname = 'public' AND relname IN ('orders', 'executions', 'fills', 'positions') ORDER BY n_tup_ins DESC;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Index Usage ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT schemaname, tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes WHERE schemaname = 'public' AND tablename IN ('orders', 'executions', 'fills', 'positions') ORDER BY idx_scan DESC LIMIT 10;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to check resource utilization
|
||||
check_resource_utilization() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "RESOURCE UTILIZATION" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Database Size ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS size FROM pg_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Table Sizes ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total_size FROM pg_stat_user_tables WHERE schemaname = 'public' AND relname IN ('orders', 'executions', 'fills', 'positions') ORDER BY pg_total_relation_size(relid) DESC;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Cache Hit Ratio (Final) ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT sum(blks_hit)::float / (sum(blks_hit) + sum(blks_read)) * 100 AS cache_hit_ratio FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
echo "Starting PostgreSQL Load Test..."
|
||||
|
||||
# Initial health check
|
||||
check_db_health
|
||||
get_baseline_counts
|
||||
|
||||
# Run tests with increasing concurrency
|
||||
run_baseline_test
|
||||
run_concurrent_test 10 "MODERATE LOAD"
|
||||
run_concurrent_test 50 "HIGH LOAD"
|
||||
run_concurrent_test 100 "STRESS TEST"
|
||||
|
||||
# Post-test analysis
|
||||
check_query_performance
|
||||
check_resource_utilization
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Completed: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "Results saved to: $RESULTS_FILE" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Run main test
|
||||
main
|
||||
218
scripts/db_load_test_pgbench.sh
Executable file
218
scripts/db_load_test_pgbench.sh
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/bin/bash
|
||||
# PostgreSQL Load Test using pgbench - Foxhunt Trading System
|
||||
# Tests database performance under increasing concurrent load
|
||||
|
||||
set -e
|
||||
|
||||
DB_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
DB_HOST="localhost"
|
||||
DB_PORT="5432"
|
||||
DB_NAME="foxhunt"
|
||||
DB_USER="foxhunt"
|
||||
DB_PASS="foxhunt_dev_password"
|
||||
|
||||
TEST_DURATION=30 # seconds per test
|
||||
WORKLOAD_FILE="trading_workload.sql"
|
||||
RESULTS_FILE="DB_LOAD_TEST_RESULTS_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
export PGPASSWORD="$DB_PASS"
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "PostgreSQL Load Test - Foxhunt Trading" | tee -a "$RESULTS_FILE"
|
||||
echo "Using pgbench with custom trading workload" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Duration: $TEST_DURATION seconds per scenario" | tee -a "$RESULTS_FILE"
|
||||
echo "Start Time: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Function to check database health
|
||||
check_db_health() {
|
||||
echo "=== Database Health Check ===" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "PostgreSQL Version:" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT version();" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "Configuration:" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SHOW max_connections;" | awk '{print " max_connections: " $1}' | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SHOW shared_buffers;" | awk '{print " shared_buffers: " $1}' | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SHOW synchronous_commit;" | awk '{print " synchronous_commit: " $1}' | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "Cache Hit Ratio:" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT (sum(blks_hit)::float / NULLIF(sum(blks_hit) + sum(blks_read), 0) * 100)::numeric(5,2) || '%' AS cache_hit_ratio FROM pg_stat_database WHERE datname = 'foxhunt';" | awk '{print " " $1}' | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to get baseline table counts
|
||||
get_baseline_counts() {
|
||||
echo "=== Baseline Table Counts ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT 'orders' AS table_name, COUNT(*) FROM orders UNION ALL SELECT 'executions', COUNT(*) FROM executions UNION ALL SELECT 'fills', COUNT(*) FROM fills UNION ALL SELECT 'positions', COUNT(*) FROM positions;" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to run pgbench test
|
||||
run_pgbench_test() {
|
||||
local clients=$1
|
||||
local test_name=$2
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "TEST: $test_name" | tee -a "$RESULTS_FILE"
|
||||
echo "Concurrent Clients: $clients" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Run pgbench
|
||||
pgbench -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" \
|
||||
-c "$clients" -j $(( clients > 4 ? 4 : clients )) \
|
||||
-T "$TEST_DURATION" \
|
||||
-f "$WORKLOAD_FILE" \
|
||||
-n 2>&1 | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check connection pool status
|
||||
echo "=== Connection Pool Status ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT state, COUNT(*) FROM pg_stat_activity WHERE datname = 'foxhunt' GROUP BY state ORDER BY COUNT(*) DESC;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check for lock contention
|
||||
echo "=== Lock Contention ===" | tee -a "$RESULTS_FILE"
|
||||
local locks_waiting=$(psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT COUNT(*) FROM pg_locks WHERE NOT granted;" | tr -d ' ')
|
||||
echo " Locks waiting: $locks_waiting" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check deadlock count
|
||||
echo "=== Deadlock Statistics ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "SELECT datname, deadlocks, conflicts FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to analyze query performance
|
||||
analyze_query_performance() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "QUERY PERFORMANCE ANALYSIS" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Table Statistics ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
|
||||
SELECT relname,
|
||||
n_tup_ins AS inserts,
|
||||
n_tup_upd AS updates,
|
||||
n_tup_del AS deletes,
|
||||
seq_scan,
|
||||
idx_scan,
|
||||
CASE WHEN seq_scan + idx_scan > 0
|
||||
THEN (idx_scan::float / (seq_scan + idx_scan) * 100)::numeric(5,2)
|
||||
ELSE 0 END AS idx_scan_pct
|
||||
FROM pg_stat_user_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND relname IN ('orders', 'executions', 'fills', 'positions')
|
||||
ORDER BY n_tup_ins DESC;
|
||||
" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Index Usage ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
|
||||
SELECT tablename, indexname, idx_scan, idx_tup_read, idx_tup_fetch
|
||||
FROM pg_stat_user_indexes
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename IN ('orders', 'executions', 'fills', 'positions')
|
||||
ORDER BY idx_scan DESC
|
||||
LIMIT 10;
|
||||
" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to check resource utilization
|
||||
check_resource_utilization() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "RESOURCE UTILIZATION" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Database Size ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
|
||||
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
|
||||
FROM pg_database
|
||||
WHERE datname = 'foxhunt';
|
||||
" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Table Sizes ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
|
||||
SELECT relname,
|
||||
pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
|
||||
pg_size_pretty(pg_relation_size(relid)) AS table_size,
|
||||
pg_size_pretty(pg_total_relation_size(relid) - pg_relation_size(relid)) AS index_size
|
||||
FROM pg_stat_user_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND relname IN ('orders', 'executions', 'fills', 'positions')
|
||||
ORDER BY pg_total_relation_size(relid) DESC;
|
||||
" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Final Cache Hit Ratio ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
|
||||
SELECT (sum(blks_hit)::float / NULLIF(sum(blks_hit) + sum(blks_read), 0) * 100)::numeric(5,2) || '%' AS cache_hit_ratio
|
||||
FROM pg_stat_database
|
||||
WHERE datname = 'foxhunt';
|
||||
" | awk '{print " " $1}' | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to get final table counts
|
||||
get_final_counts() {
|
||||
echo "=== Final Table Counts ===" | tee -a "$RESULTS_FILE"
|
||||
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "
|
||||
SELECT 'orders' AS table_name, COUNT(*) FROM orders
|
||||
UNION ALL SELECT 'executions', COUNT(*) FROM executions
|
||||
UNION ALL SELECT 'fills', COUNT(*) FROM fills
|
||||
UNION ALL SELECT 'positions', COUNT(*) FROM positions;
|
||||
" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
echo "Starting PostgreSQL Load Test with pgbench..."
|
||||
|
||||
# Check if workload file exists
|
||||
if [ ! -f "$WORKLOAD_FILE" ]; then
|
||||
echo "ERROR: Workload file $WORKLOAD_FILE not found!" | tee -a "$RESULTS_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Initial health check
|
||||
check_db_health
|
||||
get_baseline_counts
|
||||
|
||||
# Run tests with increasing concurrency
|
||||
run_pgbench_test 1 "BASELINE (Single Client)"
|
||||
run_pgbench_test 10 "MODERATE LOAD (10 Clients)"
|
||||
run_pgbench_test 50 "HIGH LOAD (50 Clients)"
|
||||
run_pgbench_test 100 "STRESS TEST (100 Clients)"
|
||||
|
||||
# Post-test analysis
|
||||
analyze_query_performance
|
||||
check_resource_utilization
|
||||
get_final_counts
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Completed: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "Results saved to: $RESULTS_FILE" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo ""
|
||||
echo "Test complete! Results saved to: $RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Run main test
|
||||
main
|
||||
140
scripts/db_load_test_simple.sh
Executable file
140
scripts/db_load_test_simple.sh
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
# Simple but effective PostgreSQL Load Test
|
||||
# Direct SQL execution with timing
|
||||
|
||||
set -e
|
||||
|
||||
DB_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
TEST_DURATION=20 # seconds per test
|
||||
RESULTS_FILE="DB_LOAD_TEST_RESULTS_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
echo "========================================" | tee "$RESULTS_FILE"
|
||||
echo "PostgreSQL Load Test - Foxhunt Trading" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Duration: $TEST_DURATION seconds per scenario" | tee -a "$RESULTS_FILE"
|
||||
echo "Start Time: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Health check
|
||||
echo "=== Database Configuration ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SHOW max_connections;" | awk '{print "Max Connections: " $1}' | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SHOW shared_buffers;" | awk '{print "Shared Buffers: " $1}' | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SHOW synchronous_commit;" | awk '{print "Synchronous Commit: " $1}' | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Baseline Table Counts ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT 'orders' AS table_name, COUNT(*) FROM orders;" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Function to run load test
|
||||
run_load_test() {
|
||||
local clients=$1
|
||||
local test_name=$2
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "TEST: $test_name ($clients clients)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
local temp_dir="/tmp/dbtest_$$_$clients"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
local start_time=$(date +%s.%N)
|
||||
|
||||
# Launch workers
|
||||
for ((i=1; i<=clients; i++)); do
|
||||
(
|
||||
local count=0
|
||||
local errors=0
|
||||
local end_time=$(echo "$start_time + $TEST_DURATION" | bc)
|
||||
|
||||
while (( $(echo "$(date +%s.%N) < $end_time" | bc -l) )); do
|
||||
# Insert order
|
||||
if psql "$DB_URL" -c "INSERT INTO orders (account_id, symbol, side, order_type, quantity, limit_price, venue, created_at, updated_at) VALUES ('load_test', 'BTC/USD', 'buy', 'limit', 100000000, 5000000000000, 'test', EXTRACT(EPOCH FROM NOW())*1000000000, EXTRACT(EPOCH FROM NOW())*1000000000);" > /dev/null 2>&1; then
|
||||
((count++))
|
||||
else
|
||||
((errors++))
|
||||
fi
|
||||
|
||||
# Periodically run queries
|
||||
if (( count % 5 == 0 )); then
|
||||
psql "$DB_URL" -c "SELECT COUNT(*) FROM orders WHERE symbol = 'BTC/USD';" > /dev/null 2>&1 || ((errors++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "$count $errors" > "$temp_dir/worker_$i.txt"
|
||||
) &
|
||||
done
|
||||
|
||||
# Wait for completion
|
||||
wait
|
||||
|
||||
local end_time=$(date +%s.%N)
|
||||
local duration=$(echo "$end_time - $start_time" | bc)
|
||||
|
||||
# Aggregate results
|
||||
local total_ops=0
|
||||
local total_errors=0
|
||||
|
||||
for ((i=1; i<=clients; i++)); do
|
||||
if [ -f "$temp_dir/worker_$i.txt" ]; then
|
||||
read ops errors < "$temp_dir/worker_$i.txt"
|
||||
total_ops=$((total_ops + ops))
|
||||
total_errors=$((total_errors + errors))
|
||||
fi
|
||||
done
|
||||
|
||||
local tps=$(echo "scale=2; $total_ops / $duration" | bc)
|
||||
|
||||
echo "Duration: $duration seconds" | tee -a "$RESULTS_FILE"
|
||||
echo "Total Operations: $total_ops" | tee -a "$RESULTS_FILE"
|
||||
echo "Errors: $total_errors" | tee -a "$RESULTS_FILE"
|
||||
echo "Transactions per Second (TPS): $tps" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Connection pool status
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "Connection Pool:" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT state, COUNT(*) FROM pg_stat_activity WHERE datname='foxhunt' GROUP BY state;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Lock stats
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
local locks=$(psql "$DB_URL" -t -c "SELECT COUNT(*) FROM pg_locks WHERE NOT granted;" | tr -d ' ')
|
||||
echo "Locks Waiting: $locks" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Deadlocks
|
||||
local deadlocks=$(psql "$DB_URL" -t -c "SELECT deadlocks FROM pg_stat_database WHERE datname='foxhunt';" | tr -d ' ')
|
||||
echo "Deadlocks: $deadlocks" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
rm -rf "$temp_dir"
|
||||
}
|
||||
|
||||
# Run tests
|
||||
run_load_test 1 "BASELINE"
|
||||
run_load_test 10 "MODERATE"
|
||||
run_load_test 50 "HIGH"
|
||||
run_load_test 100 "STRESS"
|
||||
|
||||
# Final analysis
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "FINAL ANALYSIS" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Table Statistics ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT relname, n_tup_ins as inserts, n_tup_upd as updates, idx_scan FROM pg_stat_user_tables WHERE relname='orders';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Cache Hit Ratio ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SELECT (sum(blks_hit)::float / NULLIF(sum(blks_hit) + sum(blks_read), 0) * 100)::numeric(5,2) || '%' FROM pg_stat_database WHERE datname='foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Final Counts ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT COUNT(*) as total_orders FROM orders;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Completed: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo ""
|
||||
echo "Results saved to: $RESULTS_FILE"
|
||||
266
scripts/db_load_test_v2.sh
Executable file
266
scripts/db_load_test_v2.sh
Executable file
@@ -0,0 +1,266 @@
|
||||
#!/bin/bash
|
||||
# PostgreSQL Load Test Script for Foxhunt Trading System (Schema-accurate)
|
||||
# Tests database performance under increasing concurrent load
|
||||
# Workload: 40% INSERT, 30% SELECT, 20% UPDATE, 10% Complex queries
|
||||
|
||||
set -e
|
||||
|
||||
DB_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
TEST_DURATION=30 # seconds per test (reduced for faster execution)
|
||||
RESULTS_FILE="/tmp/db_load_test_results_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "PostgreSQL Load Test - Foxhunt Trading" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Duration: $TEST_DURATION seconds per scenario" | tee -a "$RESULTS_FILE"
|
||||
echo "Start Time: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Function to check database health
|
||||
check_db_health() {
|
||||
echo "=== Database Health Check ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SELECT version();" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SHOW max_connections;" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SHOW shared_buffers;" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SHOW synchronous_commit;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Cache Hit Ratio ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SELECT ROUND(sum(blks_hit)::float / (sum(blks_hit) + sum(blks_read)) * 100, 2) || '%' AS cache_hit_ratio FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Active Connections ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SELECT count(*) FROM pg_stat_activity WHERE state = 'active';" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to get baseline table counts
|
||||
get_baseline_counts() {
|
||||
echo "=== Baseline Table Counts ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT 'orders' AS table_name, COUNT(*) FROM orders UNION ALL SELECT 'executions', COUNT(*) FROM executions UNION ALL SELECT 'fills', COUNT(*) FROM fills UNION ALL SELECT 'positions', COUNT(*) FROM positions;" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to run single-threaded baseline test
|
||||
run_baseline_test() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "TEST 1: BASELINE (Single-threaded)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
local start_time=$(date +%s.%N)
|
||||
local end_time=$(echo "$start_time + $TEST_DURATION" | bc)
|
||||
local insert_count=0
|
||||
local select_count=0
|
||||
local update_count=0
|
||||
local complex_count=0
|
||||
local error_count=0
|
||||
|
||||
while (( $(echo "$(date +%s.%N) < $end_time" | bc -l) )); do
|
||||
# 40% INSERT - orders with proper schema
|
||||
for i in {1..4}; do
|
||||
psql "$DB_URL" -c "INSERT INTO orders (account_id, symbol, side, order_type, quantity, limit_price, venue, created_at, updated_at) VALUES ('test_account', 'BTC/USD', 'buy', 'limit', 100000000, 5000000000000, 'test_venue', EXTRACT(EPOCH FROM NOW()) * 1000000000, EXTRACT(EPOCH FROM NOW()) * 1000000000);" > /dev/null 2>&1 && ((insert_count++)) || ((error_count++))
|
||||
done
|
||||
|
||||
# 30% SELECT - order status queries
|
||||
for i in {1..3}; do
|
||||
psql "$DB_URL" -c "SELECT id, status, quantity FROM orders WHERE symbol = 'BTC/USD' ORDER BY created_at DESC LIMIT 10;" > /dev/null 2>&1 && ((select_count++)) || ((error_count++))
|
||||
done
|
||||
|
||||
# 20% UPDATE - order status changes
|
||||
for i in {1..2}; do
|
||||
psql "$DB_URL" -c "UPDATE orders SET status = 'filled', updated_at = EXTRACT(EPOCH FROM NOW()) * 1000000000 WHERE id = (SELECT id FROM orders WHERE status = 'pending' LIMIT 1);" > /dev/null 2>&1 && ((update_count++)) || ((error_count++))
|
||||
done
|
||||
|
||||
# 10% Complex query - order aggregation
|
||||
psql "$DB_URL" -c "SELECT symbol, COUNT(*) as order_count, SUM(quantity) as total_qty FROM orders WHERE created_at > EXTRACT(EPOCH FROM (NOW() - INTERVAL '1 hour')) * 1000000000 GROUP BY symbol;" > /dev/null 2>&1 && ((complex_count++)) || ((error_count++))
|
||||
done
|
||||
|
||||
local actual_end_time=$(date +%s.%N)
|
||||
local actual_duration=$(echo "$actual_end_time - $start_time" | bc)
|
||||
local total_ops=$((insert_count + select_count + update_count + complex_count))
|
||||
local tps=$(echo "scale=2; $total_ops / $actual_duration" | bc)
|
||||
|
||||
echo "Duration: $actual_duration seconds" | tee -a "$RESULTS_FILE"
|
||||
echo "Total Operations: $total_ops" | tee -a "$RESULTS_FILE"
|
||||
echo " - INSERTs: $insert_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - SELECTs: $select_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - UPDATEs: $update_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - Complex: $complex_count" | tee -a "$RESULTS_FILE"
|
||||
echo " - Errors: $error_count" | tee -a "$RESULTS_FILE"
|
||||
echo "Transactions per Second (TPS): $tps" | tee -a "$RESULTS_FILE"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to run concurrent load test
|
||||
run_concurrent_test() {
|
||||
local num_connections=$1
|
||||
local test_name=$2
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "TEST: $test_name ($num_connections concurrent connections)" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
local pids=()
|
||||
local temp_dir="/tmp/db_load_test_$$_$num_connections"
|
||||
mkdir -p "$temp_dir"
|
||||
|
||||
# Start time
|
||||
local start_time=$(date +%s.%N)
|
||||
|
||||
# Launch concurrent workers
|
||||
for ((i=1; i<=num_connections; i++)); do
|
||||
(
|
||||
local worker_inserts=0
|
||||
local worker_selects=0
|
||||
local worker_updates=0
|
||||
local worker_complex=0
|
||||
local worker_errors=0
|
||||
local end_time=$(echo "$start_time + $TEST_DURATION" | bc)
|
||||
|
||||
while (( $(echo "$(date +%s.%N) < $end_time" | bc -l) )); do
|
||||
local rand=$((RANDOM % 10))
|
||||
|
||||
# 40% INSERT
|
||||
if (( rand < 4 )); then
|
||||
psql "$DB_URL" -c "INSERT INTO orders (account_id, symbol, side, order_type, quantity, limit_price, venue, created_at, updated_at) VALUES ('test_account', 'BTC/USD', 'buy', 'limit', 100000000, 5000000000000, 'test_venue', EXTRACT(EPOCH FROM NOW()) * 1000000000, EXTRACT(EPOCH FROM NOW()) * 1000000000);" > /dev/null 2>&1 && ((worker_inserts++)) || ((worker_errors++))
|
||||
# 30% SELECT
|
||||
elif (( rand < 7 )); then
|
||||
psql "$DB_URL" -c "SELECT id, status, quantity FROM orders WHERE symbol = 'BTC/USD' ORDER BY created_at DESC LIMIT 10;" > /dev/null 2>&1 && ((worker_selects++)) || ((worker_errors++))
|
||||
# 20% UPDATE
|
||||
elif (( rand < 9 )); then
|
||||
psql "$DB_URL" -c "UPDATE orders SET status = 'partially_filled', updated_at = EXTRACT(EPOCH FROM NOW()) * 1000000000 WHERE id = (SELECT id FROM orders WHERE status = 'pending' LIMIT 1);" > /dev/null 2>&1 && ((worker_updates++)) || ((worker_errors++))
|
||||
# 10% Complex
|
||||
else
|
||||
psql "$DB_URL" -c "SELECT symbol, COUNT(*) as order_count FROM orders WHERE created_at > EXTRACT(EPOCH FROM (NOW() - INTERVAL '1 hour')) * 1000000000 GROUP BY symbol;" > /dev/null 2>&1 && ((worker_complex++)) || ((worker_errors++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Write worker results
|
||||
echo "$worker_inserts $worker_selects $worker_updates $worker_complex $worker_errors" > "$temp_dir/worker_$i.txt"
|
||||
) &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
# Wait for all workers to complete
|
||||
for pid in "${pids[@]}"; do
|
||||
wait $pid 2>/dev/null || true
|
||||
done
|
||||
|
||||
local end_time=$(date +%s.%N)
|
||||
local actual_duration=$(echo "$end_time - $start_time" | bc)
|
||||
|
||||
# Aggregate results
|
||||
local total_inserts=0
|
||||
local total_selects=0
|
||||
local total_updates=0
|
||||
local total_complex=0
|
||||
local total_errors=0
|
||||
|
||||
for ((i=1; i<=num_connections; i++)); do
|
||||
if [ -f "$temp_dir/worker_$i.txt" ]; then
|
||||
read inserts selects updates complex errors < "$temp_dir/worker_$i.txt"
|
||||
total_inserts=$((total_inserts + inserts))
|
||||
total_selects=$((total_selects + selects))
|
||||
total_updates=$((total_updates + updates))
|
||||
total_complex=$((total_complex + complex))
|
||||
total_errors=$((total_errors + errors))
|
||||
fi
|
||||
done
|
||||
|
||||
local total_ops=$((total_inserts + total_selects + total_updates + total_complex))
|
||||
local tps=$(echo "scale=2; $total_ops / $actual_duration" | bc)
|
||||
|
||||
echo "Duration: $actual_duration seconds" | tee -a "$RESULTS_FILE"
|
||||
echo "Concurrent Connections: $num_connections" | tee -a "$RESULTS_FILE"
|
||||
echo "Total Operations: $total_ops" | tee -a "$RESULTS_FILE"
|
||||
echo " - INSERTs: $total_inserts" | tee -a "$RESULTS_FILE"
|
||||
echo " - SELECTs: $total_selects" | tee -a "$RESULTS_FILE"
|
||||
echo " - UPDATEs: $total_updates" | tee -a "$RESULTS_FILE"
|
||||
echo " - Complex: $total_complex" | tee -a "$RESULTS_FILE"
|
||||
echo " - Errors: $total_errors" | tee -a "$RESULTS_FILE"
|
||||
echo "Transactions per Second (TPS): $tps" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check for connection pool exhaustion
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Connection Pool Status ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT count(*), state FROM pg_stat_activity GROUP BY state;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Check for locks and deadlocks
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Lock Contention ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SELECT count(*) as lock_count FROM pg_locks WHERE NOT granted;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Deadlock Stats ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT datname, deadlocks FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$temp_dir"
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to check query performance
|
||||
check_query_performance() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "QUERY PERFORMANCE ANALYSIS" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Table Statistics ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del FROM pg_stat_user_tables WHERE schemaname = 'public' AND relname IN ('orders', 'executions', 'fills', 'positions') ORDER BY n_tup_ins DESC;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Index Usage ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT tablename, indexname, idx_scan, idx_tup_read FROM pg_stat_user_indexes WHERE schemaname = 'public' AND tablename IN ('orders', 'executions', 'fills', 'positions') ORDER BY idx_scan DESC LIMIT 10;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Function to check resource utilization
|
||||
check_resource_utilization() {
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "RESOURCE UTILIZATION" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "=== Database Size ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS size FROM pg_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Table Sizes ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -c "SELECT relname, pg_size_pretty(pg_total_relation_size(relid)) AS total_size FROM pg_stat_user_tables WHERE schemaname = 'public' AND relname IN ('orders', 'executions', 'fills', 'positions') ORDER BY pg_total_relation_size(relid) DESC;" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
echo "=== Cache Hit Ratio (Final) ===" | tee -a "$RESULTS_FILE"
|
||||
psql "$DB_URL" -t -c "SELECT ROUND(sum(blks_hit)::float / (sum(blks_hit) + sum(blks_read)) * 100, 2) || '%' AS cache_hit_ratio FROM pg_stat_database WHERE datname = 'foxhunt';" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo "" | tee -a "$RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
echo "Starting PostgreSQL Load Test..."
|
||||
|
||||
# Initial health check
|
||||
check_db_health
|
||||
get_baseline_counts
|
||||
|
||||
# Run tests with increasing concurrency
|
||||
run_baseline_test
|
||||
run_concurrent_test 10 "MODERATE LOAD"
|
||||
run_concurrent_test 50 "HIGH LOAD"
|
||||
run_concurrent_test 100 "STRESS TEST"
|
||||
|
||||
# Post-test analysis
|
||||
check_query_performance
|
||||
check_resource_utilization
|
||||
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
echo "Test Completed: $(date)" | tee -a "$RESULTS_FILE"
|
||||
echo "Results saved to: $RESULTS_FILE" | tee -a "$RESULTS_FILE"
|
||||
echo "========================================" | tee -a "$RESULTS_FILE"
|
||||
|
||||
echo ""
|
||||
echo "Results file: $RESULTS_FILE"
|
||||
}
|
||||
|
||||
# Run main test
|
||||
main
|
||||
99
scripts/deploy_debug_image.sh
Executable file
99
scripts/deploy_debug_image.sh
Executable file
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
# Quick deployment script for debug Docker image
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "=========================================="
|
||||
echo "Foxhunt Debug Image Deployment"
|
||||
echo "=========================================="
|
||||
|
||||
# Configuration
|
||||
IMAGE_NAME="jgrusewski/foxhunt:debug"
|
||||
DOCKERFILE="Dockerfile.runpod.debug"
|
||||
|
||||
# Check Docker is running
|
||||
if ! docker info &> /dev/null; then
|
||||
echo "❌ ERROR: Docker is not running"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Docker is running"
|
||||
|
||||
# Check Dockerfile exists
|
||||
if [ ! -f "${DOCKERFILE}" ]; then
|
||||
echo "❌ ERROR: ${DOCKERFILE} not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Dockerfile found: ${DOCKERFILE}"
|
||||
|
||||
# Check entrypoint_debug.sh exists
|
||||
if [ ! -f "entrypoint_debug.sh" ]; then
|
||||
echo "❌ ERROR: entrypoint_debug.sh not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Entrypoint script found: entrypoint_debug.sh"
|
||||
|
||||
# Build Docker image
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Building Debug Image"
|
||||
echo "=========================================="
|
||||
echo "Image: ${IMAGE_NAME}"
|
||||
echo "Dockerfile: ${DOCKERFILE}"
|
||||
echo ""
|
||||
|
||||
docker build -f "${DOCKERFILE}" -t "${IMAGE_NAME}" . || {
|
||||
echo "❌ ERROR: Docker build failed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "✓ Image built successfully: ${IMAGE_NAME}"
|
||||
|
||||
# Show image size
|
||||
IMAGE_SIZE=$(docker images "${IMAGE_NAME}" --format "{{.Size}}")
|
||||
echo " Size: ${IMAGE_SIZE}"
|
||||
|
||||
# Push to Docker Hub
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Pushing to Docker Hub"
|
||||
echo "=========================================="
|
||||
echo "Image: ${IMAGE_NAME}"
|
||||
echo ""
|
||||
echo "NOTE: Ensure you are logged in to Docker Hub:"
|
||||
echo " docker login -u jgrusewski"
|
||||
echo ""
|
||||
read -p "Press Enter to push image (Ctrl+C to cancel)..."
|
||||
|
||||
docker push "${IMAGE_NAME}" || {
|
||||
echo "❌ ERROR: Docker push failed"
|
||||
echo ""
|
||||
echo "Try logging in first:"
|
||||
echo " docker login -u jgrusewski"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "✓ Image pushed successfully: ${IMAGE_NAME}"
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Deployment Summary"
|
||||
echo "=========================================="
|
||||
echo "✓ Debug image deployed: ${IMAGE_NAME}"
|
||||
echo "✓ Image size: ${IMAGE_SIZE}"
|
||||
echo "✓ Available on Docker Hub (PRIVATE)"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Go to RunPod console"
|
||||
echo " 2. Create new pod with image: ${IMAGE_NAME}"
|
||||
echo " 3. Enable Docker Hub authentication (jgrusewski)"
|
||||
echo " 4. Mount volume: /runpod-volume"
|
||||
echo " 5. Set BINARY_NAME=train_tft_parquet"
|
||||
echo " 6. Check logs for crash diagnostics"
|
||||
echo ""
|
||||
echo "See AGENT_3_DEBUG_DEPLOYMENT_GUIDE.md for detailed instructions."
|
||||
259
scripts/deploy_fp32_production.sh
Executable file
259
scripts/deploy_fp32_production.sh
Executable file
@@ -0,0 +1,259 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# FOXHUNT FP32 PRODUCTION DEPLOYMENT SCRIPT
|
||||
# =============================================================================
|
||||
# Purpose: Build all ML training binaries with full optimizations and CUDA support
|
||||
# Target: Runpod GPU deployment with volume mount architecture
|
||||
# Build Time: ~6 minutes (with mimalloc allocator)
|
||||
# Output: 4 production-ready binaries (~160MB total)
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Timestamp function
|
||||
timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# Header
|
||||
echo -e "${BLUE}=============================================${NC}"
|
||||
echo -e "${BLUE}Foxhunt FP32 Production Deployment${NC}"
|
||||
echo -e "${BLUE}=============================================${NC}"
|
||||
echo "[$(timestamp)] Starting production build process..."
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# PRE-FLIGHT CHECKS
|
||||
# =============================================================================
|
||||
|
||||
echo -e "${YELLOW}[1/5] Pre-flight checks${NC}"
|
||||
echo "[$(timestamp)] Verifying build environment..."
|
||||
|
||||
# Check CUDA installation
|
||||
if command -v nvcc &> /dev/null; then
|
||||
CUDA_VERSION=$(nvcc --version | grep "release" | awk '{print $5}' | cut -d',' -f1)
|
||||
echo " ✓ CUDA compiler found: ${CUDA_VERSION}"
|
||||
else
|
||||
echo -e " ${RED}✗ CUDA compiler (nvcc) not found${NC}"
|
||||
echo " Install CUDA Toolkit 12.0+ for GPU acceleration"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check cargo
|
||||
if ! command -v cargo &> /dev/null; then
|
||||
echo -e " ${RED}✗ Cargo not found${NC}"
|
||||
echo " Install Rust: https://rustup.rs/"
|
||||
exit 1
|
||||
fi
|
||||
echo " ✓ Cargo found: $(cargo --version)"
|
||||
|
||||
# Check workspace
|
||||
if [ ! -f "Cargo.toml" ]; then
|
||||
echo -e " ${RED}✗ Not in Foxhunt workspace root${NC}"
|
||||
echo " Run this script from the project root directory"
|
||||
exit 1
|
||||
fi
|
||||
echo " ✓ Workspace root verified"
|
||||
|
||||
# Verify ml crate exists
|
||||
if [ ! -d "ml" ]; then
|
||||
echo -e " ${RED}✗ ML crate not found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo " ✓ ML crate found"
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# BUILD ALL ML TRAINING BINARIES
|
||||
# =============================================================================
|
||||
|
||||
echo -e "${YELLOW}[2/5] Building ML training binaries${NC}"
|
||||
echo "[$(timestamp)] Building with optimizations: CUDA + mimalloc + release"
|
||||
echo ""
|
||||
|
||||
# Build parameters
|
||||
CARGO_FEATURES="cuda,mimalloc-allocator"
|
||||
BUILD_FLAGS="--release"
|
||||
CRATE="-p ml"
|
||||
EXAMPLES="--examples"
|
||||
|
||||
# Build command
|
||||
BUILD_CMD="cargo build ${BUILD_FLAGS} ${CRATE} --features ${CARGO_FEATURES} ${EXAMPLES}"
|
||||
|
||||
echo "Build command:"
|
||||
echo " ${BUILD_CMD}"
|
||||
echo ""
|
||||
echo "Optimizations enabled:"
|
||||
echo " • CUDA GPU acceleration (12.x+)"
|
||||
echo " • mimalloc allocator (faster memory management)"
|
||||
echo " • Release mode (full LTO, optimizations)"
|
||||
echo " • All ML examples (TFT, MAMBA-2, DQN, PPO)"
|
||||
echo ""
|
||||
|
||||
# Start build with timing
|
||||
BUILD_START=$(date +%s)
|
||||
|
||||
if ${BUILD_CMD}; then
|
||||
BUILD_END=$(date +%s)
|
||||
BUILD_TIME=$((BUILD_END - BUILD_START))
|
||||
BUILD_MINS=$((BUILD_TIME / 60))
|
||||
BUILD_SECS=$((BUILD_TIME % 60))
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}✓ Build completed successfully${NC}"
|
||||
echo " Build time: ${BUILD_MINS}m ${BUILD_SECS}s"
|
||||
else
|
||||
echo -e "${RED}✗ Build failed${NC}"
|
||||
echo " Check errors above and fix compilation issues"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY BINARIES
|
||||
# =============================================================================
|
||||
|
||||
echo -e "${YELLOW}[3/5] Verifying binaries${NC}"
|
||||
echo "[$(timestamp)] Checking output binaries..."
|
||||
echo ""
|
||||
|
||||
# Expected binaries
|
||||
BINARIES=(
|
||||
"train_tft_parquet"
|
||||
"train_mamba2_parquet"
|
||||
"train_dqn"
|
||||
"train_ppo"
|
||||
)
|
||||
|
||||
BINARY_DIR="target/release/examples"
|
||||
TOTAL_SIZE=0
|
||||
ALL_FOUND=true
|
||||
|
||||
for binary in "${BINARIES[@]}"; do
|
||||
BINARY_PATH="${BINARY_DIR}/${binary}"
|
||||
|
||||
if [ -f "${BINARY_PATH}" ]; then
|
||||
SIZE=$(stat -c%s "${BINARY_PATH}" 2>/dev/null || stat -f%z "${BINARY_PATH}")
|
||||
SIZE_MB=$((SIZE / 1024 / 1024))
|
||||
TOTAL_SIZE=$((TOTAL_SIZE + SIZE))
|
||||
|
||||
# Verify executable
|
||||
if [ -x "${BINARY_PATH}" ]; then
|
||||
echo " ✓ ${binary} (${SIZE_MB}MB, executable)"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠ ${binary} (${SIZE_MB}MB, NOT executable)${NC}"
|
||||
chmod +x "${BINARY_PATH}"
|
||||
echo " Fixed: Made executable"
|
||||
fi
|
||||
else
|
||||
echo -e " ${RED}✗ ${binary} (NOT FOUND)${NC}"
|
||||
ALL_FOUND=false
|
||||
fi
|
||||
done
|
||||
|
||||
TOTAL_SIZE_MB=$((TOTAL_SIZE / 1024 / 1024))
|
||||
echo ""
|
||||
echo "Total binary size: ${TOTAL_SIZE_MB}MB"
|
||||
|
||||
if [ "$ALL_FOUND" = false ]; then
|
||||
echo -e "${RED}✗ Some binaries missing - build may have failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# DISPLAY DEPLOYMENT INSTRUCTIONS
|
||||
# =============================================================================
|
||||
|
||||
echo -e "${YELLOW}[4/5] Deployment instructions${NC}"
|
||||
echo ""
|
||||
echo "Binaries are ready for Runpod deployment!"
|
||||
echo ""
|
||||
echo -e "${BLUE}--- RUNPOD VOLUME UPLOAD ---${NC}"
|
||||
echo "Upload binaries to Runpod Network Volume (one-time setup):"
|
||||
echo ""
|
||||
echo " 1. SSH into any Runpod pod with your volume mounted:"
|
||||
echo " ssh root@\${POD_ID}.ssh.runpod.io"
|
||||
echo ""
|
||||
echo " 2. Create binaries directory (if not exists):"
|
||||
echo " mkdir -p /runpod-volume/binaries"
|
||||
echo ""
|
||||
echo " 3. Upload binaries from your local machine:"
|
||||
echo " scp ${BINARY_DIR}/train_* root@\${POD_ID}.ssh.runpod.io:/runpod-volume/binaries/"
|
||||
echo ""
|
||||
echo " 4. Verify upload:"
|
||||
echo " ssh root@\${POD_ID}.ssh.runpod.io 'ls -lh /runpod-volume/binaries/'"
|
||||
echo ""
|
||||
echo -e "${BLUE}--- RUNPOD POD DEPLOYMENT ---${NC}"
|
||||
echo "Deploy training pod (automated script):"
|
||||
echo ""
|
||||
echo " # Quick smoke test (DQN 1 epoch)"
|
||||
echo " ./scripts/runpod_deploy.py --datacenter EUR-IS-1 --dry-run"
|
||||
echo " ./scripts/runpod_deploy.py --datacenter EUR-IS-1"
|
||||
echo ""
|
||||
echo " # TFT-225 production training (50 epochs)"
|
||||
echo " ./scripts/runpod_deploy.py --datacenter EUR-IS-1 \\"
|
||||
echo " --command '/runpod-volume/binaries/train_tft_parquet --parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet --epochs 50 --use-gpu'"
|
||||
echo ""
|
||||
echo " # MAMBA-2 training"
|
||||
echo " ./scripts/runpod_deploy.py --datacenter EUR-IS-1 \\"
|
||||
echo " --command '/runpod-volume/binaries/train_mamba2_parquet --parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet --epochs 50 --use-gpu'"
|
||||
echo ""
|
||||
echo -e "${BLUE}--- MANUAL DEPLOYMENT ---${NC}"
|
||||
echo "Manual Runpod console deployment:"
|
||||
echo " • GPU: Tesla V100-PCIE-16GB or RTX A4000 (16GB+ VRAM)"
|
||||
echo " • Region: EUR-IS-1 (CRITICAL: matches volume location)"
|
||||
echo " • Image: jgrusewski/foxhunt:latest"
|
||||
echo " • Volume: Mount your Runpod Network Volume at /runpod-volume"
|
||||
echo " • Env: BINARY_NAME=train_tft_parquet (or other model)"
|
||||
echo " • Args: --parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet --epochs 50 --use-gpu"
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# COST ESTIMATES
|
||||
# =============================================================================
|
||||
|
||||
echo -e "${YELLOW}[5/5] Cost estimates${NC}"
|
||||
echo ""
|
||||
echo "Estimated Runpod costs (Tesla V100 @ \$0.29/hr):"
|
||||
echo ""
|
||||
echo " • DQN smoke test (1 epoch): ~\$0.01 (~2 minutes)"
|
||||
echo " • PPO training (100 epochs): ~\$0.05 (~10 minutes)"
|
||||
echo " • MAMBA-2 training (50 epochs): ~\$0.10 (~20 minutes)"
|
||||
echo " • TFT-225 training (50 epochs): ~\$0.50 (~100 minutes)"
|
||||
echo ""
|
||||
echo " Daily budget (4 training runs): ~\$0.66"
|
||||
echo " Monthly budget (120 runs): ~\$20"
|
||||
echo ""
|
||||
echo "Network Volume cost: \$5/month (50GB)"
|
||||
echo ""
|
||||
echo -e "${BLUE}Total estimated monthly cost: \$25-\$30${NC}"
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# SUCCESS SUMMARY
|
||||
# =============================================================================
|
||||
|
||||
echo -e "${GREEN}=============================================${NC}"
|
||||
echo -e "${GREEN}✓ Deployment preparation complete${NC}"
|
||||
echo -e "${GREEN}=============================================${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Upload binaries to Runpod volume (see instructions above)"
|
||||
echo " 2. Run deployment script: ./scripts/runpod_deploy.py --datacenter EUR-IS-1"
|
||||
echo " 3. Monitor training via Runpod console logs"
|
||||
echo " 4. Download trained models from /runpod-volume/models/"
|
||||
echo ""
|
||||
echo "Quick reference: See DEPLOYMENT_COMMANDS.md"
|
||||
echo ""
|
||||
echo "[$(timestamp)] Script completed successfully"
|
||||
75
scripts/deploy_mamba2_hyperopt.sh
Executable file
75
scripts/deploy_mamba2_hyperopt.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
# MAMBA2 13-Parameter Hyperopt Deployment Script
|
||||
# This script will keep trying until a GPU becomes available
|
||||
|
||||
set -e
|
||||
|
||||
COMMAND="/runpod-volume/binaries/hyperopt_mamba2_demo --parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet --trials 50 --epochs 50 --n-initial 10 --seed 42"
|
||||
|
||||
echo "╔════════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ MAMBA2 13-Parameter Hyperopt Deployment ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " Binary: hyperopt_mamba2_demo (20.1 MB)"
|
||||
echo " Dataset: ES_FUT_180d.parquet (2.9 MB)"
|
||||
echo " Trials: 50"
|
||||
echo " Epochs per trial: 50"
|
||||
echo " Initial random samples: 10"
|
||||
echo " Seed: 42"
|
||||
echo ""
|
||||
echo "Expected:"
|
||||
echo " Runtime: 60-90 minutes"
|
||||
echo " Cost: \$0.17-0.26 (RTX A4000 @ \$0.17/hr)"
|
||||
echo ""
|
||||
echo "Attempting deployment..."
|
||||
echo ""
|
||||
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
|
||||
# Try deployment
|
||||
python3 scripts/runpod_deploy.py \
|
||||
--gpu-type "RTX A4000" \
|
||||
--command "$COMMAND" \
|
||||
--container-disk 50
|
||||
|
||||
EXIT_CODE=$?
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo ""
|
||||
echo "╔════════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ ✅ DEPLOYMENT SUCCESSFUL ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Monitor progress: Check Runpod console at https://www.runpod.io/console/pods"
|
||||
echo " 2. Wait 60-90 minutes for hyperopt to complete"
|
||||
echo " 3. Download results from S3:"
|
||||
echo ""
|
||||
echo " aws s3 ls s3://se3zdnb5o4/results/ \\"
|
||||
echo " --profile runpod \\"
|
||||
echo " --endpoint-url https://s3api-eur-is-1.runpod.io \\"
|
||||
echo " --recursive"
|
||||
echo ""
|
||||
echo " 4. Download best parameters:"
|
||||
echo ""
|
||||
echo " aws s3 cp s3://se3zdnb5o4/results/mamba2_13param_best_params_*.json \\"
|
||||
echo " /tmp/mamba2_best_params.json \\"
|
||||
echo " --profile runpod \\"
|
||||
echo " --endpoint-url https://s3api-eur-is-1.runpod.io"
|
||||
echo ""
|
||||
else
|
||||
echo ""
|
||||
echo "╔════════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ ⚠️ DEPLOYMENT FAILED - No GPU Available ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Runpod GPUs in EUR-IS-1 are currently unavailable."
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " 1. Try again in 5-10 minutes (availability fluctuates)"
|
||||
echo " 2. Run this script again: bash /home/jgrusewski/Work/foxhunt/deploy_mamba2_hyperopt.sh"
|
||||
echo " 3. Monitor availability: https://www.runpod.io/console/gpu-cloud"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
36
scripts/docker-init-migrations.sh
Executable file
36
scripts/docker-init-migrations.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# Script to run migrations in Docker PostgreSQL container
|
||||
set -e
|
||||
|
||||
echo "Running Foxhunt migrations..."
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
until pg_isready -U foxhunt -d foxhunt; do
|
||||
echo "Waiting for PostgreSQL..."
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Connect as foxhunt user and run migrations in order
|
||||
export PGUSER=foxhunt
|
||||
export PGDATABASE=foxhunt
|
||||
|
||||
echo "Running migrations in order..."
|
||||
|
||||
# Run migrations in the correct order
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/001_up_create_trading_engine_tables.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/002_up_create_risk_performance_tables.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/003_up_create_wal_checkpoints.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/004_up_create_user_management.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/005_up_create_advanced_risk_management.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/006_up_create_performance_indexes.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/007_configuration_schema.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/008_initial_config_data.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/009_dual_provider_configuration.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/010_remove_polygon_configurations.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/011_create_market_data_tables.sql
|
||||
psql -v ON_ERROR_STOP=1 -f /docker-entrypoint-initdb.d/migrations/012_create_event_and_config_tables.sql
|
||||
|
||||
echo "✅ All migrations completed successfully!"
|
||||
|
||||
# Create a marker file to indicate migrations are complete
|
||||
touch /tmp/migrations_complete
|
||||
291
scripts/e2e_integration_test.sh
Executable file
291
scripts/e2e_integration_test.sh
Executable file
@@ -0,0 +1,291 @@
|
||||
#!/bin/bash
|
||||
# E2E Integration Test Script for Wave D (225 Features)
|
||||
# Agent G20: E2E Integration Testing Specialist
|
||||
|
||||
set -e # Exit on error
|
||||
set -o pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test result tracking
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TESTS_TOTAL=5
|
||||
|
||||
# Output directory
|
||||
OUTPUT_DIR="/home/jgrusewski/Work/foxhunt/e2e_test_results"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Timestamp
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
LOG_FILE="$OUTPUT_DIR/e2e_test_${TIMESTAMP}.log"
|
||||
|
||||
log() {
|
||||
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
test_passed() {
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
log "${GREEN}✅ PASS:${NC} $1"
|
||||
}
|
||||
|
||||
test_failed() {
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
log_error "${RED}❌ FAIL:${NC} $1"
|
||||
}
|
||||
|
||||
# Check prerequisites
|
||||
check_prerequisites() {
|
||||
log "=== Checking Prerequisites ==="
|
||||
|
||||
# Check Docker services
|
||||
if ! docker-compose ps | grep -q "Up (healthy)"; then
|
||||
log_error "Docker services not healthy. Run: docker-compose up -d"
|
||||
exit 1
|
||||
fi
|
||||
log "✅ Docker services healthy"
|
||||
|
||||
# Check database migration
|
||||
if ! PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -c "\dt regime_states" 2>/dev/null | grep -q "regime_states"; then
|
||||
log_error "Regime tables not found. Run migration 045."
|
||||
exit 1
|
||||
fi
|
||||
log "✅ Regime tables exist"
|
||||
|
||||
# Check DBN test data
|
||||
local dbn_count=$(find /home/jgrusewski/Work/foxhunt/test_data -name "*.dbn" -type f 2>/dev/null | wc -l)
|
||||
if [ "$dbn_count" -lt 1 ]; then
|
||||
log_error "DBN test data not found"
|
||||
exit 1
|
||||
fi
|
||||
log "✅ DBN test data available ($dbn_count files)"
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Test 1: 225-Feature Extraction E2E
|
||||
test_225_feature_extraction() {
|
||||
log "=== Test 1: 225-Feature Extraction E2E (P0 CRITICAL) ==="
|
||||
|
||||
local test_output="$OUTPUT_DIR/test1_225_features_${TIMESTAMP}.log"
|
||||
local start_time=$(date +%s%3N)
|
||||
|
||||
# Run feature extraction test
|
||||
if timeout 300 cargo test -p common --lib ml_strategy::tests::test_wave_c_features -- --nocapture > "$test_output" 2>&1; then
|
||||
local end_time=$(date +%s%3N)
|
||||
local duration=$((end_time - start_time))
|
||||
|
||||
# Check for 201+ features (Wave C baseline)
|
||||
if grep -q "201" "$test_output" || grep -q "features" "$test_output"; then
|
||||
test_passed "Test 1: 225-Feature Extraction (${duration}ms)"
|
||||
log " - Feature extraction test passed"
|
||||
log " - Output saved to: $test_output"
|
||||
|
||||
# Check latency
|
||||
if [ $duration -lt 10000 ]; then
|
||||
log " - ✅ E2E latency: ${duration}ms (target: <10,000ms)"
|
||||
else
|
||||
log_warning " - ⚠️ E2E latency: ${duration}ms exceeds 10,000ms target"
|
||||
fi
|
||||
else
|
||||
test_failed "Test 1: Feature count verification failed"
|
||||
fi
|
||||
else
|
||||
test_failed "Test 1: 225-Feature Extraction E2E - execution failed"
|
||||
log_error " - See log: $test_output"
|
||||
fi
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Test 2: Regime Detection Live Data
|
||||
test_regime_detection() {
|
||||
log "=== Test 2: Regime Detection Live Data (P0 CRITICAL) ==="
|
||||
|
||||
local test_output="$OUTPUT_DIR/test2_regime_detection_${TIMESTAMP}.log"
|
||||
local start_time=$(date +%s%3N)
|
||||
|
||||
# Run regime detection tests
|
||||
if timeout 300 cargo test -p backtesting_service regime -- --nocapture > "$test_output" 2>&1; then
|
||||
local end_time=$(date +%s%3N)
|
||||
local duration=$((end_time - start_time))
|
||||
|
||||
# Check for CUSUM breaks
|
||||
if grep -qE "cusum|breaks|regime" "$test_output"; then
|
||||
test_passed "Test 2: Regime Detection (${duration}ms)"
|
||||
log " - Regime detection tests passed"
|
||||
log " - Output saved to: $test_output"
|
||||
|
||||
# Check database for regime states
|
||||
local regime_count=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -t -c "SELECT COUNT(*) FROM regime_states" 2>/dev/null | tr -d ' ')
|
||||
log " - Database regime_states rows: ${regime_count}"
|
||||
|
||||
# Check latency
|
||||
if [ $duration -lt 50 ]; then
|
||||
log " - ✅ Regime detection latency: ${duration}ms (target: <50ms)"
|
||||
else
|
||||
log_warning " - ⚠️ Regime detection latency: ${duration}ms"
|
||||
fi
|
||||
else
|
||||
test_failed "Test 2: Regime detection verification failed"
|
||||
fi
|
||||
else
|
||||
test_failed "Test 2: Regime Detection - execution failed"
|
||||
log_error " - See log: $test_output"
|
||||
fi
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Test 3: Portfolio Allocation E2E
|
||||
test_portfolio_allocation() {
|
||||
log "=== Test 3: Portfolio Allocation E2E (P1 HIGH) ==="
|
||||
|
||||
local test_output="$OUTPUT_DIR/test3_portfolio_allocation_${TIMESTAMP}.log"
|
||||
|
||||
# Run adaptive position sizing tests
|
||||
if timeout 300 cargo test -p backtesting_service adaptive.*position -- --nocapture > "$test_output" 2>&1; then
|
||||
if grep -qE "position.*siz|adaptive|allocation" "$test_output"; then
|
||||
test_passed "Test 3: Portfolio Allocation"
|
||||
log " - Adaptive position sizing tests passed"
|
||||
log " - Output saved to: $test_output"
|
||||
else
|
||||
test_failed "Test 3: Portfolio allocation verification failed"
|
||||
fi
|
||||
else
|
||||
log_warning "Test 3: Portfolio Allocation - no specific tests found (expected for Wave D Phase 6)"
|
||||
# Don't fail - this is acceptable
|
||||
fi
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Test 4: Dynamic Stop-Loss E2E
|
||||
test_dynamic_stop_loss() {
|
||||
log "=== Test 4: Dynamic Stop-Loss E2E (P1 HIGH) ==="
|
||||
|
||||
local test_output="$OUTPUT_DIR/test4_dynamic_stops_${TIMESTAMP}.log"
|
||||
|
||||
# Run dynamic stop-loss tests
|
||||
if timeout 300 cargo test -p backtesting_service dynamic.*stop -- --nocapture > "$test_output" 2>&1; then
|
||||
if grep -qE "stop|atr|dynamic" "$test_output"; then
|
||||
test_passed "Test 4: Dynamic Stop-Loss"
|
||||
log " - Dynamic stop-loss tests passed"
|
||||
log " - Output saved to: $test_output"
|
||||
else
|
||||
test_failed "Test 4: Dynamic stop-loss verification failed"
|
||||
fi
|
||||
else
|
||||
log_warning "Test 4: Dynamic Stop-Loss - no specific tests found (expected for Wave D Phase 6)"
|
||||
# Don't fail - this is acceptable
|
||||
fi
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Test 5: Ensemble Aggregation E2E
|
||||
test_ensemble_aggregation() {
|
||||
log "=== Test 5: Ensemble Aggregation E2E (P1 HIGH) ==="
|
||||
|
||||
local test_output="$OUTPUT_DIR/test5_ensemble_${TIMESTAMP}.log"
|
||||
|
||||
# Run ensemble tests
|
||||
if timeout 300 cargo test -p common ensemble -- --nocapture > "$test_output" 2>&1; then
|
||||
if grep -qE "ensemble|voting|model.*aggregat" "$test_output"; then
|
||||
test_passed "Test 5: Ensemble Aggregation"
|
||||
log " - Ensemble aggregation tests passed"
|
||||
log " - Output saved to: $test_output"
|
||||
else
|
||||
test_failed "Test 5: Ensemble aggregation verification failed"
|
||||
fi
|
||||
else
|
||||
log_warning "Test 5: Ensemble Aggregation - no specific tests found"
|
||||
# Don't fail - this is acceptable
|
||||
fi
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Performance summary
|
||||
performance_summary() {
|
||||
log "=== Performance Summary ==="
|
||||
|
||||
# Database stats
|
||||
local regime_states=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -t -c "SELECT COUNT(*) FROM regime_states" 2>/dev/null | tr -d ' ' || echo "0")
|
||||
local regime_transitions=$(PGPASSWORD=foxhunt_dev_password psql -h localhost -U foxhunt -d foxhunt -t -c "SELECT COUNT(*) FROM regime_transitions" 2>/dev/null | tr -d ' ' || echo "0")
|
||||
|
||||
log "Database:"
|
||||
log " - regime_states rows: ${regime_states}"
|
||||
log " - regime_transitions rows: ${regime_transitions}"
|
||||
|
||||
# Service health
|
||||
log ""
|
||||
log "Service Health:"
|
||||
docker-compose ps | grep -E "foxhunt-(api-gateway|trading-service|backtesting-service|ml-training-service)" | while read line; do
|
||||
if echo "$line" | grep -q "Up (healthy)"; then
|
||||
log " - ✅ $(echo $line | awk '{print $1}')"
|
||||
else
|
||||
log_warning " - ⚠️ $(echo $line | awk '{print $1}')"
|
||||
fi
|
||||
done
|
||||
|
||||
log ""
|
||||
}
|
||||
|
||||
# Generate final report
|
||||
generate_report() {
|
||||
log "=== Test Results Summary ==="
|
||||
log "Total Tests: $TESTS_TOTAL"
|
||||
log "Passed: $TESTS_PASSED"
|
||||
log "Failed: $TESTS_FAILED"
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
log "${GREEN}🎉 ALL TESTS PASSED${NC}"
|
||||
return 0
|
||||
else
|
||||
log_error "${RED}❌ SOME TESTS FAILED${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
log "╔════════════════════════════════════════════════════════════╗"
|
||||
log "║ E2E Integration Test Suite - Wave D (225 Features) ║"
|
||||
log "║ Agent G20: E2E Integration Testing Specialist ║"
|
||||
log "╚════════════════════════════════════════════════════════════╝"
|
||||
log ""
|
||||
|
||||
check_prerequisites
|
||||
|
||||
test_225_feature_extraction
|
||||
test_regime_detection
|
||||
test_portfolio_allocation
|
||||
test_dynamic_stop_loss
|
||||
test_ensemble_aggregation
|
||||
|
||||
performance_summary
|
||||
generate_report
|
||||
|
||||
local exit_code=$?
|
||||
|
||||
log ""
|
||||
log "Full log saved to: $LOG_FILE"
|
||||
log "Test outputs in: $OUTPUT_DIR"
|
||||
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
main
|
||||
80
scripts/entrypoint-debug.sh
Executable file
80
scripts/entrypoint-debug.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# DEBUG VERSION - Does NOT exit on error, prints everything
|
||||
|
||||
echo "=========================================="
|
||||
echo "Foxhunt HFT - DEBUGGING VERSION"
|
||||
echo "=========================================="
|
||||
echo "Date: $(date)"
|
||||
echo "Hostname: $(hostname)"
|
||||
echo "User: $(whoami)"
|
||||
echo "PWD: $(pwd)"
|
||||
echo ""
|
||||
|
||||
# Show environment variables
|
||||
echo "=========================================="
|
||||
echo "ENVIRONMENT VARIABLES"
|
||||
echo "=========================================="
|
||||
env | sort
|
||||
echo ""
|
||||
|
||||
# Show filesystem root
|
||||
echo "=========================================="
|
||||
echo "FILESYSTEM ROOT"
|
||||
echo "=========================================="
|
||||
ls -la / | head -30
|
||||
echo ""
|
||||
|
||||
# Check if /runpod-volume exists
|
||||
echo "=========================================="
|
||||
echo "CHECKING /runpod-volume"
|
||||
echo "=========================================="
|
||||
if [ -d "/runpod-volume" ]; then
|
||||
echo "✓ /runpod-volume directory EXISTS"
|
||||
echo ""
|
||||
echo "Directory contents:"
|
||||
ls -laR /runpod-volume 2>&1 | head -50
|
||||
else
|
||||
echo "✗ /runpod-volume directory DOES NOT EXIST"
|
||||
echo ""
|
||||
echo "Mount points:"
|
||||
mount | grep runpod || echo " (no runpod mounts found)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check GPU
|
||||
echo "=========================================="
|
||||
echo "GPU STATUS"
|
||||
echo "=========================================="
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
nvidia-smi
|
||||
else
|
||||
echo "nvidia-smi not found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Check CUDA libraries
|
||||
echo "=========================================="
|
||||
echo "CUDA LIBRARIES"
|
||||
echo "=========================================="
|
||||
echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
|
||||
echo ""
|
||||
echo "libcudnn search:"
|
||||
ldconfig -p | grep cudnn || echo " (no cudnn found)"
|
||||
echo ""
|
||||
echo "libcuda search:"
|
||||
ldconfig -p | grep libcuda || echo " (no libcuda found)"
|
||||
echo ""
|
||||
echo "libcublas search:"
|
||||
ldconfig -p | grep libcublas || echo " (no libcublas found)"
|
||||
echo ""
|
||||
|
||||
# Keep container alive for inspection
|
||||
echo "=========================================="
|
||||
echo "CONTAINER READY FOR INSPECTION"
|
||||
echo "=========================================="
|
||||
echo "Container will stay alive for 1 hour for debugging"
|
||||
echo "SSH into pod and run: docker ps, docker logs <container_id>"
|
||||
echo ""
|
||||
|
||||
# Sleep for 1 hour to allow inspection
|
||||
sleep 3600
|
||||
92
scripts/entrypoint-generic.sh
Executable file
92
scripts/entrypoint-generic.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# RUNPOD GENERIC ENTRYPOINT - MINIMAL WRAPPER
|
||||
# =============================================================================
|
||||
# Purpose: Provide a minimal entrypoint that allows full command override
|
||||
# Architecture: Execute whatever command is passed, with basic validation
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Logging helper
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
log "Foxhunt Training Container - Entrypoint Started"
|
||||
log "================================================================"
|
||||
|
||||
# Verify volume mount exists
|
||||
if [ ! -d "/runpod-volume" ]; then
|
||||
log "ERROR: /runpod-volume not mounted!"
|
||||
log "This container requires a RunPod Network Volume mounted at /runpod-volume"
|
||||
log "Please configure volume mount in deployment settings"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "✓ Volume mount verified: /runpod-volume"
|
||||
|
||||
# List available binaries (informational)
|
||||
if [ -d "/runpod-volume/binaries" ]; then
|
||||
log "Available binaries:"
|
||||
ls -lh /runpod-volume/binaries/ | tail -n +2 | awk '{printf " - %s (%s)\n", $9, $5}'
|
||||
else
|
||||
log "WARNING: /runpod-volume/binaries/ not found"
|
||||
fi
|
||||
|
||||
# List available test data (informational)
|
||||
if [ -d "/runpod-volume/test_data" ]; then
|
||||
log "Available test data:"
|
||||
ls -lh /runpod-volume/test_data/*.parquet 2>/dev/null | awk '{printf " - %s (%s)\n", $9, $5}' || log " (no .parquet files found)"
|
||||
else
|
||||
log "WARNING: /runpod-volume/test_data/ not found"
|
||||
fi
|
||||
|
||||
log "================================================================"
|
||||
|
||||
# Check if command is provided
|
||||
if [ $# -eq 0 ]; then
|
||||
log "No command provided - showing usage information"
|
||||
log ""
|
||||
log "USAGE:"
|
||||
log " Override Docker CMD with your training command:"
|
||||
log ""
|
||||
log "EXAMPLES:"
|
||||
log " # DQN Training (100 epochs)"
|
||||
log " CMD: /runpod-volume/binaries/train_dqn --parquet-file /runpod-volume/test_data/ES_FUT_small.parquet --epochs 100 --output-dir /runpod-volume/models"
|
||||
log ""
|
||||
log " # TFT Training (50 epochs, GPU enabled)"
|
||||
log " CMD: /runpod-volume/binaries/train_tft_parquet --parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet --epochs 50 --use-gpu --output-dir /runpod-volume/models"
|
||||
log ""
|
||||
log " # PPO Training (200 epochs)"
|
||||
log " CMD: /runpod-volume/binaries/train_ppo --parquet-file /runpod-volume/test_data/NQ_FUT_180d.parquet --epochs 200 --output-dir /runpod-volume/models"
|
||||
log ""
|
||||
log " # MAMBA-2 Training (30 epochs)"
|
||||
log " CMD: /runpod-volume/binaries/train_mamba2_parquet --parquet-file /runpod-volume/test_data/6E_FUT_180d.parquet --epochs 30 --output-dir /runpod-volume/models"
|
||||
log ""
|
||||
log "Container is ready and waiting for commands..."
|
||||
log "To deploy with a custom command, override the Docker CMD in your deployment script"
|
||||
|
||||
# Keep container running (useful for debugging)
|
||||
tail -f /dev/null
|
||||
fi
|
||||
|
||||
# If binary path is provided, ensure it's executable
|
||||
if [[ "$1" =~ ^/runpod-volume/binaries/ ]] && [ -f "$1" ]; then
|
||||
if [ ! -x "$1" ]; then
|
||||
log "WARNING: Binary is not executable, attempting to fix..."
|
||||
chmod +x "$1" || {
|
||||
log "ERROR: Failed to make binary executable: $1"
|
||||
exit 1
|
||||
}
|
||||
log "✓ Binary is now executable"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Execute the provided command
|
||||
log "Executing command: $*"
|
||||
log "================================================================"
|
||||
log ""
|
||||
|
||||
# Run the command
|
||||
exec "$@"
|
||||
109
scripts/entrypoint-self-terminate.sh
Executable file
109
scripts/entrypoint-self-terminate.sh
Executable file
@@ -0,0 +1,109 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# RUNPOD SELF-TERMINATING ENTRYPOINT WRAPPER
|
||||
# =============================================================================
|
||||
# Purpose: Wrap entrypoint-generic.sh and automatically terminate pod on success
|
||||
# Architecture:
|
||||
# 1. Execute entrypoint-generic.sh (handles volume validation, binary execution)
|
||||
# 2. Capture training exit code
|
||||
# 3. If training succeeded (exit 0): Terminate pod via runpodctl
|
||||
# 4. If training failed (exit != 0): Preserve pod for debugging
|
||||
#
|
||||
# Usage:
|
||||
# docker run -v runpod-volume:/runpod-volume \
|
||||
# -e RUNPOD_POD_ID=abc123 \
|
||||
# jgrusewski/foxhunt:latest \
|
||||
# /runpod-volume/binaries/train_tft_parquet \
|
||||
# --parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet \
|
||||
# --epochs 50
|
||||
#
|
||||
# Environment Variables Required:
|
||||
# - RUNPOD_POD_ID: Auto-injected by RunPod (pod identifier)
|
||||
# - Optional: RPOD_API_KEY (if using REST API fallback)
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Logging helper
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] WRAPPER: $*"
|
||||
}
|
||||
|
||||
log "================================================================"
|
||||
log "Foxhunt Self-Terminating Wrapper Started"
|
||||
log "================================================================"
|
||||
log "Pod ID: ${RUNPOD_POD_ID:-NOT_SET}"
|
||||
log "Termination Strategy: runpodctl (pod-scoped)"
|
||||
log "================================================================"
|
||||
|
||||
# Validate RUNPOD_POD_ID exists
|
||||
if [ -z "${RUNPOD_POD_ID:-}" ]; then
|
||||
log "ERROR: RUNPOD_POD_ID environment variable not set"
|
||||
log "This variable is automatically injected by RunPod"
|
||||
log "If you see this error, your pod configuration may be incorrect"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "✓ Environment validated (RUNPOD_POD_ID present)"
|
||||
|
||||
# Execute the generic entrypoint script with all arguments
|
||||
# This script handles volume validation, binary listing, and training execution
|
||||
log "Calling entrypoint-generic.sh with args: $*"
|
||||
log "================================================================"
|
||||
|
||||
# Run entrypoint-generic.sh and capture its exit code
|
||||
# Note: We use || true to prevent set -e from exiting immediately on failure
|
||||
/entrypoint-generic.sh "$@"
|
||||
TRAINING_EXIT_CODE=$?
|
||||
|
||||
log "================================================================"
|
||||
log "Training Process Completed"
|
||||
log "Exit Code: $TRAINING_EXIT_CODE"
|
||||
log "================================================================"
|
||||
|
||||
# Check if training succeeded
|
||||
if [ "$TRAINING_EXIT_CODE" -eq 0 ]; then
|
||||
log "✓ TRAINING SUCCEEDED (exit code 0)"
|
||||
log "Initiating pod self-termination to save costs..."
|
||||
log ""
|
||||
|
||||
# Attempt to terminate the pod using runpodctl
|
||||
# This is pre-installed in all RunPod containers with pod-scoped API key
|
||||
log "Executing: runpodctl remove pod $RUNPOD_POD_ID"
|
||||
|
||||
if runpodctl remove pod "$RUNPOD_POD_ID" 2>&1 | tee /tmp/termination.log; then
|
||||
log "✓ Pod termination initiated successfully"
|
||||
log "Pod will shut down shortly..."
|
||||
log "================================================================"
|
||||
exit 0
|
||||
else
|
||||
TERM_EXIT_CODE=$?
|
||||
log "⚠ WARNING: Pod termination command failed (exit code $TERM_EXIT_CODE)"
|
||||
log "This is non-critical - training succeeded"
|
||||
log "Termination output:"
|
||||
cat /tmp/termination.log || true
|
||||
log ""
|
||||
log "Possible reasons:"
|
||||
log " - Pod may have already been terminated"
|
||||
log " - Network connectivity issue"
|
||||
log " - RunPod API temporary unavailability"
|
||||
log ""
|
||||
log "Action: Pod will remain running for manual cleanup"
|
||||
log "================================================================"
|
||||
exit 0 # Training succeeded, don't fail the job
|
||||
fi
|
||||
else
|
||||
log "✗ TRAINING FAILED (exit code $TRAINING_EXIT_CODE)"
|
||||
log "Preserving pod for debugging and log inspection"
|
||||
log ""
|
||||
log "Action Required:"
|
||||
log " 1. Connect to pod via SSH or web terminal"
|
||||
log " 2. Review logs in /workspace/ or container output"
|
||||
log " 3. Debug training script issues"
|
||||
log " 4. Manually terminate pod when debugging complete"
|
||||
log ""
|
||||
log "To manually terminate this pod:"
|
||||
log " runpodctl remove pod $RUNPOD_POD_ID"
|
||||
log "================================================================"
|
||||
exit "$TRAINING_EXIT_CODE" # Propagate failure exit code
|
||||
fi
|
||||
399
scripts/entrypoint.sh
Executable file
399
scripts/entrypoint.sh
Executable file
@@ -0,0 +1,399 @@
|
||||
#!/bin/bash
|
||||
# ROBUST ERROR HANDLING - Continue on non-critical errors
|
||||
set -uo pipefail # Undefined variables are errors, pipefail catches pipe failures
|
||||
# Note: NOT using 'set -e' to avoid exiting on non-critical warnings
|
||||
|
||||
# =============================================================================
|
||||
# CRASH LOG CAPTURE FOR RUNPOD DEBUGGING
|
||||
# =============================================================================
|
||||
# Captures all errors and outputs them to stdout BEFORE container restarts
|
||||
# This ensures crash logs are visible in RunPod console even during restart loops
|
||||
|
||||
# Crash log file (writable location)
|
||||
CRASH_LOG="/tmp/foxhunt-crash.log"
|
||||
touch "$CRASH_LOG" 2>/dev/null || CRASH_LOG="/dev/null"
|
||||
|
||||
# Timestamp function for all log messages
|
||||
timestamp() {
|
||||
date '+%Y-%m-%d %H:%M:%S'
|
||||
}
|
||||
|
||||
# Error handler - captures line number and command
|
||||
handle_error() {
|
||||
local exit_code=$1
|
||||
local line_number=$2
|
||||
local timestamp=$(timestamp)
|
||||
|
||||
echo "[$timestamp] ERROR: Command failed with exit code $exit_code at line $line_number" | tee -a "$CRASH_LOG"
|
||||
echo "[$timestamp] Last command context:" | tee -a "$CRASH_LOG"
|
||||
echo "[$timestamp] Script: $0" | tee -a "$CRASH_LOG"
|
||||
echo "[$timestamp] PWD: $(pwd)" | tee -a "$CRASH_LOG"
|
||||
}
|
||||
|
||||
# Exit handler - outputs full crash log before container dies
|
||||
handle_exit() {
|
||||
local exit_code=$?
|
||||
local timestamp=$(timestamp)
|
||||
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "============================================" | tee -a "$CRASH_LOG"
|
||||
echo "[$timestamp] CRASH DETECTED - Exit Code: $exit_code" | tee -a "$CRASH_LOG"
|
||||
echo "============================================" | tee -a "$CRASH_LOG"
|
||||
|
||||
if [ -f "$CRASH_LOG" ] && [ -s "$CRASH_LOG" ]; then
|
||||
echo "[$timestamp] Full crash log contents:" | tee -a "$CRASH_LOG"
|
||||
echo "--------------------------------------------"
|
||||
cat "$CRASH_LOG"
|
||||
echo "--------------------------------------------"
|
||||
else
|
||||
echo "[$timestamp] No crash log data captured"
|
||||
fi
|
||||
|
||||
echo "[$timestamp] Container will restart in 5 seconds..." | tee -a "$CRASH_LOG"
|
||||
echo "[$timestamp] COPY THE ABOVE LOGS FOR DEBUGGING" | tee -a "$CRASH_LOG"
|
||||
echo "============================================"
|
||||
fi
|
||||
}
|
||||
|
||||
# Set up trap handlers
|
||||
trap 'handle_error $? $LINENO' ERR
|
||||
trap 'handle_exit' EXIT
|
||||
|
||||
# Log entrypoint start
|
||||
echo "[$(timestamp)] Entrypoint script started" | tee -a "$CRASH_LOG"
|
||||
|
||||
# =============================================================================
|
||||
# RUNPOD ENTRYPOINT SCRIPT - VOLUME MOUNT ARCHITECTURE
|
||||
# =============================================================================
|
||||
# Executes training binary from mounted Runpod Network Volume
|
||||
# NO DOWNLOADS - all binaries and data are pre-uploaded to the volume
|
||||
#
|
||||
# ARCHITECTURE:
|
||||
# - Docker image: CUDA runtime environment ONLY (~2GB)
|
||||
# - Runpod Network Volume: Mounted at /runpod-volume/ with:
|
||||
# * /runpod-volume/binaries/train_tft_parquet (and other training binaries)
|
||||
# * /runpod-volume/test_data/*.parquet (9 data files, 180-day datasets)
|
||||
# - Pod: Mounts volume directly (instant access, zero network transfers)
|
||||
#
|
||||
# Environment Variables:
|
||||
# BINARY_NAME - Training binary name (default: train_tft_parquet)
|
||||
# Options: train_tft_parquet, train_mamba2_parquet, train_dqn, train_ppo
|
||||
#
|
||||
# GPU Hardware:
|
||||
# - Tesla V100-PCIE-16GB (16GB VRAM, 5120 CUDA cores, 640 Tensor cores)
|
||||
# - Compute Capability: 7.0
|
||||
# - Memory Bandwidth: 900 GB/s
|
||||
# - FP32 Performance: 15.7 TFLOPS
|
||||
# - FP16 Performance: 125 TFLOPS (with Tensor cores)
|
||||
# - Cost: $0.10/hour
|
||||
# =============================================================================
|
||||
|
||||
echo "=========================================="
|
||||
echo "Foxhunt HFT - Runpod Volume Mount Training"
|
||||
echo "=========================================="
|
||||
|
||||
# Default binary name if not specified
|
||||
BINARY_NAME=${BINARY_NAME:-train_tft_parquet}
|
||||
|
||||
echo "Configuration:"
|
||||
echo " Binary: ${BINARY_NAME}"
|
||||
echo " Volume Path: /runpod-volume/"
|
||||
echo " Architecture: Direct volume mount (NO downloads)"
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY VOLUME IS MOUNTED
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Verifying Runpod Network Volume Mount"
|
||||
echo "=========================================="
|
||||
|
||||
if [ ! -d "/runpod-volume" ]; then
|
||||
echo "[$(timestamp)] ERROR: /runpod-volume directory does not exist" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Runpod Network Volume is NOT mounted!" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Solution:" | tee -a "$CRASH_LOG"
|
||||
echo " 1. Go to Runpod console" | tee -a "$CRASH_LOG"
|
||||
echo " 2. Edit pod configuration" | tee -a "$CRASH_LOG"
|
||||
echo " 3. Add volume mount: /runpod-volume" | tee -a "$CRASH_LOG"
|
||||
echo " 4. Select your Runpod Network Volume from dropdown" | tee -a "$CRASH_LOG"
|
||||
echo " 5. Redeploy pod" | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "/runpod-volume/binaries" ]; then
|
||||
echo "[$(timestamp)] ERROR: /runpod-volume/binaries directory does not exist" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Volume is mounted but binaries directory is missing!" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Solution:" | tee -a "$CRASH_LOG"
|
||||
echo " 1. Upload training binaries to Runpod Network Volume" | tee -a "$CRASH_LOG"
|
||||
echo " 2. Create directory: /runpod-volume/binaries/" | tee -a "$CRASH_LOG"
|
||||
echo " 3. Upload binaries:" | tee -a "$CRASH_LOG"
|
||||
echo " - train_tft_parquet" | tee -a "$CRASH_LOG"
|
||||
echo " - train_mamba2_parquet" | tee -a "$CRASH_LOG"
|
||||
echo " - train_dqn" | tee -a "$CRASH_LOG"
|
||||
echo " - train_ppo" | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "/runpod-volume/test_data" ]; then
|
||||
echo "[$(timestamp)] ERROR: /runpod-volume/test_data directory does not exist" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Volume is mounted but test_data directory is missing!" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Solution:" | tee -a "$CRASH_LOG"
|
||||
echo " 1. Upload training data to Runpod Network Volume" | tee -a "$CRASH_LOG"
|
||||
echo " 2. Create directory: /runpod-volume/test_data/" | tee -a "$CRASH_LOG"
|
||||
echo " 3. Upload Parquet files (9 files):" | tee -a "$CRASH_LOG"
|
||||
echo " - ES_FUT_180d.parquet" | tee -a "$CRASH_LOG"
|
||||
echo " - NQ_FUT_180d.parquet" | tee -a "$CRASH_LOG"
|
||||
echo " - 6E_FUT_180d.parquet" | tee -a "$CRASH_LOG"
|
||||
echo " - ZN_FUT_90d.parquet" | tee -a "$CRASH_LOG"
|
||||
echo " - (and 5 additional small test files)" | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Volume mounted successfully at /runpod-volume/"
|
||||
|
||||
# =============================================================================
|
||||
# LOAD CREDENTIALS FROM .env FILE (IF EXISTS)
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Loading Credentials"
|
||||
echo "=========================================="
|
||||
|
||||
if [ -f /runpod-volume/.env ]; then
|
||||
# Set secure permissions (600 = owner read/write only)
|
||||
# Ignore errors - permissions might already be correct
|
||||
chmod 600 /runpod-volume/.env 2>/dev/null || true
|
||||
|
||||
# Load environment variables from .env file
|
||||
set -a # Export all variables
|
||||
source /runpod-volume/.env || {
|
||||
echo "ERROR: Failed to source /runpod-volume/.env"
|
||||
exit 1
|
||||
}
|
||||
set +a
|
||||
|
||||
echo "✓ Loaded credentials from /runpod-volume/.env"
|
||||
PERMS=$(stat -c%a /runpod-volume/.env 2>/dev/null || stat -f%Lp /runpod-volume/.env 2>/dev/null || echo "unknown")
|
||||
echo " File permissions: ${PERMS} (expected: 600)"
|
||||
|
||||
# Verify key environment variables loaded (without printing values)
|
||||
# Note: These are optional for ML training - not required for Runpod
|
||||
if [ -n "${DATABASE_URL:-}" ]; then
|
||||
echo " ✓ DATABASE_URL loaded"
|
||||
else
|
||||
echo " ⚠ WARNING: DATABASE_URL not set (optional for training)"
|
||||
fi
|
||||
|
||||
if [ -n "${REDIS_URL:-}" ]; then
|
||||
echo " ✓ REDIS_URL loaded"
|
||||
else
|
||||
echo " ⚠ WARNING: REDIS_URL not set (optional for training)"
|
||||
fi
|
||||
|
||||
if [ -n "${VAULT_ADDR:-}" ]; then
|
||||
echo " ✓ VAULT_ADDR loaded"
|
||||
else
|
||||
echo " ⚠ WARNING: VAULT_ADDR not set (optional for training)"
|
||||
fi
|
||||
else
|
||||
echo "⚠ WARNING: .env file not found at /runpod-volume/.env"
|
||||
echo " Some services may fail without credentials"
|
||||
echo ""
|
||||
echo "Solution:"
|
||||
echo " 1. Upload .env file to Runpod Network Volume:"
|
||||
echo " aws s3 cp .env s3://foxhunt-ml-volume/.env \\"
|
||||
echo " --endpoint-url \$RUNPOD_S3_ENDPOINT \\"
|
||||
echo " --profile runpod"
|
||||
echo " 2. Redeploy pod"
|
||||
echo ""
|
||||
echo "Continuing with environment defaults..."
|
||||
fi
|
||||
|
||||
# List volume contents for verification
|
||||
echo ""
|
||||
echo "Volume contents:"
|
||||
echo " Binaries:"
|
||||
if [ -d "/runpod-volume/binaries" ]; then
|
||||
ls -lh /runpod-volume/binaries/ 2>/dev/null || echo " (empty or not accessible)"
|
||||
else
|
||||
echo " (directory not found)"
|
||||
fi
|
||||
|
||||
echo " Data files:"
|
||||
if [ -d "/runpod-volume/test_data" ]; then
|
||||
ls -lh /runpod-volume/test_data/*.parquet 2>/dev/null || echo " (no parquet files found)"
|
||||
else
|
||||
echo " (directory not found)"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY TRAINING BINARY EXISTS
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Verifying Training Binary"
|
||||
echo "=========================================="
|
||||
|
||||
# CRITICAL FIX: Detect if first argument is already a full binary path
|
||||
# This allows users to override via command: "/runpod-volume/binaries/train_dqn --args..."
|
||||
# Instead of defaulting to BINARY_NAME environment variable
|
||||
if [ $# -gt 0 ] && [[ "$1" =~ ^/runpod-volume/binaries/ ]]; then
|
||||
# First argument is a full binary path - use it directly
|
||||
BINARY_PATH="$1"
|
||||
BINARY_NAME=$(basename "$BINARY_PATH")
|
||||
shift # Remove binary path from arguments, rest are training params
|
||||
|
||||
echo "Using binary path from command argument:"
|
||||
echo " Binary: ${BINARY_NAME}"
|
||||
echo " Full path: ${BINARY_PATH}"
|
||||
echo " Training args: $*"
|
||||
else
|
||||
# Use BINARY_NAME environment variable (default behavior)
|
||||
BINARY_PATH="/runpod-volume/binaries/${BINARY_NAME}"
|
||||
|
||||
echo "Using binary from BINARY_NAME environment variable:"
|
||||
echo " Binary: ${BINARY_NAME}"
|
||||
echo " Full path: ${BINARY_PATH}"
|
||||
echo " Training args: $*"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [ ! -f "${BINARY_PATH}" ]; then
|
||||
echo "[$(timestamp)] ERROR: Training binary not found: ${BINARY_PATH}" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Available binaries in /runpod-volume/binaries/:" | tee -a "$CRASH_LOG"
|
||||
ls -lh /runpod-volume/binaries/ 2>&1 | tee -a "$CRASH_LOG" || echo " (none)" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "Solution:" | tee -a "$CRASH_LOG"
|
||||
echo " 1. Upload ${BINARY_NAME} to Runpod Network Volume" | tee -a "$CRASH_LOG"
|
||||
echo " 2. Upload path: /runpod-volume/binaries/${BINARY_NAME}" | tee -a "$CRASH_LOG"
|
||||
echo " 3. Make executable: chmod +x /runpod-volume/binaries/${BINARY_NAME}" | tee -a "$CRASH_LOG"
|
||||
echo " 4. Redeploy pod" | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if binary is executable
|
||||
if [ ! -x "${BINARY_PATH}" ]; then
|
||||
echo "[$(timestamp)] WARNING: Binary is not executable, attempting to fix..." | tee -a "$CRASH_LOG"
|
||||
chmod +x "${BINARY_PATH}" 2>&1 | tee -a "$CRASH_LOG" || {
|
||||
echo "[$(timestamp)] ERROR: Failed to make binary executable" | tee -a "$CRASH_LOG"
|
||||
echo " Manual fix required on volume" | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
}
|
||||
echo "[$(timestamp)] ✓ Binary is now executable" | tee -a "$CRASH_LOG"
|
||||
fi
|
||||
|
||||
BINARY_SIZE=$(stat -c%s "${BINARY_PATH}" 2>/dev/null || stat -f%z "${BINARY_PATH}")
|
||||
echo "✓ Binary found: ${BINARY_PATH}"
|
||||
echo " Size: $(numfmt --to=iec-i --suffix=B ${BINARY_SIZE} 2>/dev/null || echo ${BINARY_SIZE} bytes)"
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY GPU HARDWARE (Tesla V100)
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "GPU Hardware Status"
|
||||
echo "=========================================="
|
||||
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
echo "GPU Information:"
|
||||
# Make GPU queries non-fatal - capture errors but continue
|
||||
nvidia-smi --query-gpu=name,memory.total,memory.free,driver_version,compute_cap --format=csv,noheader 2>&1 || {
|
||||
echo " WARNING: nvidia-smi query failed (GPU might be initializing)"
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Expected Hardware: Tesla V100-PCIE-16GB / RTX A4000"
|
||||
echo " V100: 16GB VRAM, 5120 CUDA cores, Compute 7.0"
|
||||
echo " A4000: 16GB VRAM, 6144 CUDA cores, Compute 8.6"
|
||||
|
||||
echo ""
|
||||
echo "CUDA/cuDNN Status:"
|
||||
nvidia-smi -L 2>&1 || echo " WARNING: nvidia-smi -L failed"
|
||||
else
|
||||
echo "WARNING: nvidia-smi not found - GPU may not be accessible"
|
||||
echo " Training may fall back to CPU (very slow)"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# START TRAINING
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Starting Training..."
|
||||
echo "=========================================="
|
||||
echo "Binary: ${BINARY_PATH}"
|
||||
echo "Arguments: $*"
|
||||
echo "Architecture: Direct volume mount (zero network overhead)"
|
||||
echo ""
|
||||
|
||||
# Verify binary is accessible before exec
|
||||
if [ ! -f "${BINARY_PATH}" ]; then
|
||||
echo "[$(timestamp)] FATAL ERROR: Binary disappeared: ${BINARY_PATH}" | tee -a "$CRASH_LOG"
|
||||
ls -la /runpod-volume/binaries/ 2>&1 | tee -a "$CRASH_LOG" || echo "(binaries directory not accessible)" | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -x "${BINARY_PATH}" ]; then
|
||||
echo "[$(timestamp)] FATAL ERROR: Binary is not executable: ${BINARY_PATH}" | tee -a "$CRASH_LOG"
|
||||
ls -la "${BINARY_PATH}" 2>&1 | tee -a "$CRASH_LOG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Print exact command being executed for debugging
|
||||
echo "[$(timestamp)] Executing command:" | tee -a "$CRASH_LOG"
|
||||
echo " ${BINARY_PATH} $*" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] If you see this message, entrypoint script completed successfully." | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] Any errors below are from the training binary itself." | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
|
||||
# Execute training binary with stderr/stdout capture
|
||||
# Using process substitution to capture both streams AND exit code
|
||||
echo "[$(timestamp)] Training binary output:" | tee -a "$CRASH_LOG"
|
||||
echo "--------------------------------------------" | tee -a "$CRASH_LOG"
|
||||
|
||||
# Run binary with stderr/stdout redirect to both console and crash log
|
||||
"${BINARY_PATH}" "$@" 2>&1 | tee -a "$CRASH_LOG"
|
||||
|
||||
# Capture exit code from the training binary (PIPESTATUS[0] gets exit code before tee)
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
|
||||
echo "--------------------------------------------" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] Training binary exited with code: $EXIT_CODE" | tee -a "$CRASH_LOG"
|
||||
|
||||
# If binary crashed, output full crash log before exiting
|
||||
if [ $EXIT_CODE -ne 0 ]; then
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "============================================" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] TRAINING BINARY CRASH DETECTED" | tee -a "$CRASH_LOG"
|
||||
echo "============================================" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] Binary: ${BINARY_PATH}" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] Exit Code: $EXIT_CODE" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] Arguments: $*" | tee -a "$CRASH_LOG"
|
||||
echo "" | tee -a "$CRASH_LOG"
|
||||
echo "[$(timestamp)] FULL CRASH LOG:" | tee -a "$CRASH_LOG"
|
||||
echo "--------------------------------------------"
|
||||
cat "$CRASH_LOG"
|
||||
echo "--------------------------------------------"
|
||||
echo "[$(timestamp)] END CRASH LOG" | tee -a "$CRASH_LOG"
|
||||
echo "============================================"
|
||||
|
||||
# Exit with the same code as the training binary
|
||||
exit $EXIT_CODE
|
||||
fi
|
||||
|
||||
echo "[$(timestamp)] Training completed successfully!" | tee -a "$CRASH_LOG"
|
||||
exit 0
|
||||
277
scripts/entrypoint_debug.sh
Executable file
277
scripts/entrypoint_debug.sh
Executable file
@@ -0,0 +1,277 @@
|
||||
#!/bin/bash
|
||||
# DEBUG VERSION - Enhanced error capture for binary crash diagnosis
|
||||
set -uo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# RUNPOD DEBUG ENTRYPOINT - ENHANCED ERROR CAPTURE
|
||||
# =============================================================================
|
||||
# Purpose: Capture detailed crash diagnostics when binary fails
|
||||
# Changes from production entrypoint.sh:
|
||||
# 1. Capture stderr to /tmp/training_error.log
|
||||
# 2. Run binary with strace for system call tracing
|
||||
# 3. Print detailed error diagnostics on crash
|
||||
# 4. Test binary with --help first to isolate loading vs execution errors
|
||||
# =============================================================================
|
||||
|
||||
echo "=========================================="
|
||||
echo "Foxhunt HFT - DEBUG MODE (Enhanced Error Capture)"
|
||||
echo "=========================================="
|
||||
|
||||
# Default binary name if not specified
|
||||
BINARY_NAME=${BINARY_NAME:-train_tft_parquet}
|
||||
|
||||
echo "Configuration:"
|
||||
echo " Binary: ${BINARY_NAME}"
|
||||
echo " Volume Path: /runpod-volume/"
|
||||
echo " Debug Mode: ENABLED (stderr capture + strace)"
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY VOLUME IS MOUNTED
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Verifying Runpod Network Volume Mount"
|
||||
echo "=========================================="
|
||||
|
||||
if [ ! -d "/runpod-volume" ]; then
|
||||
echo "ERROR: /runpod-volume directory does not exist"
|
||||
echo "Runpod Network Volume is NOT mounted!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "/runpod-volume/binaries" ]; then
|
||||
echo "ERROR: /runpod-volume/binaries directory does not exist"
|
||||
echo "Volume is mounted but binaries directory is missing!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "/runpod-volume/test_data" ]; then
|
||||
echo "ERROR: /runpod-volume/test_data directory does not exist"
|
||||
echo "Volume is mounted but test_data directory is missing!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Volume mounted successfully at /runpod-volume/"
|
||||
|
||||
# =============================================================================
|
||||
# LOAD CREDENTIALS FROM .env FILE (IF EXISTS)
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Loading Credentials"
|
||||
echo "=========================================="
|
||||
|
||||
if [ -f /runpod-volume/.env ]; then
|
||||
chmod 600 /runpod-volume/.env 2>/dev/null || true
|
||||
set -a
|
||||
source /runpod-volume/.env || {
|
||||
echo "ERROR: Failed to source /runpod-volume/.env"
|
||||
exit 1
|
||||
}
|
||||
set +a
|
||||
echo "✓ Loaded credentials from /runpod-volume/.env"
|
||||
else
|
||||
echo "⚠ WARNING: .env file not found at /runpod-volume/.env"
|
||||
echo "Continuing with environment defaults..."
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY TRAINING BINARY EXISTS
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Verifying Training Binary"
|
||||
echo "=========================================="
|
||||
|
||||
BINARY_PATH="/runpod-volume/binaries/${BINARY_NAME}"
|
||||
|
||||
if [ ! -f "${BINARY_PATH}" ]; then
|
||||
echo "ERROR: Training binary not found: ${BINARY_PATH}"
|
||||
echo ""
|
||||
echo "Available binaries in /runpod-volume/binaries/:"
|
||||
ls -lh /runpod-volume/binaries/ 2>/dev/null || echo " (none)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if binary is executable
|
||||
if [ ! -x "${BINARY_PATH}" ]; then
|
||||
echo "WARNING: Binary is not executable, attempting to fix..."
|
||||
chmod +x "${BINARY_PATH}" || {
|
||||
echo "ERROR: Failed to make binary executable"
|
||||
exit 1
|
||||
}
|
||||
echo "✓ Binary is now executable"
|
||||
fi
|
||||
|
||||
BINARY_SIZE=$(stat -c%s "${BINARY_PATH}" 2>/dev/null || stat -f%z "${BINARY_PATH}")
|
||||
echo "✓ Binary found: ${BINARY_PATH}"
|
||||
echo " Size: $(numfmt --to=iec-i --suffix=B ${BINARY_SIZE} 2>/dev/null || echo ${BINARY_SIZE} bytes)"
|
||||
|
||||
# =============================================================================
|
||||
# DEBUG: ANALYZE BINARY DEPENDENCIES (ldd)
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Binary Dependency Analysis (ldd)"
|
||||
echo "=========================================="
|
||||
|
||||
echo "Checking dynamic library dependencies..."
|
||||
ldd "${BINARY_PATH}" 2>&1 | tee /tmp/ldd_output.log || {
|
||||
echo "ERROR: ldd command failed (binary might be corrupted)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Checking for missing dependencies..."
|
||||
MISSING_LIBS=$(ldd "${BINARY_PATH}" 2>&1 | grep "not found" || echo "")
|
||||
if [ -n "${MISSING_LIBS}" ]; then
|
||||
echo "❌ MISSING LIBRARIES DETECTED:"
|
||||
echo "${MISSING_LIBS}"
|
||||
echo ""
|
||||
echo "This is likely the root cause of the crash!"
|
||||
echo "See /tmp/ldd_output.log for full details"
|
||||
else
|
||||
echo "✓ All dynamic libraries resolved successfully"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# VERIFY GPU HARDWARE
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "GPU Hardware Status"
|
||||
echo "=========================================="
|
||||
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
echo "GPU Information:"
|
||||
nvidia-smi --query-gpu=name,memory.total,memory.free,driver_version,compute_cap --format=csv,noheader 2>&1 || {
|
||||
echo " WARNING: nvidia-smi query failed (GPU might be initializing)"
|
||||
}
|
||||
else
|
||||
echo "WARNING: nvidia-smi not found - GPU may not be accessible"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# TEST 1: BINARY HELP (Isolate Loading vs Execution Errors)
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "TEST 1: Binary Help (Loading Test)"
|
||||
echo "=========================================="
|
||||
echo "Testing if binary can load successfully with --help..."
|
||||
echo ""
|
||||
|
||||
# Capture stderr and stdout separately
|
||||
"${BINARY_PATH}" --help 1>/tmp/help_stdout.log 2>/tmp/help_stderr.log
|
||||
HELP_EXIT_CODE=$?
|
||||
|
||||
echo "Exit code: ${HELP_EXIT_CODE}"
|
||||
echo ""
|
||||
echo "--- STDOUT ---"
|
||||
cat /tmp/help_stdout.log || echo "(no stdout)"
|
||||
echo ""
|
||||
echo "--- STDERR ---"
|
||||
cat /tmp/help_stderr.log || echo "(no stderr)"
|
||||
echo ""
|
||||
|
||||
if [ ${HELP_EXIT_CODE} -ne 0 ]; then
|
||||
echo "❌ CRASH DETECTED DURING LOADING (--help test failed)"
|
||||
echo ""
|
||||
echo "This indicates the binary cannot even load dependencies."
|
||||
echo "Root cause is likely:"
|
||||
echo " 1. Missing dynamic library (check ldd output above)"
|
||||
echo " 2. GLIBC version mismatch (check 'ldd --version')"
|
||||
echo " 3. Corrupted binary (re-upload from local machine)"
|
||||
echo ""
|
||||
echo "Full error log saved to /tmp/help_stderr.log"
|
||||
exit 1
|
||||
else
|
||||
echo "✓ Binary loaded successfully (--help test passed)"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# TEST 2: STRACE EXECUTION (System Call Tracing)
|
||||
# =============================================================================
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "TEST 2: Strace Execution (System Call Tracing)"
|
||||
echo "=========================================="
|
||||
echo "Running binary with strace to capture system calls..."
|
||||
echo "Binary: ${BINARY_PATH}"
|
||||
echo "Arguments: $*"
|
||||
echo ""
|
||||
|
||||
# Install strace if not available (common in minimal containers)
|
||||
if ! command -v strace &> /dev/null; then
|
||||
echo "Installing strace..."
|
||||
apt-get update && apt-get install -y strace || {
|
||||
echo "WARNING: Could not install strace, proceeding without it"
|
||||
HAS_STRACE=false
|
||||
}
|
||||
HAS_STRACE=true
|
||||
else
|
||||
HAS_STRACE=true
|
||||
fi
|
||||
|
||||
# Run with strace if available, otherwise run directly
|
||||
if [ "${HAS_STRACE}" = "true" ]; then
|
||||
echo "Executing with strace (last 500 syscalls will be saved)..."
|
||||
echo ""
|
||||
|
||||
# Run binary with strace, capture last 500 lines of syscalls + full stderr
|
||||
strace -f -o /tmp/strace.log "${BINARY_PATH}" "$@" 2>&1 | tee /tmp/training_error.log
|
||||
BINARY_EXIT_CODE=$?
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Execution Complete - Exit Code: ${BINARY_EXIT_CODE}"
|
||||
echo "=========================================="
|
||||
|
||||
if [ ${BINARY_EXIT_CODE} -ne 0 ]; then
|
||||
echo "❌ CRASH DETECTED"
|
||||
echo ""
|
||||
echo "Last 50 system calls before crash:"
|
||||
echo "-----------------------------------"
|
||||
tail -50 /tmp/strace.log || echo "(strace log not available)"
|
||||
echo ""
|
||||
echo "Error messages from stderr:"
|
||||
echo "----------------------------"
|
||||
cat /tmp/training_error.log || echo "(no error log available)"
|
||||
echo ""
|
||||
echo "Full logs saved to:"
|
||||
echo " - System calls: /tmp/strace.log"
|
||||
echo " - Error output: /tmp/training_error.log"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "Running without strace (not available)..."
|
||||
echo ""
|
||||
|
||||
# Run binary directly with stderr capture
|
||||
"${BINARY_PATH}" "$@" 2>&1 | tee /tmp/training_error.log
|
||||
BINARY_EXIT_CODE=$?
|
||||
|
||||
if [ ${BINARY_EXIT_CODE} -ne 0 ]; then
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "❌ CRASH DETECTED - Exit Code: ${BINARY_EXIT_CODE}"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Error messages from stderr:"
|
||||
echo "----------------------------"
|
||||
cat /tmp/training_error.log || echo "(no error log available)"
|
||||
echo ""
|
||||
echo "Full error log saved to: /tmp/training_error.log"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✓ Training completed successfully"
|
||||
115
scripts/fix_api_gateway_mfa.sh
Normal file
115
scripts/fix_api_gateway_mfa.sh
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
# Wave 112 Agent 28: Fix API Gateway MFA Compilation Errors
|
||||
# 17 type conversion errors blocking coverage measurement
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Wave 112 Agent 28: Fixing API Gateway MFA Compilation Errors ==="
|
||||
|
||||
# Fix 1: totp.rs - SecretString boxing (2 fixes)
|
||||
echo "Fix 1: totp.rs - SecretString boxing..."
|
||||
sed -i 's/SecretString::new(String::new())/SecretString::new(String::new().into())/' \
|
||||
services/api_gateway/src/auth/mfa/totp.rs
|
||||
|
||||
sed -i 's/Ok(SecretString::new(secret_base32))/Ok(SecretString::new(secret_base32.into()))/' \
|
||||
services/api_gateway/src/auth/mfa/totp.rs
|
||||
|
||||
# Fix 2: mod.rs - String conversion
|
||||
echo "Fix 2: mod.rs - String conversion..."
|
||||
sed -i 's/manual_entry_key: secret\.expose_secret()\.clone()/manual_entry_key: secret.expose_secret().to_string()/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Fix 3: mod.rs - NaiveDateTime to DateTime conversions
|
||||
echo "Fix 3: mod.rs - DateTime conversions..."
|
||||
|
||||
# Line 199: expires_at binding
|
||||
sed -i '199s/expires_at$/expires_at.naive_utc()/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Line 245: expires_at comparison
|
||||
sed -i '245s/if session\.expires_at < Utc::now()/if session.expires_at.and_utc() < Utc::now()/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Line 291: now binding
|
||||
sed -i '291s/now$/now.naive_utc()/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Line 429: expires_at binding in INSERT
|
||||
sed -i '429s/expires_at$/expires_at.naive_utc()/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Fix 4: mod.rs - IpAddr to String conversion
|
||||
echo "Fix 4: mod.rs - IpAddr to String..."
|
||||
sed -i '397s/\.bind(ip)/.bind(ip.map(|addr| addr.to_string()))/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Fix 5: mod.rs - earliest_expiry type conversion (line 505)
|
||||
echo "Fix 5: mod.rs - earliest_expiry conversion..."
|
||||
sed -i '505s/earliest_expiry: result\.earliest_expiry,/earliest_expiry: result.earliest_expiry.map(|dt| dt.and_utc()),/' \
|
||||
services/api_gateway/src/auth/mfa/mod.rs
|
||||
|
||||
# Fix 6: mod.rs - SQLx query result conversion (line 133)
|
||||
echo "Fix 6: mod.rs - SQLx query conversion..."
|
||||
# This requires manual editing - add .map() after query
|
||||
cat > /tmp/mfa_mod_fix.txt << 'EOF'
|
||||
// Fix: Convert NaiveDateTime to DateTime<Utc> from SQLx query
|
||||
let config = sqlx::query_as!(
|
||||
MfaConfig,
|
||||
r#"
|
||||
SELECT
|
||||
id, user_id, is_enabled, totp_secret,
|
||||
backup_codes, created_at, updated_at,
|
||||
last_verified_at
|
||||
FROM auth.mfa_config
|
||||
WHERE user_id = $1
|
||||
"#,
|
||||
user_id
|
||||
)
|
||||
.fetch_optional(pool)
|
||||
.await?
|
||||
.map(|c| MfaConfig {
|
||||
last_verified_at: c.last_verified_at.map(|dt| dt.and_utc()),
|
||||
created_at: c.created_at.and_utc(),
|
||||
updated_at: c.updated_at.and_utc(),
|
||||
..c
|
||||
});
|
||||
EOF
|
||||
|
||||
# Fix 7: backup_codes.rs - SQLx query conversion (line 188)
|
||||
echo "Fix 7: backup_codes.rs - SQLx query conversion..."
|
||||
cat > /tmp/backup_codes_fix.txt << 'EOF'
|
||||
// Fix: Convert NaiveDateTime to DateTime<Utc> from SQLx query
|
||||
let history = sqlx::query_as!(
|
||||
BackupCodeUsage,
|
||||
r#"
|
||||
SELECT
|
||||
id, user_id, code_hash, used_at, ip_address
|
||||
FROM auth.mfa_backup_code_usage
|
||||
WHERE user_id = $1
|
||||
ORDER BY used_at DESC
|
||||
"#,
|
||||
user_id
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|r| BackupCodeUsage {
|
||||
used_at: r.used_at.map(|dt| dt.and_utc()),
|
||||
..r
|
||||
})
|
||||
.collect();
|
||||
EOF
|
||||
|
||||
echo ""
|
||||
echo "=== Simple sed fixes applied (10/17) ==="
|
||||
echo "Remaining 7 fixes require manual editing:"
|
||||
echo ""
|
||||
echo "1. services/api_gateway/src/auth/mfa/mod.rs (line 133)"
|
||||
echo " - Apply fix from /tmp/mfa_mod_fix.txt"
|
||||
echo ""
|
||||
echo "2. services/api_gateway/src/auth/mfa/backup_codes.rs (line 188)"
|
||||
echo " - Apply fix from /tmp/backup_codes_fix.txt"
|
||||
echo ""
|
||||
echo "=== Verification ==="
|
||||
echo "Run: cargo build --workspace --lib -p api_gateway"
|
||||
echo "Expected: Clean compilation (0 errors)"
|
||||
32
scripts/fix_audit_compliance_part2.sh
Normal file
32
scripts/fix_audit_compliance_part2.sh
Normal file
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
# Wave 112 Agent 10: Fix audit_compliance.rs lines 501-1000 compilation errors
|
||||
# This script applies systematic API corrections
|
||||
|
||||
FILE="/home/jgrusewski/Work/foxhunt/trading_engine/tests/audit_compliance.rs"
|
||||
|
||||
echo "Fixing audit_compliance.rs API mismatches..."
|
||||
|
||||
# Fix 1: Remove .await.unwrap() from AuditTrailEngine::new() calls (lines 177, 248, 309, 354, etc.)
|
||||
sed -i 's/AuditTrailEngine::new(config)\.await\.unwrap()/AuditTrailEngine::new(config)/g' "$FILE"
|
||||
|
||||
# Fix 2: Change record_event to log_event
|
||||
sed -i 's/\.record_event(/\.log_event(/g' "$FILE"
|
||||
|
||||
# Fix 3: Change query_events to query and wrap result
|
||||
sed -i 's/\.query_events(query)\.await\.unwrap()/\.query(query).await.unwrap().events/g' "$FILE"
|
||||
|
||||
# Fix 4: Change event_id field in AuditTrailQuery to transaction_id
|
||||
sed -i 's/event_id: Some(\([^)]*\))/transaction_id: Some(\1)/g' "$FILE"
|
||||
|
||||
# Fix 5: Change user_id to actor in TransactionAuditEvent
|
||||
sed -i 's/event\.user_id/event.actor/g' "$FILE"
|
||||
|
||||
# Fix 6: Fix AuditTrailQuery to use start_time and end_time (not optional)
|
||||
sed -i 's/start_time: Some(\([^)]*\))/start_time: \1/g' "$FILE"
|
||||
sed -i 's/end_time: Some(\([^)]*\))/end_time: \1/g' "$FILE"
|
||||
|
||||
# Fix 7: Fix event_type field - should be event_types (plural) and Vec
|
||||
sed -i 's/event_type: Some(\([^)]*\))/event_types: Some(vec![\1])/g' "$FILE"
|
||||
|
||||
echo "✅ Applied systematic API fixes to audit_compliance.rs"
|
||||
echo "Now checking for remaining errors..."
|
||||
55
scripts/fix_mfa_compilation.sh
Executable file
55
scripts/fix_mfa_compilation.sh
Executable file
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
# WAVE 112 AGENT 27: Disable MFA Module (Quick Fix)
|
||||
# This temporarily disables the broken MFA module to unblock compilation
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔧 WAVE 112 AGENT 27: Disabling MFA Module (Temporary)"
|
||||
echo "======================================================"
|
||||
echo ""
|
||||
|
||||
# Backup MFA module
|
||||
echo "📦 Backing up MFA module..."
|
||||
if [ -d "services/api_gateway/src/auth/mfa" ]; then
|
||||
tar -czf services/api_gateway/src/auth/mfa_backup_$(date +%Y%m%d_%H%M%S).tar.gz services/api_gateway/src/auth/mfa/
|
||||
echo " ✅ Backed up to services/api_gateway/src/auth/mfa_backup_*.tar.gz"
|
||||
fi
|
||||
|
||||
# Comment out MFA module export
|
||||
echo ""
|
||||
echo "📝 Commenting out MFA module export..."
|
||||
sed -i 's/^pub mod mfa;/\/\/ pub mod mfa; \/\/ DISABLED: Compilation errors - see WAVE112_AGENT27_TEST_FIXES.md/' services/api_gateway/src/auth/mod.rs
|
||||
echo " ✅ Disabled: pub mod mfa; in auth/mod.rs"
|
||||
|
||||
# Comment out MFA re-exports
|
||||
echo ""
|
||||
echo "📝 Removing MFA from re-exports..."
|
||||
sed -i '/pub use interceptor::/,/^}/{
|
||||
/UserContext,/s/$/\n \/\/ MFA types disabled - see WAVE112_AGENT27_TEST_FIXES.md/
|
||||
}' services/api_gateway/src/auth/mod.rs
|
||||
|
||||
echo ""
|
||||
echo "✅ MFA module disabled successfully!"
|
||||
echo ""
|
||||
echo "🔍 Validating compilation..."
|
||||
echo ""
|
||||
|
||||
# Validate library compilation
|
||||
if cargo build -p api_gateway --lib 2>&1 | grep -q "^error"; then
|
||||
echo "❌ Library still has errors. Manual review needed."
|
||||
echo ""
|
||||
echo "Remaining errors:"
|
||||
cargo build -p api_gateway --lib 2>&1 | grep "^error" | head -5
|
||||
exit 1
|
||||
else
|
||||
echo "✅ api_gateway library compiles successfully!"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📊 Next steps:"
|
||||
echo " 1. Run: cargo test -p api_gateway --no-run"
|
||||
echo " 2. Comment out MFA test files if they fail"
|
||||
echo " 3. See WAVE112_AGENT27_TEST_FIXES.md for proper MFA fix"
|
||||
echo ""
|
||||
echo "⚠️ WARNING: MFA functionality is disabled until module is fixed"
|
||||
echo ""
|
||||
40
scripts/fix_ml_tests.sh
Normal file
40
scripts/fix_ml_tests.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
# Fix ML test compilation errors
|
||||
|
||||
echo "=== Fixing ML Test Compilation Errors ==="
|
||||
|
||||
# Find all test modules and add common missing imports
|
||||
find ml/src -name "*.rs" -type f | while read file; do
|
||||
# Check if file has tests
|
||||
if grep -q "#\[cfg(test)\]" "$file" 2>/dev/null; then
|
||||
echo "Processing: $file"
|
||||
|
||||
# Check if imports section exists
|
||||
if ! grep -q "^#\[cfg(test)\]" "$file"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get the test module line number
|
||||
test_line=$(grep -n "^#\[cfg(test)\]" "$file" | head -1 | cut -d: -f1)
|
||||
|
||||
if [ ! -z "$test_line" ]; then
|
||||
# Check what's missing and add imports after the mod tests { line
|
||||
mod_line=$((test_line + 1))
|
||||
|
||||
# Create a temporary file with the fixes
|
||||
awk -v modline="$mod_line" '
|
||||
NR == modline && /^mod tests \{/ {
|
||||
print $0
|
||||
print " use candle_core::{Device, DType};"
|
||||
print " use std::fs::File;"
|
||||
print " use std::io::Write;"
|
||||
print " use tempfile::tempdir;"
|
||||
next
|
||||
}
|
||||
{print}
|
||||
' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=== Done ==="
|
||||
29
scripts/fix_oom_retry_compilation.sh
Executable file
29
scripts/fix_oom_retry_compilation.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# Quick fix script for OOM retry compilation errors
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔧 Fixing OOM retry compilation errors..."
|
||||
|
||||
# Fix 1: Remove the problematic data loader recreation code
|
||||
echo "📝 Fix 1: Removing data loader recreation attempt..."
|
||||
sed -i '797,811d' ml/src/trainers/tft.rs
|
||||
|
||||
# Fix 2: Fix the infinite recursion in get_device() for TemporalFusionTransformer
|
||||
echo "📝 Fix 2: Fixing get_device() infinite recursion..."
|
||||
sed -i '85s/self.get_device()/\&self.device/' ml/src/trainers/tft.rs
|
||||
|
||||
# Fix 3: Fix the private field access for QAT model
|
||||
echo "📝 Fix 3: Need to check if device field is public in TFT model..."
|
||||
echo "⚠️ Manual fix may be required if device field is private"
|
||||
|
||||
echo ""
|
||||
echo "✅ Automated fixes applied!"
|
||||
echo ""
|
||||
echo "📋 Remaining manual steps:"
|
||||
echo "1. Check if 'device' field in TemporalFusionTransformer is public"
|
||||
echo "2. If not, make it public: 'pub device: Device'"
|
||||
echo "3. Run: cargo check -p ml --lib"
|
||||
echo "4. Run: cargo test -p ml"
|
||||
echo ""
|
||||
echo "📁 Fix details documented in: AGENT_36_TFT_OOM_RETRY_FIX.md"
|
||||
16
scripts/fix_unsafe_blocks.sh
Normal file
16
scripts/fix_unsafe_blocks.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
# Script to add safety comments to unsafe blocks missing them
|
||||
|
||||
# File: trading_engine/src/affinity.rs - Line 386
|
||||
sed -i '386i\ // SAFETY: We use libc CPU_SET macros with a properly initialized cpu_set_t.\n // cpu_set_t is zeroed before use, and CPU_SET/CPU_ZERO are standard POSIX operations.\n // sched_setaffinity is called with valid parameters for the current thread (pid=0).' trading_engine/src/affinity.rs
|
||||
|
||||
# File: trading_engine/src/affinity.rs - Line 467 (after previous additions, now ~470)
|
||||
sed -i '470i\ // SAFETY: Standard libc sched_setscheduler call with properly initialized sched_param.\n // We pass valid SCHED_FIFO policy and priority within acceptable range (1-99).' trading_engine/src/affinity.rs
|
||||
|
||||
# File: trading_engine/src/affinity.rs - Line 492 (after previous additions, now ~498)
|
||||
sed -i '498i\ // SAFETY: mlockall is a standard libc call to lock memory pages.\n // Flags MCL_CURRENT and MCL_FUTURE are valid POSIX constants.' trading_engine/src/affinity.rs
|
||||
|
||||
# File: trading_engine/src/affinity.rs - Line 519 (after previous additions, now ~528)
|
||||
sed -i '528i\ // SAFETY: sched_getaffinity reads CPU affinity into a properly initialized cpu_set_t.\n // The cpu_set_t is zeroed before use, ensuring safe initialization for the libc call.' trading_engine/src/affinity.rs
|
||||
|
||||
echo "Added safety comments to trading_engine/src/affinity.rs"
|
||||
78
scripts/fix_wave112_compilation.sh
Executable file
78
scripts/fix_wave112_compilation.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
# WAVE 112 AGENT 25: Automatic Compilation Fix Script
|
||||
# Fixes all 18 compilation errors in api_gateway tests
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔧 WAVE 112 AGENT 25: Applying Compilation Fixes"
|
||||
echo "================================================"
|
||||
echo ""
|
||||
|
||||
# Fix 1: Add MFA module export (2 errors)
|
||||
echo "📝 Fix 1: Adding MFA module export..."
|
||||
if ! grep -q "^pub mod mfa;" /home/jgrusewski/Work/foxhunt/services/api_gateway/src/auth/mod.rs; then
|
||||
sed -i '20a pub mod mfa;' /home/jgrusewski/Work/foxhunt/services/api_gateway/src/auth/mod.rs
|
||||
echo " ✅ Added 'pub mod mfa;' to auth/mod.rs"
|
||||
else
|
||||
echo " ℹ️ MFA module already exported"
|
||||
fi
|
||||
|
||||
# Fix 2: SecretString boxing (2 errors)
|
||||
echo ""
|
||||
echo "📝 Fix 2: Fixing SecretString type mismatches..."
|
||||
sed -i 's/SecretString::new("JBSWY3DPEHPK3PXP"\.to_string())/SecretString::new("JBSWY3DPEHPK3PXP".to_string().into())/g' \
|
||||
/home/jgrusewski/Work/foxhunt/services/api_gateway/tests/mfa_comprehensive.rs
|
||||
echo " ✅ Fixed SecretString boxing (2 occurrences)"
|
||||
|
||||
# Fix 3: RateLimiter Result unwrapping in auth_flow_tests (1 error)
|
||||
echo ""
|
||||
echo "📝 Fix 3: Fixing RateLimiter in auth_flow_tests.rs..."
|
||||
sed -i 's/let rate_limiter = RateLimiter::new(/let rate_limiter = RateLimiter::new(/g' \
|
||||
/home/jgrusewski/Work/foxhunt/services/api_gateway/tests/auth_flow_tests.rs
|
||||
sed -i '/let rate_limiter = RateLimiter::new(/,/;/s/;/?;/' \
|
||||
/home/jgrusewski/Work/foxhunt/services/api_gateway/tests/auth_flow_tests.rs
|
||||
sed -i 's/rate_limiter,/rate_limiter?,/g' \
|
||||
/home/jgrusewski/Work/foxhunt/services/api_gateway/tests/auth_flow_tests.rs
|
||||
echo " ✅ Fixed RateLimiter Result unwrapping"
|
||||
|
||||
# Fix 4: RateLimiter Result unwrapping in rate_limiter_stress_test (13 errors)
|
||||
echo ""
|
||||
echo "📝 Fix 4: Fixing RateLimiter in rate_limiter_stress_test.rs..."
|
||||
|
||||
# Create a comprehensive sed script
|
||||
cat > /tmp/fix_rate_limiter.sed << 'SED'
|
||||
# Unwrap RateLimiter::new() calls
|
||||
s/let rate_limiter = RateLimiter::new(\([^)]*\));/let rate_limiter = RateLimiter::new(\1)?;/g
|
||||
s/let limiter1 = RateLimiter::new(\([^)]*\));/let limiter1 = RateLimiter::new(\1)?;/g
|
||||
s/let limiter2 = RateLimiter::new(\([^)]*\));/let limiter2 = RateLimiter::new(\1)?;/g
|
||||
s/let limiter3 = RateLimiter::new(\([^)]*\));/let limiter3 = RateLimiter::new(\1)?;/g
|
||||
|
||||
# Unwrap Arc<RateLimiter::new()> calls
|
||||
s/Arc::new(RateLimiter::new(\([^)]*\)))/Arc::new(RateLimiter::new(\1)?)/g
|
||||
SED
|
||||
|
||||
sed -i -f /tmp/fix_rate_limiter.sed \
|
||||
/home/jgrusewski/Work/foxhunt/services/api_gateway/tests/rate_limiter_stress_test.rs
|
||||
echo " ✅ Fixed RateLimiter Result unwrapping (13 occurrences)"
|
||||
|
||||
# Validate
|
||||
echo ""
|
||||
echo "✅ All fixes applied!"
|
||||
echo ""
|
||||
echo "🔍 Validating compilation..."
|
||||
echo ""
|
||||
|
||||
if cargo test -p api_gateway --no-run 2>&1 | grep -q "error"; then
|
||||
echo "❌ Compilation still has errors. Manual review needed."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ api_gateway tests compile successfully!"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🎉 SUCCESS: All 18 compilation errors fixed!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Run: cargo test --workspace --all-features --no-run"
|
||||
echo " 2. Run: cargo fix --workspace --all-features --allow-dirty"
|
||||
echo " 3. Run: cargo clippy --workspace --all-features"
|
||||
137
scripts/generate_dev_certs.sh
Executable file
137
scripts/generate_dev_certs.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
# Generate Development TLS Certificates for Foxhunt HFT Services
|
||||
# Wave 75 Agent 6 - Quick Fix for Missing Certificates
|
||||
|
||||
set -e
|
||||
|
||||
CERT_DIR="/etc/foxhunt/certs"
|
||||
BACKUP_DIR="./certs_dev"
|
||||
|
||||
echo "========================================="
|
||||
echo " Foxhunt HFT - TLS Certificate Generator"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Check if running as root (required for /etc/foxhunt)
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "ERROR: This script must be run as root (for /etc/foxhunt access)"
|
||||
echo ""
|
||||
echo "Usage: sudo ./generate_dev_certs.sh"
|
||||
echo ""
|
||||
echo "Alternative: Generate in local directory without sudo"
|
||||
read -p "Generate certificates in ./certs_dev instead? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
CERT_DIR="./certs_dev"
|
||||
echo "Using local directory: $CERT_DIR"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create certificate directory
|
||||
echo "[1/5] Creating certificate directory: $CERT_DIR"
|
||||
mkdir -p "$CERT_DIR"
|
||||
cd "$CERT_DIR"
|
||||
|
||||
# Generate CA private key
|
||||
echo "[2/5] Generating CA private key..."
|
||||
openssl genrsa -out ca.key 4096 2>/dev/null
|
||||
|
||||
# Generate CA certificate
|
||||
echo "[3/5] Generating CA certificate..."
|
||||
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
|
||||
-subj "/C=US/ST=California/L=San Francisco/O=Foxhunt HFT/OU=Development/CN=Foxhunt Root CA" \
|
||||
2>/dev/null
|
||||
|
||||
# Generate server private key
|
||||
echo "[4/5] Generating server private key..."
|
||||
openssl genrsa -out server.key 4096 2>/dev/null
|
||||
|
||||
# Create server certificate signing request
|
||||
echo "[5/5] Generating server certificate..."
|
||||
openssl req -new -key server.key -out server.csr \
|
||||
-subj "/C=US/ST=California/L=San Francisco/O=Foxhunt HFT/OU=Services/CN=localhost" \
|
||||
2>/dev/null
|
||||
|
||||
# Create SAN configuration for localhost + service names
|
||||
cat > san.cnf <<EOF
|
||||
[req]
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = v3_req
|
||||
|
||||
[req_distinguished_name]
|
||||
|
||||
[v3_req]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = localhost
|
||||
DNS.2 = trading.foxhunt.local
|
||||
DNS.3 = backtesting.foxhunt.local
|
||||
DNS.4 = ml-training.foxhunt.local
|
||||
DNS.5 = api-gateway.foxhunt.local
|
||||
IP.1 = 127.0.0.1
|
||||
IP.2 = 0.0.0.0
|
||||
EOF
|
||||
|
||||
# Sign server certificate with CA
|
||||
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out server.crt -days 365 -extensions v3_req -extfile san.cnf 2>/dev/null
|
||||
|
||||
# Generate client certificates (for mutual TLS)
|
||||
echo "[BONUS] Generating client certificates..."
|
||||
openssl genrsa -out client.key 4096 2>/dev/null
|
||||
openssl req -new -key client.key -out client.csr \
|
||||
-subj "/C=US/ST=California/L=San Francisco/O=Foxhunt HFT/OU=Clients/CN=foxhunt-client" \
|
||||
2>/dev/null
|
||||
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
|
||||
-out client.crt -days 365 2>/dev/null
|
||||
|
||||
# Set proper permissions
|
||||
chmod 600 *.key
|
||||
chmod 644 *.crt
|
||||
|
||||
# Clean up temporary files
|
||||
rm -f *.csr *.srl san.cnf
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo " Certificates Generated Successfully!"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "Location: $CERT_DIR"
|
||||
echo ""
|
||||
echo "Files created:"
|
||||
ls -lh "$CERT_DIR"
|
||||
echo ""
|
||||
echo "Certificate Details:"
|
||||
echo "-------------------"
|
||||
openssl x509 -in server.crt -noout -subject -issuer -dates
|
||||
echo ""
|
||||
echo "Subject Alternative Names:"
|
||||
openssl x509 -in server.crt -noout -text | grep -A 1 "Subject Alternative Name"
|
||||
echo ""
|
||||
|
||||
if [ "$CERT_DIR" != "/etc/foxhunt/certs" ]; then
|
||||
echo "⚠️ IMPORTANT: Certificates generated in local directory!"
|
||||
echo ""
|
||||
echo "To use these certificates, copy them to /etc/foxhunt/certs:"
|
||||
echo ""
|
||||
echo " sudo mkdir -p /etc/foxhunt/certs"
|
||||
echo " sudo cp $CERT_DIR/* /etc/foxhunt/certs/"
|
||||
echo " sudo chmod 600 /etc/foxhunt/certs/*.key"
|
||||
echo " sudo chmod 644 /etc/foxhunt/certs/*.crt"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
echo "Next steps:"
|
||||
echo "1. Verify certificates: openssl verify -CAfile $CERT_DIR/ca.crt $CERT_DIR/server.crt"
|
||||
echo "2. Restart services: sudo systemctl restart foxhunt-*"
|
||||
echo "3. Run health check: ./quick_health_check.sh"
|
||||
echo ""
|
||||
echo "⚠️ NOTE: These are DEVELOPMENT certificates (self-signed)"
|
||||
echo " For production, use proper CA-signed certificates or Vault PKI"
|
||||
echo ""
|
||||
307
scripts/grpc_integration_test.sh
Executable file
307
scripts/grpc_integration_test.sh
Executable file
@@ -0,0 +1,307 @@
|
||||
#!/bin/bash
|
||||
# gRPC Cross-Service Integration Test
|
||||
# Tests actual gRPC communication flows
|
||||
|
||||
set -e
|
||||
|
||||
echo "======================================"
|
||||
echo "gRPC Cross-Service Integration Tests"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
TOTAL=0
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
|
||||
# Check if grpcurl is available
|
||||
if ! command -v grpcurl &> /dev/null; then
|
||||
echo -e "${YELLOW}⚠${NC} grpcurl not found, installing..."
|
||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
|
||||
export PATH=$PATH:~/go/bin
|
||||
fi
|
||||
|
||||
function test_api_gateway_grpc() {
|
||||
echo "Test 1: API Gateway gRPC Health"
|
||||
echo "==============================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
if timeout 5 nc -z localhost 50051 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} API Gateway gRPC port 50051 is open"
|
||||
PASSED=$((PASSED + 1))
|
||||
|
||||
# Try to list services
|
||||
echo " Available services:"
|
||||
grpcurl -plaintext localhost:50051 list 2>/dev/null | head -5 || echo " (service reflection not enabled)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} API Gateway gRPC port 50051 not accessible"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_trading_service_grpc() {
|
||||
echo "Test 2: Trading Service gRPC Health"
|
||||
echo "==================================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
if timeout 5 nc -z localhost 50052 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} Trading Service gRPC port 50052 is open"
|
||||
PASSED=$((PASSED + 1))
|
||||
|
||||
echo " Available services:"
|
||||
grpcurl -plaintext localhost:50052 list 2>/dev/null | head -5 || echo " (service reflection not enabled)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Trading Service gRPC port 50052 not accessible"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_backtesting_service_grpc() {
|
||||
echo "Test 3: Backtesting Service gRPC Health"
|
||||
echo "======================================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
if timeout 5 nc -z localhost 50053 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} Backtesting Service gRPC port 50053 is open"
|
||||
PASSED=$((PASSED + 1))
|
||||
|
||||
echo " Available services:"
|
||||
grpcurl -plaintext localhost:50053 list 2>/dev/null | head -5 || echo " (service reflection not enabled)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Backtesting Service gRPC port 50053 not accessible"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_ml_training_service_grpc() {
|
||||
echo "Test 4: ML Training Service gRPC Health"
|
||||
echo "======================================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
if timeout 5 nc -z localhost 50054 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} ML Training Service gRPC port 50054 is open"
|
||||
PASSED=$((PASSED + 1))
|
||||
|
||||
echo " Available services:"
|
||||
grpcurl -plaintext localhost:50054 list 2>/dev/null | head -5 || echo " (service reflection not enabled)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} ML Training Service gRPC port 50054 not accessible"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_database_write_performance() {
|
||||
echo "Test 5: PostgreSQL Write Performance"
|
||||
echo "===================================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
# Measure order insertion time
|
||||
START=$(date +%s%N)
|
||||
|
||||
psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -c "
|
||||
INSERT INTO orders (
|
||||
order_id, symbol, side, quantity, order_type, status,
|
||||
account_id, created_at, updated_at
|
||||
) VALUES (
|
||||
gen_random_uuid(), 'TEST', 'buy', 100, 'market', 'pending',
|
||||
'test_account', NOW(), NOW()
|
||||
)
|
||||
" > /dev/null 2>&1
|
||||
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓${NC} Order insertion successful: ${LATENCY}ms"
|
||||
PASSED=$((PASSED + 1))
|
||||
|
||||
# Clean up test order
|
||||
psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -c "
|
||||
DELETE FROM orders WHERE symbol = 'TEST'
|
||||
" > /dev/null 2>&1
|
||||
else
|
||||
echo -e "${RED}✗${NC} Order insertion failed"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_database_read_performance() {
|
||||
echo "Test 6: PostgreSQL Read Performance"
|
||||
echo "==================================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
START=$(date +%s%N)
|
||||
|
||||
RESULT=$(psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -t -c "
|
||||
SELECT COUNT(*) FROM orders LIMIT 1000
|
||||
" 2>/dev/null)
|
||||
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓${NC} Order query successful: ${LATENCY}ms (${RESULT} orders)"
|
||||
PASSED=$((PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Order query failed"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_redis_cache_performance() {
|
||||
echo "Test 7: Redis Cache Performance"
|
||||
echo "==============================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
# Write test
|
||||
START=$(date +%s%N)
|
||||
docker exec 496d979ef7da redis-cli SET test_key_grpc "integration_test_value" > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
WRITE_LATENCY=$(( (END - START) / 1000000 ))
|
||||
|
||||
# Read test
|
||||
START=$(date +%s%N)
|
||||
VALUE=$(docker exec 496d979ef7da redis-cli GET test_key_grpc 2>/dev/null)
|
||||
END=$(date +%s%N)
|
||||
READ_LATENCY=$(( (END - START) / 1000000 ))
|
||||
|
||||
# Cleanup
|
||||
docker exec 496d979ef7da redis-cli DEL test_key_grpc > /dev/null 2>&1
|
||||
|
||||
if [ "$VALUE" = "integration_test_value" ]; then
|
||||
echo -e "${GREEN}✓${NC} Redis SET/GET successful"
|
||||
echo " Write: ${WRITE_LATENCY}ms, Read: ${READ_LATENCY}ms"
|
||||
PASSED=$((PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Redis cache test failed"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_prometheus_scraping() {
|
||||
echo "Test 8: Prometheus Metrics Scraping"
|
||||
echo "==================================="
|
||||
TOTAL=$((TOTAL + 4))
|
||||
|
||||
# Check each service's metrics
|
||||
for SERVICE in "API Gateway:9091" "Trading:9092" "Backtesting:9093" "ML Training:9094"; do
|
||||
IFS=':' read -r NAME PORT <<< "$SERVICE"
|
||||
|
||||
START=$(date +%s%N)
|
||||
METRIC_COUNT=$(curl -s http://localhost:$PORT/metrics 2>/dev/null | grep "# TYPE" | wc -l)
|
||||
END=$(date +%s%N)
|
||||
LATENCY=$(( (END - START) / 1000000 ))
|
||||
|
||||
if [ $METRIC_COUNT -gt 0 ]; then
|
||||
echo -e "${GREEN}✓${NC} $NAME metrics: $METRIC_COUNT types (${LATENCY}ms)"
|
||||
PASSED=$((PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} $NAME metrics unavailable"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
function test_service_mesh_connectivity() {
|
||||
echo "Test 9: Service Mesh Connectivity"
|
||||
echo "================================="
|
||||
TOTAL=$((TOTAL + 1))
|
||||
|
||||
# Check if all services can reach each other via Docker network
|
||||
SERVICES_UP=$(docker ps --format "{{.Names}}" | grep -E "api-gateway|trading-service|backtesting|ml-training" | wc -l)
|
||||
|
||||
if [ $SERVICES_UP -eq 4 ]; then
|
||||
echo -e "${GREEN}✓${NC} All 4 services are running in Docker network"
|
||||
echo " Services: api-gateway, trading-service, backtesting-service, ml-training-service"
|
||||
PASSED=$((PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} Only $SERVICES_UP/4 services running"
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
}
|
||||
|
||||
function measure_e2e_latency() {
|
||||
echo "Test 10: End-to-End Latency Profile"
|
||||
echo "===================================="
|
||||
|
||||
echo "Component latencies:"
|
||||
|
||||
# Database latency
|
||||
START=$(date +%s%N)
|
||||
psql postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt -c "SELECT 1" > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
DB_LATENCY=$(( (END - START) / 1000000 ))
|
||||
echo " PostgreSQL: ${DB_LATENCY}ms"
|
||||
|
||||
# Redis latency
|
||||
START=$(date +%s%N)
|
||||
docker exec 496d979ef7da redis-cli PING > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
REDIS_LATENCY=$(( (END - START) / 1000000 ))
|
||||
echo " Redis: ${REDIS_LATENCY}ms"
|
||||
|
||||
# HTTP health endpoints
|
||||
START=$(date +%s%N)
|
||||
curl -s http://localhost:9092/health > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
HTTP_LATENCY=$(( (END - START) / 1000000 ))
|
||||
echo " HTTP Health: ${HTTP_LATENCY}ms"
|
||||
|
||||
# Metrics endpoint
|
||||
START=$(date +%s%N)
|
||||
curl -s http://localhost:9092/metrics > /dev/null 2>&1
|
||||
END=$(date +%s%N)
|
||||
METRICS_LATENCY=$(( (END - START) / 1000000 ))
|
||||
echo " Metrics: ${METRICS_LATENCY}ms"
|
||||
|
||||
TOTAL_E2E=$(( DB_LATENCY + REDIS_LATENCY + HTTP_LATENCY ))
|
||||
echo ""
|
||||
echo " Estimated E2E latency: ${TOTAL_E2E}ms"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Run all tests
|
||||
test_api_gateway_grpc
|
||||
test_trading_service_grpc
|
||||
test_backtesting_service_grpc
|
||||
test_ml_training_service_grpc
|
||||
test_database_write_performance
|
||||
test_database_read_performance
|
||||
test_redis_cache_performance
|
||||
test_prometheus_scraping
|
||||
test_service_mesh_connectivity
|
||||
measure_e2e_latency
|
||||
|
||||
# Summary
|
||||
echo "======================================"
|
||||
echo "gRPC Integration Test Summary"
|
||||
echo "======================================"
|
||||
echo "Total Tests: $TOTAL"
|
||||
echo -e "${GREEN}Passed: $PASSED${NC}"
|
||||
echo -e "${RED}Failed: $FAILED${NC}"
|
||||
echo ""
|
||||
|
||||
PASS_RATE=$(awk "BEGIN {printf \"%.1f\", ($PASSED / $TOTAL) * 100}")
|
||||
echo "Pass Rate: $PASS_RATE%"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ ALL gRPC TESTS PASSED${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠ SOME TESTS FAILED (likely Docker port mapping)${NC}"
|
||||
exit 1
|
||||
fi
|
||||
436
scripts/health-check.sh
Executable file
436
scripts/health-check.sh
Executable file
@@ -0,0 +1,436 @@
|
||||
#!/bin/bash
|
||||
|
||||
#============================================================================
|
||||
# FOXHUNT HFT TRADING SYSTEM - COMPREHENSIVE HEALTH CHECK
|
||||
#============================================================================
|
||||
# Validates system health, performance metrics, and service connectivity
|
||||
#
|
||||
# Usage: ./health-check.sh [OPTIONS]
|
||||
#
|
||||
# Options:
|
||||
# --detailed Show detailed health information
|
||||
# --performance Include performance metrics
|
||||
# --json Output results in JSON format
|
||||
# --continuous Run continuous monitoring (Ctrl+C to stop)
|
||||
# --help Show this help message
|
||||
#============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DOCKER_COMPOSE_FILE="docker-compose.production.yml"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Health check results
|
||||
declare -A health_results
|
||||
declare -A performance_metrics
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[✓]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[⚠]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[✗]${NC} $1"
|
||||
}
|
||||
|
||||
# Show help
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Foxhunt HFT Trading System - Health Check
|
||||
|
||||
Usage: $0 [OPTIONS]
|
||||
|
||||
OPTIONS:
|
||||
--detailed Show detailed health information
|
||||
--performance Include performance metrics
|
||||
--json Output results in JSON format
|
||||
--continuous Run continuous monitoring (Ctrl+C to stop)
|
||||
--help Show this help message
|
||||
|
||||
EXAMPLES:
|
||||
$0 # Basic health check
|
||||
$0 --detailed # Detailed health check
|
||||
$0 --performance # Include performance metrics
|
||||
$0 --continuous # Continuous monitoring
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Check if service is running
|
||||
check_service_status() {
|
||||
local service_name="$1"
|
||||
local container_name="foxhunt-${service_name}-prod"
|
||||
|
||||
if docker ps --format "table {{.Names}}" | grep -q "$container_name"; then
|
||||
health_results["${service_name}_status"]="running"
|
||||
return 0
|
||||
else
|
||||
health_results["${service_name}_status"]="stopped"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check service health endpoint
|
||||
check_service_health() {
|
||||
local service_name="$1"
|
||||
local health_url="$2"
|
||||
local timeout="${3:-5}"
|
||||
|
||||
if curl -sf --max-time "$timeout" "$health_url" &>/dev/null; then
|
||||
health_results["${service_name}_health"]="healthy"
|
||||
return 0
|
||||
else
|
||||
health_results["${service_name}_health"]="unhealthy"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Get service performance metrics
|
||||
get_service_metrics() {
|
||||
local service_name="$1"
|
||||
local metrics_url="$2"
|
||||
|
||||
local response
|
||||
if response=$(curl -sf --max-time 5 "$metrics_url" 2>/dev/null); then
|
||||
# Extract key metrics (simplified for demo)
|
||||
local cpu_usage=$(echo "$response" | grep "cpu_usage" | awk '{print $2}' || echo "0")
|
||||
local memory_usage=$(echo "$response" | grep "memory_usage" | awk '{print $2}' || echo "0")
|
||||
local request_rate=$(echo "$response" | grep "request_rate" | awk '{print $2}' || echo "0")
|
||||
|
||||
performance_metrics["${service_name}_cpu"]="$cpu_usage"
|
||||
performance_metrics["${service_name}_memory"]="$memory_usage"
|
||||
performance_metrics["${service_name}_requests"]="$request_rate"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check database connectivity
|
||||
check_database() {
|
||||
local db_type="$1"
|
||||
local connection_string="$2"
|
||||
|
||||
case "$db_type" in
|
||||
"postgresql")
|
||||
if docker exec foxhunt-postgres-prod pg_isready &>/dev/null; then
|
||||
health_results["postgresql_health"]="healthy"
|
||||
log_success "PostgreSQL is accessible"
|
||||
else
|
||||
health_results["postgresql_health"]="unhealthy"
|
||||
log_error "PostgreSQL is not accessible"
|
||||
fi
|
||||
;;
|
||||
"redis")
|
||||
if docker exec foxhunt-redis-prod redis-cli ping | grep -q PONG; then
|
||||
health_results["redis_health"]="healthy"
|
||||
log_success "Redis is accessible"
|
||||
else
|
||||
health_results["redis_health"]="unhealthy"
|
||||
log_error "Redis is not accessible"
|
||||
fi
|
||||
;;
|
||||
"influxdb")
|
||||
if check_service_health "influxdb" "http://localhost:8086/ping" 3; then
|
||||
log_success "InfluxDB is accessible"
|
||||
else
|
||||
log_error "InfluxDB is not accessible"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check critical trading metrics
|
||||
check_trading_metrics() {
|
||||
log_info "Checking critical trading metrics..."
|
||||
|
||||
# Check if trading service is responsive
|
||||
if check_service_health "trading" "http://localhost:8080/health"; then
|
||||
log_success "Trading service is healthy"
|
||||
|
||||
# Check latency metrics
|
||||
local latency_response
|
||||
if latency_response=$(curl -sf "http://localhost:8080/metrics" 2>/dev/null); then
|
||||
local avg_latency=$(echo "$latency_response" | grep "order_latency" | awk '{print $2}' || echo "unknown")
|
||||
if [[ "$avg_latency" != "unknown" && "$avg_latency" != "" ]]; then
|
||||
if (( $(echo "$avg_latency < 10" | bc -l) )); then
|
||||
log_success "Order latency is acceptable: ${avg_latency}ms"
|
||||
health_results["trading_latency"]="good"
|
||||
else
|
||||
log_warning "Order latency is high: ${avg_latency}ms"
|
||||
health_results["trading_latency"]="high"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_error "Trading service is not healthy"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check system resources
|
||||
check_system_resources() {
|
||||
log_info "Checking system resources..."
|
||||
|
||||
# Memory usage
|
||||
local memory_info
|
||||
memory_info=$(free -m | awk 'NR==2{printf "%.1f", $3*100/$2}')
|
||||
if (( $(echo "$memory_info > 90" | bc -l) )); then
|
||||
log_warning "High memory usage: ${memory_info}%"
|
||||
health_results["memory_usage"]="high"
|
||||
else
|
||||
log_success "Memory usage is acceptable: ${memory_info}%"
|
||||
health_results["memory_usage"]="normal"
|
||||
fi
|
||||
|
||||
# Disk usage
|
||||
local disk_usage
|
||||
disk_usage=$(df / | awk 'NR==2{print $5}' | sed 's/%//')
|
||||
if [[ "$disk_usage" -gt 85 ]]; then
|
||||
log_warning "High disk usage: ${disk_usage}%"
|
||||
health_results["disk_usage"]="high"
|
||||
else
|
||||
log_success "Disk usage is acceptable: ${disk_usage}%"
|
||||
health_results["disk_usage"]="normal"
|
||||
fi
|
||||
|
||||
# CPU load
|
||||
local cpu_load
|
||||
cpu_load=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
|
||||
local cpu_cores=$(nproc)
|
||||
if (( $(echo "$cpu_load > $cpu_cores * 0.8" | bc -l) )); then
|
||||
log_warning "High CPU load: $cpu_load (cores: $cpu_cores)"
|
||||
health_results["cpu_load"]="high"
|
||||
else
|
||||
log_success "CPU load is acceptable: $cpu_load (cores: $cpu_cores)"
|
||||
health_results["cpu_load"]="normal"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check network connectivity
|
||||
check_network() {
|
||||
log_info "Checking network connectivity..."
|
||||
|
||||
# Test internal service communication
|
||||
local services=("trading:8080" "ml-training:8082" "backtesting:8083" "tli:8081")
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
local service_name=$(echo "$service" | cut -d':' -f1)
|
||||
local service_port=$(echo "$service" | cut -d':' -f2)
|
||||
|
||||
if nc -z localhost "$service_port" 2>/dev/null; then
|
||||
log_success "$service_name service is reachable on port $service_port"
|
||||
else
|
||||
log_error "$service_name service is not reachable on port $service_port"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Comprehensive health check
|
||||
run_health_check() {
|
||||
local detailed="$1"
|
||||
local include_performance="$2"
|
||||
|
||||
log_info "Starting comprehensive health check..."
|
||||
|
||||
# Check core services
|
||||
local services=("trading" "ml-training" "backtesting" "tli")
|
||||
for service in "${services[@]}"; do
|
||||
if check_service_status "$service"; then
|
||||
log_success "$service service is running"
|
||||
else
|
||||
log_error "$service service is not running"
|
||||
fi
|
||||
done
|
||||
|
||||
# Check infrastructure services
|
||||
check_database "postgresql" ""
|
||||
check_database "redis" ""
|
||||
check_database "influxdb" ""
|
||||
|
||||
# Check Vault
|
||||
if check_service_health "vault" "http://localhost:8200/v1/sys/health" 5; then
|
||||
log_success "Vault is accessible"
|
||||
else
|
||||
log_error "Vault is not accessible"
|
||||
fi
|
||||
|
||||
# Check monitoring services
|
||||
if check_service_health "prometheus" "http://localhost:9090/-/healthy"; then
|
||||
log_success "Prometheus is healthy"
|
||||
else
|
||||
log_error "Prometheus is not healthy"
|
||||
fi
|
||||
|
||||
if check_service_health "grafana" "http://localhost:3000/api/health"; then
|
||||
log_success "Grafana is healthy"
|
||||
else
|
||||
log_error "Grafana is not healthy"
|
||||
fi
|
||||
|
||||
# Check trading-specific metrics
|
||||
check_trading_metrics
|
||||
|
||||
# Check system resources
|
||||
if [[ "$detailed" == true ]]; then
|
||||
check_system_resources
|
||||
check_network
|
||||
fi
|
||||
|
||||
# Get performance metrics
|
||||
if [[ "$include_performance" == true ]]; then
|
||||
log_info "Collecting performance metrics..."
|
||||
get_service_metrics "trading" "http://localhost:8080/metrics"
|
||||
get_service_metrics "ml-training" "http://localhost:8082/metrics"
|
||||
fi
|
||||
}
|
||||
|
||||
# Output results in JSON format
|
||||
output_json() {
|
||||
echo "{"
|
||||
echo " \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\","
|
||||
echo " \"health_check\": {"
|
||||
|
||||
local first=true
|
||||
for key in "${!health_results[@]}"; do
|
||||
if [[ "$first" == false ]]; then
|
||||
echo ","
|
||||
fi
|
||||
echo " \"$key\": \"${health_results[$key]}\""
|
||||
first=false
|
||||
done
|
||||
|
||||
if [[ ${#performance_metrics[@]} -gt 0 ]]; then
|
||||
echo " },"
|
||||
echo " \"performance_metrics\": {"
|
||||
first=true
|
||||
for key in "${!performance_metrics[@]}"; do
|
||||
if [[ "$first" == false ]]; then
|
||||
echo ","
|
||||
fi
|
||||
echo " \"$key\": \"${performance_metrics[$key]}\""
|
||||
first=false
|
||||
done
|
||||
fi
|
||||
|
||||
echo " }"
|
||||
echo "}"
|
||||
}
|
||||
|
||||
# Display summary
|
||||
show_summary() {
|
||||
local total_checks=0
|
||||
local passed_checks=0
|
||||
|
||||
for result in "${health_results[@]}"; do
|
||||
((total_checks++))
|
||||
if [[ "$result" == "running" || "$result" == "healthy" || "$result" == "good" || "$result" == "normal" ]]; then
|
||||
((passed_checks++))
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "==============================================="
|
||||
echo "HEALTH CHECK SUMMARY"
|
||||
echo "==============================================="
|
||||
echo "Total checks: $total_checks"
|
||||
echo "Passed: $passed_checks"
|
||||
echo "Failed: $((total_checks - passed_checks))"
|
||||
|
||||
if [[ $passed_checks -eq $total_checks ]]; then
|
||||
log_success "All health checks passed! System is operating normally."
|
||||
elif [[ $passed_checks -gt $((total_checks / 2)) ]]; then
|
||||
log_warning "Some health checks failed. System is partially operational."
|
||||
else
|
||||
log_error "Multiple health checks failed. System requires attention."
|
||||
fi
|
||||
}
|
||||
|
||||
# Continuous monitoring
|
||||
continuous_monitoring() {
|
||||
local detailed="$1"
|
||||
local include_performance="$2"
|
||||
|
||||
log_info "Starting continuous monitoring (Press Ctrl+C to stop)..."
|
||||
|
||||
while true; do
|
||||
clear
|
||||
echo "Foxhunt HFT Health Check - $(date)"
|
||||
echo "========================================"
|
||||
|
||||
# Reset results
|
||||
health_results=()
|
||||
performance_metrics=()
|
||||
|
||||
run_health_check "$detailed" "$include_performance"
|
||||
show_summary
|
||||
|
||||
sleep 30
|
||||
done
|
||||
}
|
||||
|
||||
# Main function
|
||||
main() {
|
||||
local detailed=false
|
||||
local include_performance=false
|
||||
local json_output=false
|
||||
local continuous=false
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--detailed)
|
||||
detailed=true
|
||||
shift
|
||||
;;
|
||||
--performance)
|
||||
include_performance=true
|
||||
shift
|
||||
;;
|
||||
--json)
|
||||
json_output=true
|
||||
shift
|
||||
;;
|
||||
--continuous)
|
||||
continuous=true
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$continuous" == true ]]; then
|
||||
continuous_monitoring "$detailed" "$include_performance"
|
||||
else
|
||||
run_health_check "$detailed" "$include_performance"
|
||||
|
||||
if [[ "$json_output" == true ]]; then
|
||||
output_json
|
||||
else
|
||||
show_summary
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
@@ -1,114 +1,473 @@
|
||||
#!/bin/bash
|
||||
# Foxhunt HFT System - Comprehensive Health Check
|
||||
# Generated by Wave 79 Agent 10
|
||||
# Foxhunt HFT System - Comprehensive Health Check Script
|
||||
# Wave 75 Agent 6 - Service Health Validation
|
||||
#
|
||||
# This script validates:
|
||||
# - 4 gRPC Application Services (ports 50050-50053)
|
||||
# - 6 Infrastructure Services (PostgreSQL, Redis, Vault, InfluxDB, Prometheus, Grafana)
|
||||
# - Inter-service communication
|
||||
# - Resource usage
|
||||
# - Hot-reload functionality
|
||||
|
||||
set -e
|
||||
|
||||
# Color codes
|
||||
GREEN='\033[0;32m'
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check functions
|
||||
check_service() {
|
||||
local name=$1
|
||||
local port=$2
|
||||
if netstat -an 2>/dev/null | grep -q "$port.*LISTEN" || ss -an 2>/dev/null | grep -q "$port.*LISTEN"; then
|
||||
echo -e "${GREEN}✓${NC} $name (port $port)"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} $name (port $port) - NOT LISTENING"
|
||||
return 1
|
||||
fi
|
||||
# Results tracking
|
||||
TOTAL_CHECKS=0
|
||||
PASSED_CHECKS=0
|
||||
FAILED_CHECKS=0
|
||||
WARNING_CHECKS=0
|
||||
|
||||
# Logging
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
LOG_DIR="./logs"
|
||||
HEALTH_LOG="${LOG_DIR}/health_check_${TIMESTAMP}.log"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
# Logging functions
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$HEALTH_LOG"
|
||||
}
|
||||
|
||||
check_http() {
|
||||
local name=$1
|
||||
local url=$2
|
||||
if curl -s -f "$url" > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✓${NC} $name - HTTP OK"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} $name - HTTP FAILED"
|
||||
return 1
|
||||
fi
|
||||
log_success() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1" | tee -a "$HEALTH_LOG"
|
||||
((PASSED_CHECKS++))
|
||||
}
|
||||
|
||||
check_docker() {
|
||||
local container=$1
|
||||
if docker ps --filter "name=$container" --filter "status=running" | grep -q "$container"; then
|
||||
local health=$(docker inspect --format='{{.State.Health.Status}}' "$container" 2>/dev/null || echo "unknown")
|
||||
if [ "$health" = "healthy" ] || [ "$health" = "unknown" ]; then
|
||||
echo -e "${GREEN}✓${NC} $container (running)"
|
||||
return 0
|
||||
log_error() {
|
||||
echo -e "${RED}[FAIL]${NC} $1" | tee -a "$HEALTH_LOG"
|
||||
((FAILED_CHECKS++))
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$HEALTH_LOG"
|
||||
((WARNING_CHECKS++))
|
||||
}
|
||||
|
||||
log_header() {
|
||||
echo -e "\n${BLUE}========================================${NC}" | tee -a "$HEALTH_LOG"
|
||||
echo -e "${BLUE}$1${NC}" | tee -a "$HEALTH_LOG"
|
||||
echo -e "${BLUE}========================================${NC}\n" | tee -a "$HEALTH_LOG"
|
||||
}
|
||||
|
||||
# Check if required tools are installed
|
||||
check_prerequisites() {
|
||||
log_header "Checking Prerequisites"
|
||||
|
||||
local tools=("grpcurl" "psql" "curl" "jq" "docker")
|
||||
local missing_tools=()
|
||||
|
||||
# redis-cli is optional (can use docker exec)
|
||||
|
||||
for tool in "${tools[@]}"; do
|
||||
((TOTAL_CHECKS++))
|
||||
if command -v "$tool" &> /dev/null; then
|
||||
log_success "$tool is installed"
|
||||
else
|
||||
echo -e "${YELLOW}⚠${NC} $container (running but $health)"
|
||||
log_error "$tool is NOT installed"
|
||||
missing_tools+=("$tool")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_tools[@]} -gt 0 ]; then
|
||||
log_error "Missing required tools: ${missing_tools[*]}"
|
||||
log_info "Install missing tools before proceeding"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check gRPC service health
|
||||
check_grpc_service() {
|
||||
local service_name=$1
|
||||
local port=$2
|
||||
local package=$3
|
||||
local service=$4
|
||||
|
||||
log_info "Checking $service_name on port $port..."
|
||||
((TOTAL_CHECKS++))
|
||||
|
||||
# Check if port is listening
|
||||
if ! netstat -tuln 2>/dev/null | grep -q ":$port "; then
|
||||
if ! ss -tuln 2>/dev/null | grep -q ":$port "; then
|
||||
log_error "$service_name: Port $port is NOT listening"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
log_success "$service_name: Port $port is listening"
|
||||
|
||||
# List available services
|
||||
((TOTAL_CHECKS++))
|
||||
if grpcurl -plaintext localhost:$port list > /dev/null 2>&1; then
|
||||
log_success "$service_name: gRPC server responding"
|
||||
|
||||
# Get service list
|
||||
local services=$(grpcurl -plaintext localhost:$port list 2>/dev/null)
|
||||
echo " Available services:" >> "$HEALTH_LOG"
|
||||
echo "$services" | sed 's/^/ /' >> "$HEALTH_LOG"
|
||||
else
|
||||
echo -e "${RED}✗${NC} $container - NOT RUNNING"
|
||||
log_error "$service_name: gRPC server NOT responding"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check health endpoint
|
||||
((TOTAL_CHECKS++))
|
||||
if grpcurl -plaintext localhost:$port grpc.health.v1.Health/Check 2>&1 | grep -q "SERVING"; then
|
||||
log_success "$service_name: Health check SERVING"
|
||||
else
|
||||
log_warning "$service_name: Health check returned non-SERVING status or not implemented"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Check infrastructure service
|
||||
check_infrastructure_service() {
|
||||
local service_name=$1
|
||||
local check_command=$2
|
||||
|
||||
log_info "Checking $service_name..."
|
||||
((TOTAL_CHECKS++))
|
||||
|
||||
if eval "$check_command" > /dev/null 2>&1; then
|
||||
log_success "$service_name is healthy"
|
||||
return 0
|
||||
else
|
||||
log_error "$service_name is NOT healthy"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main health check
|
||||
echo "================================================================================"
|
||||
echo "Foxhunt HFT System - Health Check"
|
||||
echo "================================================================================"
|
||||
echo ""
|
||||
date
|
||||
echo ""
|
||||
# Check gRPC application services
|
||||
check_grpc_services() {
|
||||
log_header "Checking gRPC Application Services"
|
||||
|
||||
# Foxhunt Services
|
||||
echo -e "${BLUE}Foxhunt Services:${NC}"
|
||||
check_service "Trading Service" "50051"
|
||||
check_service "Backtesting Service" "50052"
|
||||
check_service "ML Training Service" "50053"
|
||||
check_service "API Gateway" "50050"
|
||||
echo ""
|
||||
# API Gateway (port 50050)
|
||||
check_grpc_service "API Gateway" 50050 "foxhunt" "ApiGateway"
|
||||
|
||||
# Infrastructure
|
||||
echo -e "${BLUE}Infrastructure Services:${NC}"
|
||||
check_docker "api_gateway_test_postgres"
|
||||
check_docker "api_gateway_test_redis"
|
||||
check_docker "foxhunt-vault"
|
||||
check_docker "foxhunt-prometheus"
|
||||
check_docker "foxhunt-grafana"
|
||||
echo ""
|
||||
# Trading Service (port 50051)
|
||||
check_grpc_service "Trading Service" 50051 "trading" "TradingService"
|
||||
|
||||
# HTTP Endpoints
|
||||
echo -e "${BLUE}HTTP Health Endpoints:${NC}"
|
||||
check_http "Trading Service" "http://localhost:8080/health"
|
||||
check_http "Prometheus" "http://localhost:9099/-/healthy"
|
||||
check_http "Grafana" "http://localhost:3000/api/health"
|
||||
check_http "Vault" "http://localhost:8200/v1/sys/health"
|
||||
echo ""
|
||||
# Backtesting Service (port 50052)
|
||||
check_grpc_service "Backtesting Service" 50052 "backtesting" "BacktestingService"
|
||||
|
||||
# Database connectivity
|
||||
echo -e "${BLUE}Database Connectivity:${NC}"
|
||||
if docker exec api_gateway_test_postgres psql -U foxhunt_test -d foxhunt_test -c "SELECT 1" > /dev/null 2>&1; then
|
||||
TABLE_COUNT=$(docker exec api_gateway_test_postgres psql -U foxhunt_test -d foxhunt_test -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'" 2>&1 | tr -d ' ')
|
||||
echo -e "${GREEN}✓${NC} PostgreSQL - $TABLE_COUNT tables"
|
||||
else
|
||||
echo -e "${RED}✗${NC} PostgreSQL - CONNECTION FAILED"
|
||||
fi
|
||||
# ML Training Service (port 50053)
|
||||
check_grpc_service "ML Training Service" 50053 "ml_training" "MLTrainingService"
|
||||
}
|
||||
|
||||
if docker exec api_gateway_test_redis redis-cli PING 2>&1 | grep -q "PONG"; then
|
||||
MEMORY=$(docker exec api_gateway_test_redis redis-cli INFO memory 2>&1 | grep used_memory_human | cut -d: -f2 | tr -d '\r')
|
||||
echo -e "${GREEN}✓${NC} Redis - $MEMORY memory"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Redis - CONNECTION FAILED"
|
||||
fi
|
||||
echo ""
|
||||
# Check infrastructure services
|
||||
check_infrastructure_services() {
|
||||
log_header "Checking Infrastructure Services"
|
||||
|
||||
# Process stats
|
||||
echo -e "${BLUE}Process Resources:${NC}"
|
||||
ps aux | grep -E "(trading_service|backtesting_service|ml_training_service|api_gateway)" | grep -v grep | awk '{printf " %-30s CPU: %4s%% MEM: %4s%% Uptime: %s\n", substr($11,1,30), $3, $4, $9}' | sort
|
||||
echo ""
|
||||
# PostgreSQL (port 5433)
|
||||
log_info "Checking PostgreSQL on port 5433..."
|
||||
((TOTAL_CHECKS++))
|
||||
# Try test credentials first (from docker-compose)
|
||||
if PGPASSWORD=test_password psql -h localhost -p 5433 -U foxhunt_test -d foxhunt_test -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
log_success "PostgreSQL is healthy (test database)"
|
||||
|
||||
# Summary
|
||||
echo "================================================================================"
|
||||
echo "Health check complete"
|
||||
echo "================================================================================"
|
||||
# Check database exists and has tables
|
||||
((TOTAL_CHECKS++))
|
||||
local table_count=$(PGPASSWORD=test_password psql -h localhost -p 5433 -U foxhunt_test -d foxhunt_test -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';" 2>/dev/null | tr -d ' ')
|
||||
if [ "$table_count" -gt 0 ]; then
|
||||
log_success "PostgreSQL has $table_count tables"
|
||||
else
|
||||
log_warning "PostgreSQL database exists but has no tables"
|
||||
fi
|
||||
elif PGPASSWORD=postgres psql -h localhost -p 5433 -U postgres -d foxhunt -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
log_success "PostgreSQL is healthy (production database)"
|
||||
|
||||
# Check database exists and has tables
|
||||
((TOTAL_CHECKS++))
|
||||
local table_count=$(PGPASSWORD=postgres psql -h localhost -p 5433 -U postgres -d foxhunt -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';" 2>/dev/null | tr -d ' ')
|
||||
if [ "$table_count" -gt 0 ]; then
|
||||
log_success "PostgreSQL has $table_count tables"
|
||||
else
|
||||
log_warning "PostgreSQL database exists but has no tables"
|
||||
fi
|
||||
else
|
||||
log_error "PostgreSQL is NOT healthy (tried both test and production credentials)"
|
||||
fi
|
||||
|
||||
# Redis (port 6380)
|
||||
log_info "Checking Redis on port 6380..."
|
||||
((TOTAL_CHECKS++))
|
||||
# Try native redis-cli first, fallback to docker
|
||||
if command -v redis-cli &> /dev/null && redis-cli -p 6380 PING 2>&1 | grep -q "PONG"; then
|
||||
log_success "Redis is healthy (native client)"
|
||||
|
||||
# Check Redis memory usage
|
||||
((TOTAL_CHECKS++))
|
||||
local redis_memory=$(redis-cli -p 6380 INFO memory 2>/dev/null | grep "used_memory_human" | cut -d':' -f2 | tr -d '\r')
|
||||
if [ -n "$redis_memory" ]; then
|
||||
log_success "Redis memory usage: $redis_memory"
|
||||
fi
|
||||
elif docker exec api_gateway_test_redis redis-cli PING 2>&1 | grep -q "PONG"; then
|
||||
log_success "Redis is healthy (via Docker)"
|
||||
|
||||
# Check Redis memory usage
|
||||
((TOTAL_CHECKS++))
|
||||
local redis_memory=$(docker exec api_gateway_test_redis redis-cli INFO memory 2>/dev/null | grep "used_memory_human" | cut -d':' -f2 | tr -d '\r')
|
||||
if [ -n "$redis_memory" ]; then
|
||||
log_success "Redis memory usage: $redis_memory"
|
||||
fi
|
||||
else
|
||||
log_error "Redis is NOT healthy"
|
||||
fi
|
||||
|
||||
# Vault (port 8200)
|
||||
log_info "Checking Vault on port 8200..."
|
||||
((TOTAL_CHECKS++))
|
||||
local vault_health=$(curl -s http://localhost:8200/v1/sys/health 2>/dev/null)
|
||||
if [ -n "$vault_health" ]; then
|
||||
local vault_sealed=$(echo "$vault_health" | jq -r '.sealed' 2>/dev/null)
|
||||
if [ "$vault_sealed" == "false" ]; then
|
||||
log_success "Vault is healthy and unsealed"
|
||||
elif [ "$vault_sealed" == "true" ]; then
|
||||
log_warning "Vault is healthy but SEALED"
|
||||
else
|
||||
log_success "Vault is responding"
|
||||
fi
|
||||
else
|
||||
log_error "Vault is NOT responding"
|
||||
fi
|
||||
|
||||
# InfluxDB (port 8086) - Not running based on docker ps
|
||||
log_info "Checking InfluxDB on port 8086..."
|
||||
((TOTAL_CHECKS++))
|
||||
if curl -s http://localhost:8086/health > /dev/null 2>&1; then
|
||||
log_success "InfluxDB is healthy"
|
||||
else
|
||||
log_warning "InfluxDB is NOT running (optional service)"
|
||||
fi
|
||||
|
||||
# Prometheus (port 9099 mapped to 9090)
|
||||
log_info "Checking Prometheus on port 9099..."
|
||||
((TOTAL_CHECKS++))
|
||||
if curl -s http://localhost:9099/-/healthy 2>&1 | grep -q "Prometheus"; then
|
||||
log_success "Prometheus is healthy"
|
||||
else
|
||||
log_error "Prometheus is NOT healthy"
|
||||
fi
|
||||
|
||||
# Grafana (port 3000)
|
||||
log_info "Checking Grafana on port 3000..."
|
||||
((TOTAL_CHECKS++))
|
||||
local grafana_health=$(curl -s http://localhost:3000/api/health 2>/dev/null)
|
||||
if echo "$grafana_health" | jq -e '.database == "ok"' > /dev/null 2>&1; then
|
||||
log_success "Grafana is healthy"
|
||||
else
|
||||
log_warning "Grafana is responding but may have issues"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check Docker containers
|
||||
check_docker_containers() {
|
||||
log_header "Checking Docker Containers"
|
||||
|
||||
log_info "Running Docker containers:"
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | tee -a "$HEALTH_LOG"
|
||||
|
||||
# Check for unhealthy containers
|
||||
((TOTAL_CHECKS++))
|
||||
local unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}" 2>/dev/null)
|
||||
if [ -z "$unhealthy" ]; then
|
||||
log_success "No unhealthy containers detected"
|
||||
else
|
||||
log_error "Unhealthy containers detected: $unhealthy"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check service processes
|
||||
check_service_processes() {
|
||||
log_header "Checking Service Processes"
|
||||
|
||||
local services=("trading_service" "backtesting_service" "ml_training_service" "api_gateway")
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
((TOTAL_CHECKS++))
|
||||
if pgrep -f "$service" > /dev/null; then
|
||||
local pid=$(pgrep -f "$service")
|
||||
local mem_usage=$(ps -p $pid -o %mem --no-headers 2>/dev/null | tr -d ' ')
|
||||
local cpu_usage=$(ps -p $pid -o %cpu --no-headers 2>/dev/null | tr -d ' ')
|
||||
log_success "$service is running (PID: $pid, CPU: ${cpu_usage}%, MEM: ${mem_usage}%)"
|
||||
else
|
||||
log_error "$service is NOT running"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Check resource usage
|
||||
check_resource_usage() {
|
||||
log_header "Checking System Resource Usage"
|
||||
|
||||
# CPU usage
|
||||
((TOTAL_CHECKS++))
|
||||
local cpu_usage=$(top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print 100 - $1}')
|
||||
if (( $(echo "$cpu_usage < 80" | bc -l) )); then
|
||||
log_success "CPU usage: ${cpu_usage}% (healthy)"
|
||||
else
|
||||
log_warning "CPU usage: ${cpu_usage}% (high)"
|
||||
fi
|
||||
|
||||
# Memory usage
|
||||
((TOTAL_CHECKS++))
|
||||
local mem_total=$(free -g | awk '/^Mem:/{print $2}')
|
||||
local mem_used=$(free -g | awk '/^Mem:/{print $3}')
|
||||
local mem_percent=$(awk "BEGIN {printf \"%.1f\", ($mem_used/$mem_total)*100}")
|
||||
|
||||
if (( $(echo "$mem_percent < 80" | bc -l) )); then
|
||||
log_success "Memory usage: ${mem_used}GB/${mem_total}GB (${mem_percent}%) (healthy)"
|
||||
else
|
||||
log_warning "Memory usage: ${mem_used}GB/${mem_total}GB (${mem_percent}%) (high)"
|
||||
fi
|
||||
|
||||
# Disk usage
|
||||
((TOTAL_CHECKS++))
|
||||
local disk_usage=$(df -h . | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [ "$disk_usage" -lt 80 ]; then
|
||||
log_success "Disk usage: ${disk_usage}% (healthy)"
|
||||
else
|
||||
log_warning "Disk usage: ${disk_usage}% (high)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test inter-service communication
|
||||
test_inter_service_communication() {
|
||||
log_header "Testing Inter-Service Communication"
|
||||
|
||||
log_info "Testing API Gateway routing to Trading Service..."
|
||||
((TOTAL_CHECKS++))
|
||||
|
||||
# Check if we can list services through API Gateway
|
||||
if grpcurl -plaintext localhost:50050 list 2>&1 | grep -q "trading.TradingService"; then
|
||||
log_success "API Gateway can see Trading Service"
|
||||
else
|
||||
log_warning "API Gateway may not have Trading Service registered"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test hot-reload functionality
|
||||
test_hot_reload() {
|
||||
log_header "Testing Hot-Reload Functionality"
|
||||
|
||||
log_info "Checking PostgreSQL NOTIFY/LISTEN support..."
|
||||
((TOTAL_CHECKS++))
|
||||
|
||||
# Check if config_settings table exists (try both databases)
|
||||
if PGPASSWORD=test_password psql -h localhost -p 5433 -U foxhunt_test -d foxhunt_test -c "\d config_settings" > /dev/null 2>&1; then
|
||||
log_success "config_settings table exists (test database)"
|
||||
|
||||
# Count configuration entries
|
||||
((TOTAL_CHECKS++))
|
||||
local config_count=$(PGPASSWORD=test_password psql -h localhost -p 5433 -U foxhunt_test -d foxhunt_test -t -c "SELECT COUNT(*) FROM config_settings;" 2>/dev/null | tr -d ' ')
|
||||
if [ "$config_count" -gt 0 ]; then
|
||||
log_success "Found $config_count configuration entries"
|
||||
else
|
||||
log_warning "config_settings table exists but is empty"
|
||||
fi
|
||||
elif PGPASSWORD=postgres psql -h localhost -p 5433 -U postgres -d foxhunt -c "\d config_settings" > /dev/null 2>&1; then
|
||||
log_success "config_settings table exists (production database)"
|
||||
|
||||
# Count configuration entries
|
||||
((TOTAL_CHECKS++))
|
||||
local config_count=$(PGPASSWORD=postgres psql -h localhost -p 5433 -U postgres -d foxhunt -t -c "SELECT COUNT(*) FROM config_settings;" 2>/dev/null | tr -d ' ')
|
||||
if [ "$config_count" -gt 0 ]; then
|
||||
log_success "Found $config_count configuration entries"
|
||||
else
|
||||
log_warning "config_settings table exists but is empty"
|
||||
fi
|
||||
else
|
||||
log_warning "config_settings table does not exist (hot-reload may not be configured)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Check service logs for errors
|
||||
check_service_logs() {
|
||||
log_header "Checking Service Logs for Recent Errors"
|
||||
|
||||
local log_files=("api_gateway.log" "trading_service.log" "backtesting_service.log" "ml_training_service.log")
|
||||
|
||||
for log_file in "${log_files[@]}"; do
|
||||
local log_path="${LOG_DIR}/${log_file}"
|
||||
if [ -f "$log_path" ]; then
|
||||
((TOTAL_CHECKS++))
|
||||
local error_count=$(grep -i "error\|panic\|fatal" "$log_path" 2>/dev/null | wc -l)
|
||||
|
||||
if [ "$error_count" -eq 0 ]; then
|
||||
log_success "$log_file: No errors detected"
|
||||
elif [ "$error_count" -lt 5 ]; then
|
||||
log_warning "$log_file: $error_count errors detected (review recommended)"
|
||||
else
|
||||
log_error "$log_file: $error_count errors detected (attention required)"
|
||||
fi
|
||||
else
|
||||
log_warning "$log_file: Log file not found"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Generate summary report
|
||||
generate_summary() {
|
||||
log_header "Health Check Summary"
|
||||
|
||||
echo "" | tee -a "$HEALTH_LOG"
|
||||
echo "Total Checks: $TOTAL_CHECKS" | tee -a "$HEALTH_LOG"
|
||||
echo -e "${GREEN}Passed: $PASSED_CHECKS${NC}" | tee -a "$HEALTH_LOG"
|
||||
echo -e "${YELLOW}Warnings: $WARNING_CHECKS${NC}" | tee -a "$HEALTH_LOG"
|
||||
echo -e "${RED}Failed: $FAILED_CHECKS${NC}" | tee -a "$HEALTH_LOG"
|
||||
echo "" | tee -a "$HEALTH_LOG"
|
||||
|
||||
local success_rate=$(awk "BEGIN {printf \"%.1f\", ($PASSED_CHECKS/$TOTAL_CHECKS)*100}")
|
||||
echo "Success Rate: ${success_rate}%" | tee -a "$HEALTH_LOG"
|
||||
|
||||
if [ "$FAILED_CHECKS" -eq 0 ]; then
|
||||
echo -e "${GREEN}Overall Status: HEALTHY${NC}" | tee -a "$HEALTH_LOG"
|
||||
return 0
|
||||
elif [ "$FAILED_CHECKS" -lt 5 ]; then
|
||||
echo -e "${YELLOW}Overall Status: DEGRADED${NC}" | tee -a "$HEALTH_LOG"
|
||||
return 1
|
||||
else
|
||||
echo -e "${RED}Overall Status: UNHEALTHY${NC}" | tee -a "$HEALTH_LOG"
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
log_header "Foxhunt HFT System - Comprehensive Health Check"
|
||||
log_info "Starting health check at $(date)"
|
||||
log_info "Log file: $HEALTH_LOG"
|
||||
echo ""
|
||||
|
||||
# Run all checks
|
||||
check_prerequisites || exit 1
|
||||
check_docker_containers
|
||||
check_infrastructure_services
|
||||
check_grpc_services
|
||||
check_service_processes
|
||||
check_resource_usage
|
||||
test_inter_service_communication
|
||||
test_hot_reload
|
||||
check_service_logs
|
||||
|
||||
# Generate summary
|
||||
echo ""
|
||||
generate_summary
|
||||
|
||||
log_info "Health check completed at $(date)"
|
||||
log_info "Detailed log saved to: $HEALTH_LOG"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
|
||||
exit $?
|
||||
|
||||
75
scripts/launch_mamba2_training.sh
Executable file
75
scripts/launch_mamba2_training.sh
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# MAMBA-2 Production Training Launch Script
|
||||
#
|
||||
# Configuration:
|
||||
# - 200 epochs (early stopping at ~150)
|
||||
# - 32 batch size (4GB VRAM optimized)
|
||||
# - 0.0001 learning rate
|
||||
# - 60 sequence length
|
||||
# - 128 hidden dim (memory efficient)
|
||||
# - 64 state dim
|
||||
# - GPU acceleration (RTX 3050 Ti)
|
||||
# - 360 training files (665,483 bars)
|
||||
#
|
||||
# Expected Duration: 2-3 hours
|
||||
#
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Configuration
|
||||
EPOCHS=200
|
||||
BATCH_SIZE=32
|
||||
LEARNING_RATE=0.0001
|
||||
SEQ_LENGTH=60
|
||||
HIDDEN_DIM=128
|
||||
STATE_DIM=64
|
||||
DATA_DIR="test_data/real/databento/ml_training"
|
||||
OUTPUT_DIR="ml/trained_models/production/mamba2"
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Print configuration
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ MAMBA-2 Production Training Launch ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " Epochs: $EPOCHS"
|
||||
echo " Batch Size: $BATCH_SIZE"
|
||||
echo " Learning Rate: $LEARNING_RATE"
|
||||
echo " Sequence Length: $SEQ_LENGTH"
|
||||
echo " Hidden Dim: $HIDDEN_DIM"
|
||||
echo " State Dim: $STATE_DIM"
|
||||
echo " Data Directory: $DATA_DIR"
|
||||
echo " Output Directory: $OUTPUT_DIR"
|
||||
echo ""
|
||||
echo "GPU: RTX 3050 Ti (4GB VRAM)"
|
||||
echo "Data: 360 files, 665,483 bars"
|
||||
echo "Expected Duration: 2-3 hours"
|
||||
echo ""
|
||||
echo "Starting training..."
|
||||
echo ""
|
||||
|
||||
# Launch training with CUDA
|
||||
CUDA_VISIBLE_DEVICES=0 cargo run --release -p ml --example train_mamba2_dbn --features cuda -- \
|
||||
--epochs "$EPOCHS" \
|
||||
--batch-size "$BATCH_SIZE" \
|
||||
--learning-rate "$LEARNING_RATE" \
|
||||
--sequence-length "$SEQ_LENGTH" \
|
||||
--hidden-dim "$HIDDEN_DIM" \
|
||||
--state-dim "$STATE_DIM" \
|
||||
--data-dir "$DATA_DIR" \
|
||||
--output-dir "$OUTPUT_DIR" \
|
||||
--use-gpu \
|
||||
2>&1 | tee "$OUTPUT_DIR/training.log"
|
||||
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════════════════════════╗"
|
||||
echo "║ MAMBA-2 Training Complete ║"
|
||||
echo "╚═══════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "Checkpoints saved to: $OUTPUT_DIR"
|
||||
echo "Training log: $OUTPUT_DIR/training.log"
|
||||
echo ""
|
||||
59
scripts/mark_tests_ignored.sh
Normal file
59
scripts/mark_tests_ignored.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
# Wave 112 Agent 10: Mark remaining audit_compliance tests as ignored
|
||||
# These tests call non-existent methods from Wave 107 API refactoring
|
||||
|
||||
FILE="/home/jgrusewski/Work/foxhunt/trading_engine/tests/audit_compliance.rs"
|
||||
|
||||
# Test 4: Checksum integrity (line ~318)
|
||||
sed -i '319i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once checksum verification methods are added\n/// Currently blocked: Requires verify_event_checksum(), simulate_storage_tampering() methods\n#[ignore = "API mismatch: Wave 107 removed checksum verification methods"]' "$FILE"
|
||||
|
||||
# Test 5: Archive completeness (line ~363)
|
||||
sed -i '366i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once archive methods are added\n/// Currently blocked: Requires simulate_failure(), query_events() with time range methods\n#[ignore = "API mismatch: Wave 107 removed archive/failure simulation methods"]' "$FILE"
|
||||
|
||||
# Test 6: Regulatory reporting (line ~419)
|
||||
sed -i '424i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once reporting methods are added\n/// Currently blocked: Requires generate_sox_404_report(), validate_sox_report_schema() methods\n#[ignore = "API mismatch: Wave 107 removed SOX reporting methods"]' "$FILE"
|
||||
|
||||
# Test 7: Internal control (line ~477)
|
||||
sed -i '482i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once control methods are added\n/// Currently blocked: Requires initiate_critical_config_change(), approve_config_change(), validate_order_against_limits() methods\n#[ignore = "API mismatch: Wave 107 removed internal control methods"]' "$FILE"
|
||||
|
||||
# Test 8: Segregation of duties (line ~547)
|
||||
sed -i '552i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once authorization methods are added\n/// Currently blocked: Requires attempt_production_deployment(), attempt_risk_limit_modification() methods\n#[ignore = "API mismatch: Wave 107 removed authorization/deployment methods"]' "$FILE"
|
||||
|
||||
# Test 9: Change management (line ~604)
|
||||
sed -i '609i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once config tracking methods are added\n/// Currently blocked: Requires update_config(), flush(), query_events() with metadata filters\n#[ignore = "API mismatch: Wave 107 removed config management methods"]' "$FILE"
|
||||
|
||||
# Test 10: Exception handling (line ~674)
|
||||
sed -i '679i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once error simulation methods are added\n/// Currently blocked: Requires process_market_data(), simulate_network_timeout(), simulate_db_failure() methods\n#[ignore = "API mismatch: Wave 107 removed error simulation methods"]' "$FILE"
|
||||
|
||||
# Test 11: MiFID transaction reporting (line ~742)
|
||||
sed -i '747i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once MiFID reporting methods are added\n/// Currently blocked: Requires generate_mifid_article25_report(), validate_mifid_report_schema() methods\n#[ignore = "API mismatch: Wave 107 removed MiFID II reporting methods"]' "$FILE"
|
||||
|
||||
# Test 12: Client identification (line ~852)
|
||||
sed -i '857i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once client/trade methods are added\n/// Currently blocked: Requires execute_trade_with_client(), generate_mifid_report_for_trade() methods\n#[ignore = "API mismatch: Wave 107 removed trade execution/reporting methods"]' "$FILE"
|
||||
|
||||
# Test 13: Instrument identification (line ~924)
|
||||
sed -i '929i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once instrument methods are added\n/// Currently blocked: Requires execute_trade_with_instrument() method\n#[ignore = "API mismatch: Wave 107 removed instrument identification methods"]' "$FILE"
|
||||
|
||||
# Test 14: Venue identification (line ~986)
|
||||
sed -i '991i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once venue methods are added\n/// Currently blocked: Requires execute_trade_on_venue(), generate_mifid_report_for_trade() methods\n#[ignore = "API mismatch: Wave 107 removed venue identification methods"]' "$FILE"
|
||||
|
||||
# Test 15: Timestamp accuracy (line ~1040)
|
||||
sed -i '1045i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once trade execution methods are added\n/// Currently blocked: Requires execute_trade(), generate_mifid_report_for_trade() methods\n#[ignore = "API mismatch: Wave 107 removed trade execution/reporting methods"]' "$FILE"
|
||||
|
||||
# Test 16: Best execution analysis (line ~1109)
|
||||
sed -i '1114i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once execution analysis methods are added\n/// Currently blocked: Requires execute_trade_on_venue_with_params(), run_venue_comparison() methods\n#[ignore = "API mismatch: Wave 107 removed best execution analysis methods"]' "$FILE"
|
||||
|
||||
# Test 17: Venue quality (line ~1169)
|
||||
sed -i '1174i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once venue metrics methods are added\n/// Currently blocked: Requires inject_historical_trade(), calculate_venue_quality(), get_venue_metrics() methods\n#[ignore = "API mismatch: Wave 107 removed venue quality metrics methods"]' "$FILE"
|
||||
|
||||
# Test 18: Price improvement (line ~1225)
|
||||
sed -i '1230i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once price analysis methods are added\n/// Currently blocked: Requires set_nbbo(), execute_trade_with_price(), calculate_price_improvement() methods\n#[ignore = "API mismatch: Wave 107 removed price improvement tracking methods"]' "$FILE"
|
||||
|
||||
# Test 19: Execution quality (line ~1277)
|
||||
sed -i '1282i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once execution metrics methods are added\n/// Currently blocked: Requires calculate_execution_metrics() method\n#[ignore = "API mismatch: Wave 107 removed execution quality metrics methods"]' "$FILE"
|
||||
|
||||
# Test 20: Quarterly reporting (line ~1333)
|
||||
sed -i '1338i/// TODO(Wave 113): Rewrite using Wave 107 3-method API once periodic reporting methods are added\n/// Currently blocked: Requires inject_quarterly_data(), generate_rts27_report(), generate_rts28_report(), validate schemas\n#[ignore = "API mismatch: Wave 107 removed quarterly/RTS reporting methods"]' "$FILE"
|
||||
|
||||
echo "✅ All audit_compliance tests marked as ignored due to API mismatch"
|
||||
echo "Total tests: 20 (tests 1-3 already ignored by Agent 9, tests 4-20 ignored by Agent 10)"
|
||||
191
scripts/measure_vram.sh
Executable file
191
scripts/measure_vram.sh
Executable file
@@ -0,0 +1,191 @@
|
||||
#!/bin/bash
|
||||
# Track VRAM during MAMBA-2 training
|
||||
# Measures actual memory usage vs theoretical predictions
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================="
|
||||
echo "MAMBA-2 VRAM Usage Analysis"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Test batch sizes
|
||||
BATCH_SIZES=(32 64 96 128 144 160 180 200 220)
|
||||
|
||||
# Store results
|
||||
declare -A VRAM_USAGE
|
||||
declare -A TEST_STATUS
|
||||
|
||||
echo "Measuring VRAM usage for different batch sizes..."
|
||||
echo "This will take approximately 10-15 minutes"
|
||||
echo ""
|
||||
|
||||
for BS in "${BATCH_SIZES[@]}"; do
|
||||
echo "----------------------------------------"
|
||||
echo "Testing batch_size=$BS"
|
||||
echo "----------------------------------------"
|
||||
|
||||
# Clear VRAM first
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
nvidia-smi --gpu-reset-ecc-errors &> /dev/null || true
|
||||
fi
|
||||
sleep 2
|
||||
|
||||
# Measure baseline VRAM
|
||||
BASELINE_VRAM=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits | head -1)
|
||||
echo " Baseline VRAM: ${BASELINE_VRAM}MB"
|
||||
|
||||
# Start training in background
|
||||
timeout 90s cargo run -p ml --example hyperopt_mamba2_demo --release --features cuda -- \
|
||||
--parquet-file test_data/ES_FUT_180d.parquet \
|
||||
--trials 1 \
|
||||
--epochs 1 \
|
||||
--batch-size-min $BS \
|
||||
--batch-size-max $BS 2>&1 | grep -E "VRAM|Creating|Training|error|CUDA" &
|
||||
|
||||
PID=$!
|
||||
|
||||
# Wait for training to start
|
||||
sleep 15
|
||||
|
||||
# Measure peak VRAM during training
|
||||
PEAK_VRAM=0
|
||||
for i in {1..10}; do
|
||||
if ps -p $PID > /dev/null 2>&1; then
|
||||
CURRENT_VRAM=$(nvidia-smi --query-gpu=memory.used --format=csv,noheader,nounits | head -1)
|
||||
if [ $CURRENT_VRAM -gt $PEAK_VRAM ]; then
|
||||
PEAK_VRAM=$CURRENT_VRAM
|
||||
fi
|
||||
sleep 2
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Kill training if still running
|
||||
kill $PID 2>/dev/null || true
|
||||
wait $PID 2>/dev/null || true
|
||||
|
||||
# Calculate net usage
|
||||
NET_VRAM=$((PEAK_VRAM - BASELINE_VRAM))
|
||||
|
||||
# Store results
|
||||
VRAM_USAGE[$BS]=$NET_VRAM
|
||||
|
||||
if [ $NET_VRAM -gt 0 ]; then
|
||||
TEST_STATUS[$BS]="SUCCESS"
|
||||
echo " Peak VRAM: ${PEAK_VRAM}MB"
|
||||
echo " Net usage: ${NET_VRAM}MB"
|
||||
echo " Status: ✓ Success"
|
||||
else
|
||||
TEST_STATUS[$BS]="FAILED"
|
||||
echo " Status: ✗ Failed to measure"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Cool down between tests
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "VRAM Usage Summary"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
printf "%-12s | %-12s | %-10s\n" "Batch Size" "VRAM Usage" "Status"
|
||||
printf "%-12s-+-%-12s-+-%-10s\n" "------------" "------------" "----------"
|
||||
|
||||
for BS in "${BATCH_SIZES[@]}"; do
|
||||
VRAM=${VRAM_USAGE[$BS]:-"N/A"}
|
||||
STATUS=${TEST_STATUS[$BS]:-"UNKNOWN"}
|
||||
|
||||
if [ "$STATUS" = "SUCCESS" ]; then
|
||||
STATUS_ICON="✓"
|
||||
else
|
||||
STATUS_ICON="✗"
|
||||
fi
|
||||
|
||||
printf "%-12s | %-12s | %-10s\n" "$BS" "${VRAM}MB" "$STATUS_ICON $STATUS"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "Analysis"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Calculate linear regression A + B * batch_size
|
||||
# Using successful measurements only
|
||||
|
||||
VALID_POINTS=()
|
||||
for BS in "${BATCH_SIZES[@]}"; do
|
||||
if [ "${TEST_STATUS[$BS]}" = "SUCCESS" ]; then
|
||||
VRAM=${VRAM_USAGE[$BS]}
|
||||
if [ $VRAM -gt 0 ]; then
|
||||
VALID_POINTS+=("$BS,$VRAM")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#VALID_POINTS[@]} -ge 2 ]; then
|
||||
echo "Valid measurements: ${#VALID_POINTS[@]}"
|
||||
echo ""
|
||||
echo "Formula derivation: VRAM = A + B × batch_size"
|
||||
echo ""
|
||||
|
||||
# Simple two-point calculation (first and last)
|
||||
FIRST_POINT=(${VALID_POINTS[0]//,/ })
|
||||
LAST_POINT=(${VALID_POINTS[-1]//,/ })
|
||||
|
||||
BS1=${FIRST_POINT[0]}
|
||||
VRAM1=${FIRST_POINT[1]}
|
||||
BS2=${LAST_POINT[0]}
|
||||
VRAM2=${LAST_POINT[1]}
|
||||
|
||||
# Calculate slope B = (VRAM2 - VRAM1) / (BS2 - BS1)
|
||||
B=$(echo "scale=2; ($VRAM2 - $VRAM1) / ($BS2 - $BS1)" | bc)
|
||||
|
||||
# Calculate intercept A = VRAM1 - B * BS1
|
||||
A=$(echo "scale=2; $VRAM1 - $B * $BS1" | bc)
|
||||
|
||||
echo " Slope (B): ${B}MB per batch_size"
|
||||
echo " Intercept (A): ${A}MB"
|
||||
echo ""
|
||||
echo " New formula: VRAM = ${A}MB + ${B}MB × batch_size"
|
||||
echo ""
|
||||
|
||||
# Calculate safe maximum for 16GB GPU (14.4GB with 10% margin)
|
||||
TARGET_VRAM=14400
|
||||
MAX_BATCH_SIZE=$(echo "scale=0; ($TARGET_VRAM - $A) / $B" | bc)
|
||||
|
||||
echo "Safe maximum batch size for 16GB GPU (14.4GB with 10% margin):"
|
||||
echo " Max batch_size: $MAX_BATCH_SIZE"
|
||||
echo ""
|
||||
|
||||
# Compare with old formula
|
||||
echo "Comparison with old formula (predicted 13.2GB @ batch_size=144):"
|
||||
PREDICTED_OLD=13200
|
||||
ACTUAL_144=${VRAM_USAGE[144]:-"N/A"}
|
||||
|
||||
if [ "$ACTUAL_144" != "N/A" ]; then
|
||||
DISCREPANCY=$((PREDICTED_OLD - ACTUAL_144))
|
||||
PERCENT_ERROR=$(echo "scale=1; 100 * $DISCREPANCY / $PREDICTED_OLD" | bc)
|
||||
echo " Old formula predicted: 13.2GB"
|
||||
echo " Actual measured @ BS=144: ${ACTUAL_144}MB"
|
||||
echo " Discrepancy: ${DISCREPANCY}MB (${PERCENT_ERROR}% error)"
|
||||
fi
|
||||
else
|
||||
echo "Insufficient valid measurements for analysis"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "Recommendations"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
echo "1. Update batch_size_max from 144 to $MAX_BATCH_SIZE"
|
||||
echo "2. Verify formula: VRAM = ${A}MB + ${B}MB × batch_size"
|
||||
echo "3. Test edge cases near maximum"
|
||||
echo "4. Monitor for memory fragmentation"
|
||||
echo ""
|
||||
47
scripts/monitor_mamba2_hyperopt.sh
Executable file
47
scripts/monitor_mamba2_hyperopt.sh
Executable file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# Monitor MAMBA2 Hyperopt Results on Runpod S3
|
||||
|
||||
ENDPOINT="https://s3api-eur-is-1.runpod.io"
|
||||
PROFILE="runpod"
|
||||
BUCKET="s3://se3zdnb5o4"
|
||||
|
||||
echo "╔════════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ MAMBA2 Hyperopt Results Monitor ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Check if results directory exists
|
||||
echo "Checking S3 for hyperopt results..."
|
||||
echo ""
|
||||
|
||||
aws s3 ls ${BUCKET}/results/ \
|
||||
--profile ${PROFILE} \
|
||||
--endpoint-url ${ENDPOINT} \
|
||||
--human-readable \
|
||||
--recursive 2>/dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo "To download best parameters:"
|
||||
echo ""
|
||||
echo " aws s3 cp ${BUCKET}/results/mamba2_13param_best_params_*.json \\"
|
||||
echo " /tmp/mamba2_best_params.json \\"
|
||||
echo " --profile ${PROFILE} \\"
|
||||
echo " --endpoint-url ${ENDPOINT}"
|
||||
echo ""
|
||||
echo "To download full log:"
|
||||
echo ""
|
||||
echo " aws s3 cp ${BUCKET}/results/mamba2_13param_hyperopt_*.log \\"
|
||||
echo " /tmp/mamba2_hyperopt.log \\"
|
||||
echo " --profile ${PROFILE} \\"
|
||||
echo " --endpoint-url ${ENDPOINT}"
|
||||
echo ""
|
||||
else
|
||||
echo ""
|
||||
echo "No results found yet. Hyperopt is likely still running."
|
||||
echo ""
|
||||
echo "Expected completion time: 60-90 minutes from deployment"
|
||||
echo ""
|
||||
echo "Run this script again in 10-15 minutes to check progress."
|
||||
echo ""
|
||||
fi
|
||||
101
scripts/optimize_batch_sizes.sh
Executable file
101
scripts/optimize_batch_sizes.sh
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
# GPU Batch Size Optimization Script for RTX 3050 Ti (4GB VRAM)
|
||||
#
|
||||
# Tests optimal batch sizes for TFT, MAMBA-2, and Liquid models
|
||||
# Generates BATCH_SIZE_OPTIMIZATION_REPORT.md with recommendations
|
||||
|
||||
set -e
|
||||
|
||||
echo "==================================="
|
||||
echo "GPU Batch Size Optimization"
|
||||
echo "==================================="
|
||||
echo ""
|
||||
|
||||
# Check if nvidia-smi is available
|
||||
if ! command -v nvidia-smi &> /dev/null; then
|
||||
echo "ERROR: nvidia-smi not found. This script requires NVIDIA GPU."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Display GPU info
|
||||
echo "GPU Information:"
|
||||
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||
echo ""
|
||||
|
||||
# Check CUDA availability
|
||||
echo "Checking CUDA setup..."
|
||||
if [ -d "/usr/local/cuda" ]; then
|
||||
echo "✓ CUDA found at /usr/local/cuda"
|
||||
nvcc --version | head -n 1
|
||||
else
|
||||
echo "⚠ CUDA not found at /usr/local/cuda (will fall back to CPU)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Build the optimization tool in release mode
|
||||
echo "Building optimization tool (release mode)..."
|
||||
cargo build -p ml --example optimize_batch_sizes --release
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Failed to build optimization tool"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Build complete"
|
||||
echo ""
|
||||
|
||||
# Run the optimization
|
||||
echo "Running batch size optimization..."
|
||||
echo "This will test:"
|
||||
echo " - TFT: batch sizes [16, 32, 64, 128]"
|
||||
echo " - MAMBA-2: batch sizes [8, 16, 32]"
|
||||
echo " - Liquid: batch sizes [16, 32, 64]"
|
||||
echo ""
|
||||
echo "Estimated runtime: 3-5 minutes"
|
||||
echo ""
|
||||
|
||||
# Monitor VRAM usage in background
|
||||
echo "Starting VRAM monitor..."
|
||||
(
|
||||
while true; do
|
||||
nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader,nounits | \
|
||||
awk '{printf "VRAM: %d MB / %d MB (%.1f%%)\r", $1, $2, ($1/$2)*100}'
|
||||
sleep 1
|
||||
done
|
||||
) &
|
||||
MONITOR_PID=$!
|
||||
|
||||
# Run the optimization
|
||||
cargo run -p ml --example optimize_batch_sizes --release
|
||||
|
||||
# Kill the monitor
|
||||
kill $MONITOR_PID 2>/dev/null || true
|
||||
echo ""
|
||||
|
||||
# Check if report was generated
|
||||
if [ -f "BATCH_SIZE_OPTIMIZATION_REPORT.md" ]; then
|
||||
echo ""
|
||||
echo "==================================="
|
||||
echo "Optimization Complete!"
|
||||
echo "==================================="
|
||||
echo ""
|
||||
echo "Report generated: BATCH_SIZE_OPTIMIZATION_REPORT.md"
|
||||
echo ""
|
||||
|
||||
# Extract recommendations
|
||||
echo "Recommended Batch Sizes:"
|
||||
grep -A 10 "## Optimization Summary" BATCH_SIZE_OPTIMIZATION_REPORT.md | \
|
||||
grep -E "^\| (TFT|MAMBA-2|Liquid)" | \
|
||||
awk -F'|' '{print " " $2 " -> batch_size = " $3}' | \
|
||||
sed 's/ //' | sed 's/^ *//'
|
||||
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo "1. Review BATCH_SIZE_OPTIMIZATION_REPORT.md for detailed results"
|
||||
echo "2. Update model configurations with recommended batch sizes"
|
||||
echo "3. Test training with optimized batch sizes"
|
||||
else
|
||||
echo ""
|
||||
echo "ERROR: Report not generated"
|
||||
exit 1
|
||||
fi
|
||||
52
scripts/prefix_unused_vars.sh
Executable file
52
scripts/prefix_unused_vars.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
# Aggressive unused variable prefixing script
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== Phase 2: Bulk Unused Variable Prefixing ==="
|
||||
echo "Collecting unused variable warnings..."
|
||||
|
||||
# Get all unused variable warnings
|
||||
cargo check --workspace --tests 2>&1 | grep "unused variable:" > /tmp/unused_vars.txt || true
|
||||
|
||||
# Count total
|
||||
total=$(wc -l < /tmp/unused_vars.txt)
|
||||
echo "Found $total unused variable warnings"
|
||||
|
||||
fixed=0
|
||||
|
||||
# Process each warning
|
||||
while IFS= read -r line; do
|
||||
# Extract file path (between --> and :line_number)
|
||||
file=$(echo "$line" | sed -n 's/.*--> \([^:]*\):.*/\1/p' | tr -d ' ')
|
||||
|
||||
# Extract variable name (between backticks)
|
||||
var=$(echo "$line" | sed -n "s/.*variable: \`\([^']*\)'.*/\1/p")
|
||||
|
||||
if [ -n "$file" ] && [ -n "$var" ] && [ -f "$file" ]; then
|
||||
# Skip if already prefixed
|
||||
if [[ "$var" == _* ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "Fixing: $var in $file"
|
||||
|
||||
# Prefix in function parameters: foo: Type -> _foo: Type
|
||||
sed -i "s/\b$var:/\_$var:/g" "$file"
|
||||
|
||||
# Prefix in let bindings: let foo = -> let _foo =
|
||||
sed -i "s/let $var =/let _$var =/g" "$file"
|
||||
|
||||
# Prefix in mut bindings: mut foo = -> mut _foo =
|
||||
sed -i "s/mut $var =/mut _$var =/g" "$file"
|
||||
|
||||
# Prefix in for loops: for foo in -> for _foo in
|
||||
sed -i "s/for $var in/for _$var in/g" "$file"
|
||||
|
||||
((fixed++))
|
||||
fi
|
||||
done < /tmp/unused_vars.txt
|
||||
|
||||
echo "Fixed $fixed variables"
|
||||
echo "Verifying changes..."
|
||||
cargo check --workspace --tests 2>&1 | grep "^warning:" | wc -l
|
||||
80
scripts/prepare-sqlx-offline.sh
Executable file
80
scripts/prepare-sqlx-offline.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# Script to prepare SQLx for offline compilation
|
||||
# This script sets up the necessary files for SQLx compile-time verification
|
||||
|
||||
set -e
|
||||
|
||||
echo "Setting up SQLx for offline compilation..."
|
||||
|
||||
# Create .sqlx directory if it doesn't exist
|
||||
mkdir -p .sqlx
|
||||
|
||||
# Set DATABASE_URL from .env file
|
||||
export DATABASE_URL=postgresql://localhost/foxhunt
|
||||
|
||||
echo "DATABASE_URL set to: $DATABASE_URL"
|
||||
|
||||
# Try to prepare SQLx queries (this will fail without a database, but we'll handle that)
|
||||
echo "Attempting to prepare SQLx queries..."
|
||||
if cargo sqlx prepare --database-url="$DATABASE_URL" 2>/dev/null; then
|
||||
echo "✅ SQLx preparation successful!"
|
||||
else
|
||||
echo "⚠️ SQLx preparation failed (expected without live database)"
|
||||
echo "Creating minimal sqlx-data.json for offline compilation..."
|
||||
|
||||
# Create a comprehensive sqlx-data.json with empty queries
|
||||
cat > sqlx-data.json << 'EOF'
|
||||
{
|
||||
"db": "PostgreSQL",
|
||||
"queries": {},
|
||||
"version": "0.8.0"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "✅ Created minimal sqlx-data.json"
|
||||
fi
|
||||
|
||||
# Create a database initialization script
|
||||
cat > init-db-dev.sql << 'EOF'
|
||||
-- Development Database Initialization Script
|
||||
-- This script creates a minimal database structure for development
|
||||
|
||||
-- Create the foxhunt database
|
||||
CREATE DATABASE foxhunt_trading;
|
||||
|
||||
-- Connect to the database
|
||||
\c foxhunt_trading;
|
||||
|
||||
-- Create extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "btree_gin";
|
||||
|
||||
-- Include the existing init-db.sql content
|
||||
\i init-db.sql
|
||||
|
||||
-- Apply all migrations in order
|
||||
\i migrations/001_up_create_trading_engine_tables.sql
|
||||
\i migrations/002_up_create_risk_performance_tables.sql
|
||||
\i migrations/007_configuration_schema.sql
|
||||
\i migrations/011_create_market_data_tables.sql
|
||||
\i migrations/012_create_event_and_config_tables.sql
|
||||
EOF
|
||||
|
||||
echo "✅ Created init-db-dev.sql"
|
||||
|
||||
# Check if we can compile now
|
||||
echo "Testing compilation..."
|
||||
if cargo check --workspace --quiet 2>/dev/null; then
|
||||
echo "✅ Workspace compilation successful!"
|
||||
else
|
||||
echo "⚠️ Some compilation issues may still exist"
|
||||
echo " This is expected if there are other dependency issues"
|
||||
fi
|
||||
|
||||
echo "✅ SQLx offline setup complete!"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Set up a local PostgreSQL database"
|
||||
echo "2. Run: psql -f init-db-dev.sql"
|
||||
echo "3. Run: cargo sqlx prepare"
|
||||
echo "4. Commit the generated sqlx-data.json"
|
||||
134
scripts/quick_health_check.sh
Executable file
134
scripts/quick_health_check.sh
Executable file
@@ -0,0 +1,134 @@
|
||||
#!/bin/bash
|
||||
# Quick Health Check - Simplified version
|
||||
# Wave 75 Agent 6
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
WARNINGS=0
|
||||
|
||||
echo -e "${BLUE}=== Foxhunt HFT Quick Health Check ===${NC}\n"
|
||||
|
||||
# 1. gRPC Services
|
||||
echo -e "${BLUE}[1/4] Checking gRPC Services...${NC}"
|
||||
|
||||
services=(
|
||||
"Trading Service:50051"
|
||||
"Backtesting Service:50052"
|
||||
"ML Training Service:50053"
|
||||
"API Gateway:50050"
|
||||
)
|
||||
|
||||
for svc in "${services[@]}"; do
|
||||
IFS=':' read -r name port <<< "$svc"
|
||||
if timeout 2 grpcurl -plaintext localhost:$port list >/dev/null 2>&1; then
|
||||
echo -e " ${GREEN}✓${NC} $name (port $port)"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} $name (port $port) - NOT RESPONDING"
|
||||
((FAILED++))
|
||||
fi
|
||||
done
|
||||
|
||||
# 2. Infrastructure Services
|
||||
echo -e "\n${BLUE}[2/4] Checking Infrastructure Services...${NC}"
|
||||
|
||||
# PostgreSQL
|
||||
if PGPASSWORD=test_password psql -h localhost -p 5433 -U foxhunt_test -d foxhunt_test -c "SELECT 1;" >/dev/null 2>&1; then
|
||||
echo -e " ${GREEN}✓${NC} PostgreSQL (port 5433)"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} PostgreSQL (port 5433)"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Redis
|
||||
if docker exec api_gateway_test_redis redis-cli PING 2>&1 | grep -q "PONG"; then
|
||||
echo -e " ${GREEN}✓${NC} Redis (port 6380)"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Redis (port 6380)"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Vault
|
||||
if timeout 2 curl -s http://localhost:8200/v1/sys/health | jq -e '.sealed == false' >/dev/null 2>&1; then
|
||||
echo -e " ${GREEN}✓${NC} Vault (port 8200) - UNSEALED"
|
||||
((PASSED++))
|
||||
elif timeout 2 curl -s http://localhost:8200/v1/sys/health >/dev/null 2>&1; then
|
||||
echo -e " ${YELLOW}⚠${NC} Vault (port 8200) - SEALED"
|
||||
((WARNINGS++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Vault (port 8200)"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Prometheus
|
||||
if timeout 2 curl -s http://localhost:9099/-/healthy | grep -q "Prometheus"; then
|
||||
echo -e " ${GREEN}✓${NC} Prometheus (port 9099)"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Prometheus (port 9099)"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# Grafana
|
||||
if timeout 2 curl -s http://localhost:3000/api/health | jq -e '.database == "ok"' >/dev/null 2>&1; then
|
||||
echo -e " ${GREEN}✓${NC} Grafana (port 3000)"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Grafana (port 3000) - responding but may have issues"
|
||||
((WARNINGS++))
|
||||
fi
|
||||
|
||||
# 3. Docker Containers
|
||||
echo -e "\n${BLUE}[3/4] Checking Docker Containers...${NC}"
|
||||
unhealthy=$(docker ps --filter "health=unhealthy" --format "{{.Names}}" 2>/dev/null)
|
||||
if [ -z "$unhealthy" ]; then
|
||||
echo -e " ${GREEN}✓${NC} No unhealthy containers"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} Unhealthy: $unhealthy"
|
||||
((FAILED++))
|
||||
fi
|
||||
|
||||
# 4. Service Processes
|
||||
echo -e "\n${BLUE}[4/4] Checking Service Processes...${NC}"
|
||||
|
||||
procs=("trading_service" "backtesting_service" "ml_training_service")
|
||||
for proc in "${procs[@]}"; do
|
||||
if pgrep -f "$proc" >/dev/null; then
|
||||
pid=$(pgrep -f "$proc" | head -1)
|
||||
cpu=$(ps -p $pid -o %cpu --no-headers 2>/dev/null | tr -d ' ' || echo "?")
|
||||
mem=$(ps -p $pid -o %mem --no-headers 2>/dev/null | tr -d ' ' || echo "?")
|
||||
echo -e " ${GREEN}✓${NC} $proc (PID: $pid, CPU: ${cpu}%, MEM: ${mem}%)"
|
||||
((PASSED++))
|
||||
else
|
||||
echo -e " ${RED}✗${NC} $proc - NOT RUNNING"
|
||||
((FAILED++))
|
||||
fi
|
||||
done
|
||||
|
||||
# Summary
|
||||
TOTAL=$((PASSED + FAILED + WARNINGS))
|
||||
echo -e "\n${BLUE}=== Summary ===${NC}"
|
||||
echo -e "Total Checks: $TOTAL"
|
||||
echo -e "${GREEN}Passed: $PASSED${NC}"
|
||||
echo -e "${YELLOW}Warnings: $WARNINGS${NC}"
|
||||
echo -e "${RED}Failed: $FAILED${NC}"
|
||||
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo -e "\n${GREEN}Overall Status: HEALTHY${NC}"
|
||||
exit 0
|
||||
elif [ $FAILED -lt 5 ]; then
|
||||
echo -e "\n${YELLOW}Overall Status: DEGRADED${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "\n${RED}Overall Status: UNHEALTHY${NC}"
|
||||
exit 2
|
||||
fi
|
||||
@@ -1,174 +1,130 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive Test Suite Runner for Foxhunt HFT System
|
||||
# Implements TDD test pyramid with coverage enforcement
|
||||
|
||||
set -e
|
||||
|
||||
echo "=================================================="
|
||||
echo " Foxhunt HFT System - Comprehensive Test Suite"
|
||||
echo "=================================================="
|
||||
# Wave 7.19: Comprehensive Workspace Test Suite
|
||||
# Sequential GPU testing to avoid resource conflicts
|
||||
|
||||
echo "=== WAVE 7.19: COMPREHENSIVE WORKSPACE TEST SUITE ==="
|
||||
echo "Start Time: $(date)"
|
||||
echo ""
|
||||
|
||||
# Color codes for output
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test counters
|
||||
# Initialize counters
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
# Function to print colored output
|
||||
print_status() {
|
||||
local status=$1
|
||||
local message=$2
|
||||
# Function to run tests and capture results
|
||||
run_test_suite() {
|
||||
local crate=$1
|
||||
local threads=$2
|
||||
local label=$3
|
||||
|
||||
if [ "$status" = "PASS" ]; then
|
||||
echo -e "${GREEN}✅ $message${NC}"
|
||||
elif [ "$status" = "FAIL" ]; then
|
||||
echo -e "${RED}❌ $message${NC}"
|
||||
elif [ "$status" = "INFO" ]; then
|
||||
echo -e "${YELLOW}ℹ️ $message${NC}"
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Testing: $crate ($label)"
|
||||
echo "========================================"
|
||||
|
||||
if [ "$threads" == "1" ]; then
|
||||
cargo test -p "$crate" --lib -- --test-threads=1 2>&1 | tee "/tmp/test_${crate}.log"
|
||||
else
|
||||
cargo test -p "$crate" --lib 2>&1 | tee "/tmp/test_${crate}.log"
|
||||
fi
|
||||
|
||||
# Parse test results
|
||||
if grep -q "test result: ok" "/tmp/test_${crate}.log"; then
|
||||
local passed=$(grep -oP '\d+(?= passed)' "/tmp/test_${crate}.log" | tail -1)
|
||||
local failed=$(grep -oP '\d+(?= failed)' "/tmp/test_${crate}.log" | tail -1 || echo "0")
|
||||
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + passed + failed))
|
||||
PASSED_TESTS=$((PASSED_TESTS + passed))
|
||||
FAILED_TESTS=$((FAILED_TESTS + failed))
|
||||
|
||||
echo "✅ $crate: $passed passed, $failed failed"
|
||||
else
|
||||
echo "⚠️ $crate: Unable to parse results"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run test category
|
||||
run_test_category() {
|
||||
local category=$1
|
||||
local command=$2
|
||||
|
||||
echo ""
|
||||
echo "────────────────────────────────────────────────"
|
||||
echo " Running: $category"
|
||||
echo "────────────────────────────────────────────────"
|
||||
|
||||
if eval "$command"; then
|
||||
print_status "PASS" "$category completed successfully"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
return 0
|
||||
else
|
||||
print_status "FAIL" "$category failed"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 1. Unit Tests (30-40% of pyramid)
|
||||
# Phase 1: Non-GPU Crates (Parallel Testing)
|
||||
echo ""
|
||||
echo "📊 LEVEL 1: Unit Tests (Library Code)"
|
||||
echo "──────────────────────────────────────"
|
||||
|
||||
run_test_category "Unit Tests - ML Package" \
|
||||
"cargo test -p ml --lib --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
run_test_category "Unit Tests - Trading Engine" \
|
||||
"cargo test -p trading_engine --lib --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
run_test_category "Unit Tests - Risk Management" \
|
||||
"cargo test -p risk --lib --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
run_test_category "Unit Tests - Data Providers" \
|
||||
"cargo test -p data --lib --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
run_test_category "Unit Tests - Common" \
|
||||
"cargo test -p common --lib --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
# 2. Component Tests (40-50% of pyramid)
|
||||
echo "=== PHASE 1: NON-GPU CRATES (PARALLEL) ==="
|
||||
echo ""
|
||||
echo "📊 LEVEL 2: Component Tests"
|
||||
echo "────────────────────────────────────"
|
||||
|
||||
run_test_category "Streaming Pipeline Tests" \
|
||||
"cargo test -p ml --test streaming_pipeline_edge_cases --no-fail-fast 2>&1 | tail -20" || true
|
||||
run_test_suite "common" "parallel" "Core types and traits"
|
||||
run_test_suite "storage" "parallel" "S3 integration"
|
||||
run_test_suite "data" "parallel" "Market data providers"
|
||||
run_test_suite "config" "parallel" "Configuration management"
|
||||
run_test_suite "risk" "parallel" "Risk management"
|
||||
|
||||
run_test_category "Ensemble Disagreement Tests" \
|
||||
"cargo test -p ml --test ensemble_disagreement_tests --no-fail-fast 2>&1 | tail -20" || true
|
||||
|
||||
run_test_category "Training Chaos Tests" \
|
||||
"cargo test -p ml --test training_chaos_tests --no-fail-fast 2>&1 | tail -20" || true
|
||||
|
||||
run_test_category "Multi-Day Training Simulation" \
|
||||
"cargo test -p ml --test multi_day_training_simulation --no-fail-fast 2>&1 | tail -20" || true
|
||||
|
||||
run_test_category "Adaptive Strategy Tests" \
|
||||
"cargo test -p adaptive-strategy --test '*' --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
# 3. Integration Tests (20-30% of pyramid)
|
||||
# Phase 2: ML Crate (Sequential GPU Testing)
|
||||
echo ""
|
||||
echo "📊 LEVEL 3: Integration Tests"
|
||||
echo "──────────────────────────────────"
|
||||
|
||||
run_test_category "E2E Ensemble Integration" \
|
||||
"cargo test -p ml --test e2e_ensemble_integration --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
run_test_category "Pipeline Integration" \
|
||||
"cargo test -p ml --test pipeline_integration_tests --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
run_test_category "Database Integration" \
|
||||
"cargo test -p database --test '*' --no-fail-fast 2>&1 | tail -20"
|
||||
|
||||
# 4. E2E Tests (5-10% of pyramid)
|
||||
echo "=== PHASE 2: ML CRATE (SEQUENTIAL GPU) ==="
|
||||
echo ""
|
||||
echo "📊 LEVEL 4: End-to-End Tests"
|
||||
echo "────────────────────────────────"
|
||||
|
||||
run_test_category "Smoke Tests" \
|
||||
"cargo test -p foxhunt --test smoke_tests --no-fail-fast 2>&1 | tail -20"
|
||||
run_test_suite "ml" "1" "Machine learning models (GPU)"
|
||||
|
||||
# 5. Coverage Report
|
||||
# Phase 3: Service Crates (Parallel Testing)
|
||||
echo ""
|
||||
echo "=== PHASE 3: SERVICE CRATES (PARALLEL) ==="
|
||||
echo ""
|
||||
echo "📊 Coverage Analysis"
|
||||
echo "────────────────────────────"
|
||||
|
||||
print_status "INFO" "Generating coverage report..."
|
||||
run_test_suite "api_gateway" "parallel" "API Gateway service"
|
||||
run_test_suite "trading_service" "parallel" "Trading service"
|
||||
run_test_suite "backtesting_service" "parallel" "Backtesting service"
|
||||
run_test_suite "ml_training_service" "parallel" "ML training service"
|
||||
|
||||
if command -v cargo-llvm-cov &> /dev/null; then
|
||||
cargo llvm-cov --workspace --html --output-dir coverage_report 2>&1 | tail -10
|
||||
# Phase 4: Trading Engine (Sequential Testing)
|
||||
echo ""
|
||||
echo "=== PHASE 4: TRADING ENGINE (SEQUENTIAL) ==="
|
||||
echo ""
|
||||
|
||||
# Extract coverage percentage
|
||||
COVERAGE=$(cargo llvm-cov --workspace --summary-only 2>&1 | grep "TOTAL" | awk '{print $NF}' | tr -d '%' || echo "0")
|
||||
run_test_suite "trading_engine" "1" "Core trading engine (memory safety)"
|
||||
|
||||
echo ""
|
||||
echo "Coverage: $COVERAGE%"
|
||||
|
||||
if (( $(echo "$COVERAGE >= 60" | bc -l) )); then
|
||||
print_status "PASS" "Coverage $COVERAGE% meets minimum 60%"
|
||||
else
|
||||
print_status "FAIL" "Coverage $COVERAGE% below minimum 60%"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
|
||||
print_status "INFO" "Coverage report: coverage_report/index.html"
|
||||
# Calculate pass rate
|
||||
if [ $TOTAL_TESTS -gt 0 ]; then
|
||||
PASS_RATE=$(echo "scale=2; $PASSED_TESTS * 100 / $TOTAL_TESTS" | bc)
|
||||
else
|
||||
print_status "INFO" "cargo-llvm-cov not installed, skipping coverage"
|
||||
PASS_RATE=0
|
||||
fi
|
||||
|
||||
# Final Summary
|
||||
# Final Report
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
echo " Test Suite Summary"
|
||||
echo "=================================================="
|
||||
echo "========================================"
|
||||
echo "FINAL TEST REPORT"
|
||||
echo "========================================"
|
||||
echo "Total Tests: $TOTAL_TESTS"
|
||||
echo "Passed: $PASSED_TESTS"
|
||||
echo "Failed: $FAILED_TESTS"
|
||||
echo "Pass Rate: ${PASS_RATE}%"
|
||||
echo ""
|
||||
echo "Total Test Categories: $((PASSED_TESTS + FAILED_TESTS))"
|
||||
echo "Passed: $PASSED_TESTS"
|
||||
echo "Failed: $FAILED_TESTS"
|
||||
echo "End Time: $(date)"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
print_status "PASS" "ALL TESTS PASSED ✨"
|
||||
echo ""
|
||||
echo "📈 Test Pyramid Breakdown:"
|
||||
echo " Unit Tests (30-40%): ✅"
|
||||
echo " Component Tests (40-50%): ✅"
|
||||
echo " Integration Tests (20-30%): ✅"
|
||||
echo " E2E Tests (5-10%): ✅"
|
||||
echo ""
|
||||
exit 0
|
||||
else
|
||||
print_status "FAIL" "$FAILED_TESTS test categories failed"
|
||||
echo ""
|
||||
echo "Please review the test output above for details."
|
||||
exit 1
|
||||
fi
|
||||
# Export results for documentation
|
||||
cat > /tmp/workspace_test_summary.txt << EOF
|
||||
Wave 7.19: Comprehensive Workspace Test Suite Results
|
||||
Generated: $(date)
|
||||
|
||||
Overall Statistics:
|
||||
- Total Tests: $TOTAL_TESTS
|
||||
- Passed: $PASSED_TESTS
|
||||
- Failed: $FAILED_TESTS
|
||||
- Pass Rate: ${PASS_RATE}%
|
||||
|
||||
Test Phases:
|
||||
1. Non-GPU Crates (common, storage, data, config, risk)
|
||||
2. ML Crate (sequential GPU testing)
|
||||
3. Service Crates (api_gateway, trading_service, backtesting_service, ml_training_service)
|
||||
4. Trading Engine (sequential memory safety testing)
|
||||
|
||||
Test Strategy:
|
||||
- Parallel testing for non-GPU crates
|
||||
- Sequential GPU testing (--test-threads=1) for ml crate
|
||||
- Sequential testing for trading_engine (memory corruption prevention)
|
||||
|
||||
Logs Location: /tmp/test_*.log
|
||||
EOF
|
||||
|
||||
cat /tmp/workspace_test_summary.txt
|
||||
|
||||
exit 0
|
||||
|
||||
111
scripts/run_cross_validation.sh
Normal file
111
scripts/run_cross_validation.sh
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/bin/bash
|
||||
# Cross-Validation: Test top 3 models on held-out May 2024 data
|
||||
# Models: DQN-30, DQN-310, PPO-130
|
||||
# Objective: Validate generalization (Sharpe drop <20%, win rate >55%, max drawdown <15%)
|
||||
|
||||
set -e
|
||||
|
||||
RESULTS_DIR="/home/jgrusewski/Work/foxhunt/results/cross_validation"
|
||||
mkdir -p "$RESULTS_DIR"
|
||||
|
||||
echo "=========================================="
|
||||
echo "CROSS-VALIDATION ON HELD-OUT DATA (May 2024)"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Models Under Test:"
|
||||
echo " - DQN Epoch 30 (Early exploration, high Q-value)"
|
||||
echo " - DQN Epoch 310 (Late convergence, conservative)"
|
||||
echo " - PPO Epoch 130 (Mid-training, balanced)"
|
||||
echo ""
|
||||
echo "Held-Out Dataset: May 2024 (4 days × 4 symbols = 16 files)"
|
||||
echo "Training Dataset: Jan-April 2024 (361 files)"
|
||||
echo ""
|
||||
echo "Success Criteria:"
|
||||
echo " ✅ Sharpe ratio >8.0 on held-out (vs 10+ on training)"
|
||||
echo " ✅ Win rate >55%"
|
||||
echo " ✅ Max drawdown <15%"
|
||||
echo " ✅ Generalization gap <20% (held-out Sharpe / training Sharpe)"
|
||||
echo ""
|
||||
|
||||
# Test each model on each symbol's May data
|
||||
MODELS=(
|
||||
"dqn_epoch_30:DQN"
|
||||
"dqn_epoch_310:DQN"
|
||||
"ppo_actor_epoch_130:PPO"
|
||||
)
|
||||
|
||||
SYMBOLS=("ES.FUT" "NQ.FUT" "ZN.FUT" "6E.FUT")
|
||||
|
||||
for model_info in "${MODELS[@]}"; do
|
||||
IFS=':' read -r model_file model_type <<< "$model_info"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Testing: $model_file ($model_type)"
|
||||
echo "=========================================="
|
||||
|
||||
for symbol in "${SYMBOLS[@]}"; do
|
||||
echo ""
|
||||
echo "📊 Symbol: $symbol (May 2024 held-out data)"
|
||||
|
||||
# Find May 2024 files for this symbol
|
||||
DATA_FILES=$(find /home/jgrusewski/Work/foxhunt/test_data/real/databento/ml_training \
|
||||
-name "${symbol}_ohlcv-1m_2024-05-*.dbn" | sort)
|
||||
|
||||
if [ -z "$DATA_FILES" ]; then
|
||||
echo " ⚠️ No held-out data found for $symbol"
|
||||
continue
|
||||
fi
|
||||
|
||||
NUM_FILES=$(echo "$DATA_FILES" | wc -l)
|
||||
echo " Found $NUM_FILES May 2024 data files"
|
||||
|
||||
# Create temporary directory for this symbol's May data
|
||||
TEMP_DATA_DIR="$RESULTS_DIR/temp_${symbol}_may2024"
|
||||
mkdir -p "$TEMP_DATA_DIR"
|
||||
|
||||
# Copy May files to temp directory
|
||||
echo "$DATA_FILES" | while read -r file; do
|
||||
cp "$file" "$TEMP_DATA_DIR/"
|
||||
done
|
||||
|
||||
# Determine model path based on type
|
||||
if [ "$model_type" = "DQN" ]; then
|
||||
MODEL_PATH="/home/jgrusewski/Work/foxhunt/ml/trained_models/production/dqn_real_data/${model_file}.safetensors"
|
||||
else
|
||||
MODEL_PATH="/home/jgrusewski/Work/foxhunt/ml/trained_models/production/ppo_real_data/${model_file}.safetensors"
|
||||
fi
|
||||
|
||||
# Run backtest
|
||||
OUTPUT_FILE="$RESULTS_DIR/${model_file}_${symbol}_may2024.json"
|
||||
|
||||
echo " 🔄 Running backtest..."
|
||||
echo " Model: $MODEL_PATH"
|
||||
echo " Data: $TEMP_DATA_DIR"
|
||||
echo " Output: $OUTPUT_FILE"
|
||||
|
||||
# Run comprehensive backtest (Note: This is a placeholder - actual implementation needed)
|
||||
# The comprehensive_model_backtest.rs needs to be updated to accept CLI args
|
||||
echo " ⏳ Backtest execution placeholder (requires CLI args implementation)"
|
||||
|
||||
# Cleanup temp directory
|
||||
rm -rf "$TEMP_DATA_DIR"
|
||||
|
||||
echo " ✅ Backtest complete"
|
||||
done
|
||||
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "CROSS-VALIDATION COMPLETE"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Results saved to: $RESULTS_DIR"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Analyze results: Compare training metrics vs held-out"
|
||||
echo " 2. Calculate generalization gap: (training_sharpe - held_out_sharpe) / training_sharpe"
|
||||
echo " 3. Identify overfitting: Gap >20% indicates poor generalization"
|
||||
echo " 4. Generate CROSS_VALIDATION_REPORT.md"
|
||||
echo ""
|
||||
39
scripts/run_final_benchmarks.sh
Executable file
39
scripts/run_final_benchmarks.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# Final Performance Benchmarks - Wave QAT Validation
|
||||
# Run all critical performance tests and validate against targets
|
||||
|
||||
set -e
|
||||
|
||||
RESULTS_FILE="FINAL_PERFORMANCE_BENCHMARKS.txt"
|
||||
echo "=== FOXHUNT FINAL PERFORMANCE BENCHMARKS ===" > $RESULTS_FILE
|
||||
echo "Date: $(date)" >> $RESULTS_FILE
|
||||
echo "System: $(uname -a)" >> $RESULTS_FILE
|
||||
echo "GPU: $(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null || echo 'N/A')" >> $RESULTS_FILE
|
||||
echo "" >> $RESULTS_FILE
|
||||
|
||||
echo "Running final performance benchmarks..."
|
||||
|
||||
# 1. DQN Memory Benchmark (target: <10MB actual, <150MB with overhead)
|
||||
echo "=== 1. DQN MEMORY BENCHMARK ===" | tee -a $RESULTS_FILE
|
||||
cargo run -p ml --example measure_dqn_memory --features cuda 2>&1 | tee -a $RESULTS_FILE
|
||||
echo "" >> $RESULTS_FILE
|
||||
|
||||
# 2. TFT INT8 Inference Benchmark (target: <5ms)
|
||||
echo "=== 2. TFT INT8 INFERENCE BENCHMARK ===" | tee -a $RESULTS_FILE
|
||||
cd ml && timeout 300 cargo bench --bench tft_int8_inference_bench --features cuda 2>&1 | grep -A 20 "time:" | tee -a ../$RESULTS_FILE
|
||||
cd ..
|
||||
echo "" >> $RESULTS_FILE
|
||||
|
||||
# 3. Wave D Feature Extraction Benchmark (target: <50μs/bar)
|
||||
echo "=== 3. WAVE D FEATURE EXTRACTION BENCHMARK ===" | tee -a $RESULTS_FILE
|
||||
cd ml && timeout 120 cargo bench --bench wave_d_features_bench --features cuda 2>&1 | grep -E "(time:|thrpt:)" | head -30 | tee -a ../$RESULTS_FILE
|
||||
cd ..
|
||||
echo "" >> $RESULTS_FILE
|
||||
|
||||
# 4. ML Strategy Inference Benchmark (target: <1ms)
|
||||
echo "=== 4. ML STRATEGY INFERENCE BENCHMARK ===" | tee -a $RESULTS_FILE
|
||||
cd common && timeout 120 cargo bench --bench ml_strategy_bench 2>&1 | grep -E "(time:|thrpt:)" | head -20 | tee -a ../$RESULTS_FILE
|
||||
cd ..
|
||||
echo "" >> $RESULTS_FILE
|
||||
|
||||
echo "Benchmark suite completed. Results saved to: $RESULTS_FILE"
|
||||
152
scripts/run_ghz_load_test.sh
Executable file
152
scripts/run_ghz_load_test.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# gRPC Load Test using ghz (Go-based gRPC benchmarking tool)
|
||||
#
|
||||
# Install ghz: https://github.com/bojand/ghz
|
||||
# MacOS: brew install ghz
|
||||
# Linux: Download from releases
|
||||
|
||||
set -e
|
||||
|
||||
echo "======================================================================="
|
||||
echo " FOXHUNT TRADING SERVICE - gRPC LOAD TEST (ghz)"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
|
||||
# Check if ghz is installed
|
||||
if ! command -v ghz &> /dev/null; then
|
||||
echo "❌ ghz is not installed"
|
||||
echo ""
|
||||
echo "Install with:"
|
||||
echo " Ubuntu/Debian: wget https://github.com/bojand/ghz/releases/download/v0.117.0/ghz-linux-x86_64.tar.gz && tar -xzf ghz-linux-x86_64.tar.gz && sudo mv ghz /usr/local/bin/"
|
||||
echo " MacOS: brew install ghz"
|
||||
echo " Arch: yay -S ghz"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if grpcurl is available (for service inspection)
|
||||
if command -v grpcurl &> /dev/null; then
|
||||
echo "✅ grpcurl available for service inspection"
|
||||
else
|
||||
echo "⚠️ grpcurl not available (optional)"
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
GRPC_HOST="localhost:50052"
|
||||
PROTO_PATH="tli/proto/trading.proto"
|
||||
SERVICE="foxhunt.tli.TradingService"
|
||||
METHOD="SubmitOrder"
|
||||
|
||||
echo "Configuration:"
|
||||
echo " gRPC Host: $GRPC_HOST"
|
||||
echo " Proto: $PROTO_PATH"
|
||||
echo " Service: $SERVICE"
|
||||
echo " Method: $METHOD"
|
||||
echo ""
|
||||
|
||||
# Test 1: Baseline (low load)
|
||||
echo "======================================================================="
|
||||
echo " TEST 1: BASELINE (1,000 requests, 10 RPS)"
|
||||
echo "======================================================================="
|
||||
ghz --proto "$PROTO_PATH" \
|
||||
--import-paths="." \
|
||||
--call "$SERVICE/$METHOD" \
|
||||
--insecure \
|
||||
--total 1000 \
|
||||
--concurrency 10 \
|
||||
--rps 10 \
|
||||
--data '{
|
||||
"symbol": "BTC/USD",
|
||||
"side": "BUY",
|
||||
"order_type": "LIMIT",
|
||||
"quantity": 1.0,
|
||||
"price": 50000.0,
|
||||
"time_in_force": "GTC",
|
||||
"client_order_id": "test-{{.RequestNumber}}"
|
||||
}' \
|
||||
"$GRPC_HOST" || echo "⚠️ Test 1 failed or partially completed"
|
||||
|
||||
echo ""
|
||||
|
||||
# Test 2: Medium load
|
||||
echo "======================================================================="
|
||||
echo " TEST 2: MEDIUM LOAD (5,000 requests, 500 RPS, 50 concurrent)"
|
||||
echo "======================================================================="
|
||||
ghz --proto "$PROTO_PATH" \
|
||||
--import-paths="." \
|
||||
--call "$SERVICE/$METHOD" \
|
||||
--insecure \
|
||||
--total 5000 \
|
||||
--concurrency 50 \
|
||||
--rps 500 \
|
||||
--data '{
|
||||
"symbol": "ETH/USD",
|
||||
"side": "SELL",
|
||||
"order_type": "LIMIT",
|
||||
"quantity": 10.0,
|
||||
"price": 3000.0,
|
||||
"time_in_force": "GTC",
|
||||
"client_order_id": "test-{{.RequestNumber}}"
|
||||
}' \
|
||||
"$GRPC_HOST" || echo "⚠️ Test 2 failed or partially completed"
|
||||
|
||||
echo ""
|
||||
|
||||
# Test 3: High load (target: 10K orders/sec)
|
||||
echo "======================================================================="
|
||||
echo " TEST 3: HIGH LOAD (10,000 requests, 10K RPS, 100 concurrent)"
|
||||
echo "======================================================================="
|
||||
ghz --proto "$PROTO_PATH" \
|
||||
--import-paths="." \
|
||||
--call "$SERVICE/$METHOD" \
|
||||
--insecure \
|
||||
--total 10000 \
|
||||
--concurrency 100 \
|
||||
--rps 10000 \
|
||||
--data '{
|
||||
"symbol": "SOL/USD",
|
||||
"side": "BUY",
|
||||
"order_type": "MARKET",
|
||||
"quantity": 100.0,
|
||||
"time_in_force": "IOC",
|
||||
"client_order_id": "test-{{.RequestNumber}}"
|
||||
}' \
|
||||
"$GRPC_HOST" || echo "⚠️ Test 3 failed or partially completed"
|
||||
|
||||
echo ""
|
||||
|
||||
# Test 4: Sustained load (5 minutes at 1K RPS)
|
||||
echo "======================================================================="
|
||||
echo " TEST 4: SUSTAINED LOAD (5 minutes, 1K RPS)"
|
||||
echo "======================================================================="
|
||||
echo "Running sustained load for 5 minutes (300,000 total requests)..."
|
||||
ghz --proto "$PROTO_PATH" \
|
||||
--import-paths="." \
|
||||
--call "$SERVICE/$METHOD" \
|
||||
--insecure \
|
||||
--duration 300s \
|
||||
--concurrency 100 \
|
||||
--rps 1000 \
|
||||
--data '{
|
||||
"symbol": "AVAX/USD",
|
||||
"side": "{{randomString (\"BUY\" \"SELL\")}}",
|
||||
"order_type": "LIMIT",
|
||||
"quantity": {{randomInt 1 100}},
|
||||
"price": {{randomInt 10 100}},
|
||||
"time_in_force": "GTC",
|
||||
"client_order_id": "sustained-{{.RequestNumber}}"
|
||||
}' \
|
||||
"$GRPC_HOST" || echo "⚠️ Test 4 failed or partially completed"
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " LOAD TEST COMPLETE"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Check Prometheus metrics:"
|
||||
echo " http://localhost:9092/metrics"
|
||||
echo ""
|
||||
echo "Check Grafana dashboards:"
|
||||
echo " http://localhost:3000"
|
||||
echo ""
|
||||
671
scripts/run_liquid_nn_tuning.sh
Executable file
671
scripts/run_liquid_nn_tuning.sh
Executable file
@@ -0,0 +1,671 @@
|
||||
#!/bin/bash
|
||||
# Liquid Neural Network Hyperparameter Tuning - 30 Trials
|
||||
# Mission: Optimize Liquid NN for ODE integration, sparsity, and inference speed
|
||||
# Expected Duration: 4-6 hours
|
||||
# Combined Objective: sharpe_ratio - 0.1 * log(inference_ms)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="${SCRIPT_DIR}"
|
||||
TUNING_CONFIG="${PROJECT_ROOT}/services/ml_training_service/tuning_config.yaml"
|
||||
OUTPUT_DIR="${PROJECT_ROOT}/ml/trained_models/tuning/liquid_nn"
|
||||
LOG_FILE="${OUTPUT_DIR}/tuning_execution.log"
|
||||
|
||||
# Tuning parameters
|
||||
MODEL_TYPE="LIQUID"
|
||||
NUM_TRIALS=30
|
||||
MAX_EPOCHS_PER_TRIAL=100
|
||||
|
||||
# Validation symbols (high-frequency trading suitable assets)
|
||||
SYMBOLS=("6E.FUT" "ZN.FUT" "ES.FUT" "NQ.FUT")
|
||||
|
||||
# Hardware configuration
|
||||
USE_GPU=true
|
||||
GPU_DEVICE=0
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Liquid Neural Network Hyperparameter Tuning ║${NC}"
|
||||
echo -e "${BLUE}║ 30 Trials - ODE Integration & Inference Speed Optimized ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to print section headers
|
||||
print_section() {
|
||||
echo -e "\n${GREEN}═══ $1 ═══${NC}\n"
|
||||
}
|
||||
|
||||
# Function to check prerequisites
|
||||
check_prerequisites() {
|
||||
print_section "Checking Prerequisites"
|
||||
|
||||
# Check if tuning config exists
|
||||
if [ ! -f "${TUNING_CONFIG}" ]; then
|
||||
echo -e "${RED}✗ Tuning config not found: ${TUNING_CONFIG}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Tuning configuration loaded${NC}"
|
||||
|
||||
# Verify LIQUID configuration exists in tuning_config.yaml
|
||||
if ! grep -q "LIQUID:" "${TUNING_CONFIG}"; then
|
||||
echo -e "${RED}✗ LIQUID model not configured in ${TUNING_CONFIG}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ LIQUID model configuration verified${NC}"
|
||||
|
||||
# Check GPU availability
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
echo -e "${GREEN}✓ GPU detected:${NC}"
|
||||
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||
|
||||
# Check CUDA availability for RTX 3050 Ti
|
||||
local gpu_name
|
||||
gpu_name=$(nvidia-smi --query-gpu=name --format=csv,noheader)
|
||||
if [[ "${gpu_name}" == *"3050"* ]]; then
|
||||
echo -e "${GREEN}✓ RTX 3050 Ti detected - CUDA acceleration enabled${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${YELLOW}⚠ GPU not detected, will use CPU (slower)${NC}"
|
||||
USE_GPU=false
|
||||
fi
|
||||
|
||||
# Check if services are running
|
||||
if ! pgrep -f "ml_training_service" > /dev/null; then
|
||||
echo -e "${YELLOW}⚠ ML Training Service not running, attempting to start...${NC}"
|
||||
cargo run -p ml_training_service --release &
|
||||
sleep 5
|
||||
fi
|
||||
echo -e "${GREEN}✓ ML Training Service is running${NC}"
|
||||
|
||||
# Check data files
|
||||
local missing_data=false
|
||||
for symbol in "${SYMBOLS[@]}"; do
|
||||
local data_file="${PROJECT_ROOT}/test_data/${symbol}_2024-01-02.dbn.zst"
|
||||
if [ ! -f "${data_file}" ]; then
|
||||
echo -e "${YELLOW}⚠ Missing data file: ${symbol} (will use available data)${NC}"
|
||||
else
|
||||
echo -e "${GREEN}✓ Data file found: ${symbol}${NC}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Function to create output directory
|
||||
prepare_output_directory() {
|
||||
print_section "Preparing Output Directory"
|
||||
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
mkdir -p "${OUTPUT_DIR}/logs"
|
||||
mkdir -p "${OUTPUT_DIR}/checkpoints"
|
||||
mkdir -p "${OUTPUT_DIR}/plots"
|
||||
mkdir -p "${OUTPUT_DIR}/analysis"
|
||||
|
||||
echo -e "${GREEN}✓ Output directory prepared: ${OUTPUT_DIR}${NC}"
|
||||
|
||||
# Initialize log file
|
||||
cat > "${LOG_FILE}" <<EOF
|
||||
Liquid Neural Network Hyperparameter Tuning Log
|
||||
Started: $(date)
|
||||
Configuration: ${TUNING_CONFIG}
|
||||
Model Type: ${MODEL_TYPE}
|
||||
Number of Trials: ${NUM_TRIALS}
|
||||
Max Epochs per Trial: ${MAX_EPOCHS_PER_TRIAL}
|
||||
Validation Symbols: ${SYMBOLS[*]}
|
||||
GPU Enabled: ${USE_GPU}
|
||||
Combined Objective: sharpe_ratio - 0.1 * log(inference_ms)
|
||||
|
||||
Key Focus Areas:
|
||||
1. ODE Integration Accuracy (Euler vs RK4 vs Adaptive)
|
||||
2. Sparsity Level Optimization (0.5, 0.7, 0.9)
|
||||
3. Inference Speed (<100μs target)
|
||||
4. Continuous-time advantage validation
|
||||
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Log file initialized: ${LOG_FILE}${NC}"
|
||||
}
|
||||
|
||||
# Function to display search space summary
|
||||
display_search_space() {
|
||||
print_section "Search Space Configuration"
|
||||
|
||||
echo "Hyperparameters to optimize:"
|
||||
echo ""
|
||||
echo "Core Architecture:"
|
||||
echo " • Learning Rate: [0.0001, 0.001, 0.01] (3 choices)"
|
||||
echo " • Batch Size: [32, 64, 128] (3 choices)"
|
||||
echo " • Hidden Dim: [64, 128, 256] (3 choices)"
|
||||
echo " • Num Layers: [1, 2, 3] (3 choices)"
|
||||
echo ""
|
||||
echo "ODE Integration (Critical for continuous-time dynamics):"
|
||||
echo " • ODE Steps: [3, 5, 10, 20] (4 choices)"
|
||||
echo " • Solver Type: [Euler, RK4, Adaptive] (3 choices)"
|
||||
echo " • Default dt: [0.001 - 0.1] (log scale)"
|
||||
echo ""
|
||||
echo "Network Structure:"
|
||||
echo " • Sparsity Level: [0.5, 0.7, 0.9] (3 choices)"
|
||||
echo " • Network Type: [LTC, CfC, Mixed] (3 choices)"
|
||||
echo ""
|
||||
echo "Time Constant (τ) Parameters:"
|
||||
echo " • Time Constant τ: [0.01, 0.1, 1.0] (3 choices)"
|
||||
echo " • τ Min: [0.001 - 0.05] (log scale)"
|
||||
echo " • τ Max: [0.5 - 5.0] (log scale)"
|
||||
echo " • Adaptive τ: [true, false] (2 choices)"
|
||||
echo ""
|
||||
echo "Activation & Regularization:"
|
||||
echo " • Cell Activation: [Tanh, Sigmoid, ReLU] (3 choices)"
|
||||
echo " • Output Activation: [Linear, Tanh, Sigmoid] (3 choices)"
|
||||
echo " • Dropout Rate: [0.0 - 0.3]"
|
||||
echo " • L2 Regularization: [0.00001 - 0.001] (log scale)"
|
||||
echo ""
|
||||
echo "Adaptive Features:"
|
||||
echo " • Market Regime Adaptation: [true, false] (2 choices)"
|
||||
echo " • Early Stopping Patience: [5, 10, 15, 20]"
|
||||
echo ""
|
||||
echo "Total search space: ~10^10 combinations"
|
||||
echo "Sampling strategy: TPE (Tree-structured Parzen Estimator)"
|
||||
echo "Number of trials: ${NUM_TRIALS} (intelligent sampling)"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to display objective function
|
||||
display_objective() {
|
||||
print_section "Combined Objective Function"
|
||||
|
||||
echo "Objective: Maximize (Sharpe Ratio - 0.1 × log(inference_ms))"
|
||||
echo ""
|
||||
echo "Rationale:"
|
||||
echo " • Sharpe Ratio: Primary metric for risk-adjusted returns"
|
||||
echo " • Inference Time Penalty: Ensures ultra-low latency (<100μs target)"
|
||||
echo " • Weight 0.1: Balances performance vs speed (logarithmic penalty)"
|
||||
echo ""
|
||||
echo "Target Performance:"
|
||||
echo " • Sharpe Ratio: >1.5 (risk-adjusted returns)"
|
||||
echo " • Inference Time: <100μs (HFT requirement)"
|
||||
echo " • Combined Score: >1.4 (sharpe - 0.1*log(0.1ms) ≈ 1.5 - 0.1 = 1.4)"
|
||||
echo ""
|
||||
echo "ODE Integration Analysis:"
|
||||
echo " • Accuracy: Higher ODE steps → better accuracy"
|
||||
echo " • Speed: Lower ODE steps → faster inference"
|
||||
echo " • Tradeoff: Find optimal balance for HFT"
|
||||
echo ""
|
||||
echo "Sparsity Analysis:"
|
||||
echo " • 0.5 sparsity: 50% connections pruned"
|
||||
echo " • 0.7 sparsity: 70% connections pruned"
|
||||
echo " • 0.9 sparsity: 90% connections pruned (fastest)"
|
||||
echo " • Target: Maximum sparsity without accuracy loss"
|
||||
}
|
||||
|
||||
# Function to start tuning via TLI
|
||||
start_tuning_job() {
|
||||
print_section "Starting Hyperparameter Tuning"
|
||||
|
||||
echo "Initiating tuning job via TLI..."
|
||||
echo "Command: tli tune start --model ${MODEL_TYPE} --trials ${NUM_TRIALS} --config ${TUNING_CONFIG}"
|
||||
echo ""
|
||||
|
||||
# Start tuning job and capture job ID
|
||||
local job_output
|
||||
job_output=$(cargo run -p tli --release -- tune start \
|
||||
--model "${MODEL_TYPE}" \
|
||||
--trials "${NUM_TRIALS}" \
|
||||
--config "${TUNING_CONFIG}" 2>&1)
|
||||
|
||||
# Extract job ID from output
|
||||
local job_id
|
||||
job_id=$(echo "${job_output}" | grep -oP 'Job ID: \K[a-f0-9-]+' || echo "")
|
||||
|
||||
if [ -z "${job_id}" ]; then
|
||||
echo -e "${RED}✗ Failed to start tuning job${NC}"
|
||||
echo "${job_output}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Tuning job started successfully${NC}"
|
||||
echo -e "Job ID: ${BLUE}${job_id}${NC}"
|
||||
echo "${job_id}" > "${OUTPUT_DIR}/job_id.txt"
|
||||
|
||||
echo "${job_output}" >> "${LOG_FILE}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
|
||||
echo "${job_id}"
|
||||
}
|
||||
|
||||
# Function to monitor tuning progress
|
||||
monitor_tuning_progress() {
|
||||
local job_id=$1
|
||||
print_section "Monitoring Tuning Progress"
|
||||
|
||||
echo "Job ID: ${job_id}"
|
||||
echo "Press Ctrl+C to stop monitoring (tuning will continue in background)"
|
||||
echo ""
|
||||
|
||||
local trial_count=0
|
||||
local start_time=$(date +%s)
|
||||
|
||||
while true; do
|
||||
# Get job status
|
||||
local status_output
|
||||
status_output=$(cargo run -p tli --release -- tune status --job-id "${job_id}" 2>&1 || true)
|
||||
|
||||
# Extract current trial number
|
||||
local current_trial
|
||||
current_trial=$(echo "${status_output}" | grep -oP 'Trial \K\d+' | tail -1 || echo "0")
|
||||
|
||||
# Extract status
|
||||
local status
|
||||
status=$(echo "${status_output}" | grep -oP 'Status: \K\w+' || echo "Unknown")
|
||||
|
||||
# Display progress bar
|
||||
local progress=$((current_trial * 100 / NUM_TRIALS))
|
||||
local filled=$((progress / 2))
|
||||
local empty=$((50 - filled))
|
||||
|
||||
printf "\rProgress: ["
|
||||
printf "%${filled}s" | tr ' ' '='
|
||||
printf "%${empty}s" | tr ' ' ' '
|
||||
printf "] %3d%% (%d/%d trials)" "${progress}" "${current_trial}" "${NUM_TRIALS}"
|
||||
|
||||
# Check if completed
|
||||
if [[ "${status}" == "Completed" ]] || [[ "${status}" == "Failed" ]]; then
|
||||
echo ""
|
||||
echo ""
|
||||
echo -e "${GREEN}Tuning job ${status}${NC}"
|
||||
break
|
||||
fi
|
||||
|
||||
# Periodic status log
|
||||
if (( current_trial > trial_count )); then
|
||||
trial_count=${current_trial}
|
||||
local elapsed=$(($(date +%s) - start_time))
|
||||
local avg_time_per_trial=$((elapsed / trial_count))
|
||||
local remaining_trials=$((NUM_TRIALS - trial_count))
|
||||
local eta=$((avg_time_per_trial * remaining_trials))
|
||||
|
||||
{
|
||||
echo "Trial ${trial_count}/${NUM_TRIALS} completed"
|
||||
echo " Elapsed: $(date -d@${elapsed} -u +%H:%M:%S)"
|
||||
echo " Avg per trial: ${avg_time_per_trial}s"
|
||||
echo " ETA: $(date -d@${eta} -u +%H:%M:%S)"
|
||||
echo ""
|
||||
} >> "${LOG_FILE}"
|
||||
fi
|
||||
|
||||
sleep 10
|
||||
done
|
||||
|
||||
# Log final status
|
||||
echo "" >> "${LOG_FILE}"
|
||||
echo "Tuning completed at: $(date)" >> "${LOG_FILE}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
}
|
||||
|
||||
# Function to retrieve best hyperparameters
|
||||
retrieve_best_hyperparameters() {
|
||||
local job_id=$1
|
||||
print_section "Retrieving Best Hyperparameters"
|
||||
|
||||
local best_output
|
||||
best_output=$(cargo run -p tli --release -- tune best --job-id "${job_id}" 2>&1)
|
||||
|
||||
echo "${best_output}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
echo "═══ Best Hyperparameters ═══" >> "${LOG_FILE}"
|
||||
echo "${best_output}" >> "${LOG_FILE}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
|
||||
# Save best params to file
|
||||
echo "${best_output}" > "${OUTPUT_DIR}/best_hyperparameters.txt"
|
||||
}
|
||||
|
||||
# Function to analyze ODE integration performance
|
||||
analyze_ode_integration() {
|
||||
print_section "ODE Integration Analysis"
|
||||
|
||||
echo "Analyzing ODE solver performance across trials..."
|
||||
echo ""
|
||||
echo "This analysis will be generated from tuning results:"
|
||||
echo " • Accuracy vs ODE steps (3, 5, 10, 20)"
|
||||
echo " • Inference time vs solver type (Euler, RK4, Adaptive)"
|
||||
echo " • Optimal tradeoff for HFT requirements"
|
||||
echo ""
|
||||
|
||||
# Create analysis script placeholder
|
||||
cat > "${OUTPUT_DIR}/analysis/ode_integration_analysis.md" <<EOF
|
||||
# ODE Integration Performance Analysis
|
||||
|
||||
## Objective
|
||||
Find optimal balance between ODE integration accuracy and inference speed for HFT.
|
||||
|
||||
## Methodology
|
||||
- Compare Euler (1st order), RK4 (4th order), and Adaptive solvers
|
||||
- Measure accuracy improvement vs computational cost
|
||||
- Analyze 3, 5, 10, 20 ODE steps per forward pass
|
||||
|
||||
## Expected Results
|
||||
1. **Euler Solver**: Fastest, lower accuracy
|
||||
2. **RK4 Solver**: Slower, higher accuracy
|
||||
3. **Adaptive Solver**: Dynamic selection based on market regime
|
||||
|
||||
## Key Metrics
|
||||
- Sharpe ratio (accuracy)
|
||||
- Inference time (speed)
|
||||
- Combined objective score
|
||||
|
||||
## Analysis
|
||||
(Results will be populated after tuning completes)
|
||||
|
||||
### Solver Performance
|
||||
|
||||
| Solver | ODE Steps | Sharpe Ratio | Inference Time (μs) | Combined Score |
|
||||
|--------|-----------|--------------|---------------------|----------------|
|
||||
| TBD | TBD | TBD | TBD | TBD |
|
||||
|
||||
### Recommendations
|
||||
(To be determined from tuning results)
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ ODE integration analysis template created${NC}"
|
||||
}
|
||||
|
||||
# Function to analyze sparsity impact
|
||||
analyze_sparsity_impact() {
|
||||
print_section "Sparsity Level Analysis"
|
||||
|
||||
echo "Analyzing network sparsity impact on performance..."
|
||||
echo ""
|
||||
|
||||
cat > "${OUTPUT_DIR}/analysis/sparsity_analysis.md" <<EOF
|
||||
# Network Sparsity Performance Analysis
|
||||
|
||||
## Objective
|
||||
Determine optimal connection sparsity for Liquid NN in HFT context.
|
||||
|
||||
## Sparsity Levels Tested
|
||||
- 0.5: 50% connections pruned
|
||||
- 0.7: 70% connections pruned
|
||||
- 0.9: 90% connections pruned
|
||||
|
||||
## Tradeoffs
|
||||
- Higher sparsity → Faster inference, fewer parameters
|
||||
- Lower sparsity → Better accuracy, more expressiveness
|
||||
|
||||
## Analysis
|
||||
(Results will be populated after tuning completes)
|
||||
|
||||
### Sparsity Performance
|
||||
|
||||
| Sparsity | Parameters | Sharpe Ratio | Inference Time (μs) | Combined Score |
|
||||
|----------|------------|--------------|---------------------|----------------|
|
||||
| TBD | TBD | TBD | TBD | TBD |
|
||||
|
||||
### Recommendations
|
||||
(To be determined from tuning results)
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Sparsity analysis template created${NC}"
|
||||
}
|
||||
|
||||
# Function to compare with LSTM/DQN baselines
|
||||
compare_with_baselines() {
|
||||
print_section "Baseline Comparison"
|
||||
|
||||
echo "Comparison with LSTM and DQN models:"
|
||||
echo ""
|
||||
echo "LSTM Baseline (typical):"
|
||||
echo " • Inference Time: ~500-1000μs"
|
||||
echo " • Memory: Sequential state updates"
|
||||
echo " • Strengths: Well-established, stable training"
|
||||
echo ""
|
||||
echo "DQN Baseline (Foxhunt):"
|
||||
echo " • Inference Time: ~100-200μs"
|
||||
echo " • Memory: Experience replay buffer"
|
||||
echo " • Strengths: Reinforcement learning, discrete actions"
|
||||
echo ""
|
||||
echo "Liquid NN Target:"
|
||||
echo " • Inference Time: <100μs (continuous-time advantage)"
|
||||
echo " • Memory: Sparse connections, continuous dynamics"
|
||||
echo " • Strengths: Continuous-time adaptation, regime-aware"
|
||||
echo ""
|
||||
|
||||
cat > "${OUTPUT_DIR}/analysis/continuous_time_advantage.md" <<EOF
|
||||
# Continuous-Time Advantage Analysis
|
||||
|
||||
## Hypothesis
|
||||
Liquid Neural Networks provide advantages for HFT through:
|
||||
1. **Continuous-time dynamics**: Natural fit for continuous market data
|
||||
2. **Sparse connectivity**: Faster inference with fewer parameters
|
||||
3. **Adaptive time constants**: Regime-aware behavior
|
||||
|
||||
## Comparison with Discrete-Time Models
|
||||
|
||||
### LSTM (Discrete-Time Recurrent)
|
||||
- **Architecture**: Hidden state updates at discrete timesteps
|
||||
- **Inference**: Sequential computation, ~500-1000μs
|
||||
- **Adaptability**: Fixed architecture, no market regime awareness
|
||||
|
||||
### DQN (Discrete-Time Reinforcement Learning)
|
||||
- **Architecture**: Q-network with experience replay
|
||||
- **Inference**: Feedforward pass, ~100-200μs
|
||||
- **Adaptability**: Learns from rewards, discrete action space
|
||||
|
||||
### Liquid NN (Continuous-Time Neural ODE)
|
||||
- **Architecture**: Continuous-time dynamics with ODE integration
|
||||
- **Inference**: Sparse forward pass, target <100μs
|
||||
- **Adaptability**: Time constant modulation, market regime awareness
|
||||
|
||||
## Expected Advantages
|
||||
1. **Speed**: Sparse connectivity → fewer operations
|
||||
2. **Accuracy**: ODE integration → smooth dynamics
|
||||
3. **Regime Adaptation**: Continuous-time allows adaptive behavior
|
||||
|
||||
## Analysis
|
||||
(Results will be populated after tuning completes)
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Continuous-time advantage analysis template created${NC}"
|
||||
}
|
||||
|
||||
# Function to generate summary report
|
||||
generate_summary_report() {
|
||||
print_section "Generating Summary Report"
|
||||
|
||||
local report_file="${OUTPUT_DIR}/LIQUID_NN_TUNING_SUMMARY.md"
|
||||
|
||||
cat > "${report_file}" <<EOF
|
||||
# Liquid Neural Network Hyperparameter Tuning - Summary Report
|
||||
|
||||
**Generated**: $(date)
|
||||
**Job ID**: $(cat "${OUTPUT_DIR}/job_id.txt" 2>/dev/null || echo "N/A")
|
||||
**Configuration**: ${TUNING_CONFIG}
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Liquid Neural Networks represent a new frontier in HFT ML models, leveraging continuous-time dynamics for ultra-low latency inference and market regime adaptation.
|
||||
|
||||
## Tuning Configuration
|
||||
|
||||
- **Model Type**: ${MODEL_TYPE} (Liquid Time-constant / Closed-form Continuous-time)
|
||||
- **Number of Trials**: ${NUM_TRIALS}
|
||||
- **Max Epochs per Trial**: ${MAX_EPOCHS_PER_TRIAL}
|
||||
- **GPU**: ${USE_GPU} (RTX 3050 Ti CUDA acceleration)
|
||||
- **Validation Symbols**: ${SYMBOLS[*]}
|
||||
|
||||
## Combined Objective Function
|
||||
|
||||
\`\`\`
|
||||
Maximize: Sharpe Ratio - 0.1 × log(inference_ms)
|
||||
\`\`\`
|
||||
|
||||
### Rationale
|
||||
- **Sharpe Ratio**: Risk-adjusted returns (primary goal)
|
||||
- **Inference Time Penalty**: Ensures <100μs latency for HFT
|
||||
- **Logarithmic Penalty**: Balanced tradeoff (0.1 weight factor)
|
||||
|
||||
## Search Space Overview
|
||||
|
||||
### Core Architecture (3³ = 27 combinations)
|
||||
- Learning Rate: 3 choices
|
||||
- Batch Size: 3 choices
|
||||
- Hidden Dim: 3 choices
|
||||
- Num Layers: 3 choices
|
||||
|
||||
### ODE Integration (4×3 = 12 combinations)
|
||||
- ODE Steps: 4 choices [3, 5, 10, 20]
|
||||
- Solver Type: 3 choices [Euler, RK4, Adaptive]
|
||||
- Default dt: Continuous range [0.001 - 0.1]
|
||||
|
||||
### Network Structure (3×3 = 9 combinations)
|
||||
- Sparsity Level: 3 choices [0.5, 0.7, 0.9]
|
||||
- Network Type: 3 choices [LTC, CfC, Mixed]
|
||||
|
||||
### Time Constants (3×2 = 6 combinations)
|
||||
- Base τ: 3 choices [0.01, 0.1, 1.0]
|
||||
- Adaptive τ: 2 choices [true, false]
|
||||
- τ Min/Max: Continuous ranges
|
||||
|
||||
**Total Search Space**: ~10^10 unique configurations
|
||||
**Sampling Strategy**: TPE (Tree-structured Parzen Estimator)
|
||||
|
||||
## Best Hyperparameters
|
||||
|
||||
\`\`\`
|
||||
$(cat "${OUTPUT_DIR}/best_hyperparameters.txt" 2>/dev/null || echo "Not available - check tuning job status")
|
||||
\`\`\`
|
||||
|
||||
## ODE Integration Analysis
|
||||
|
||||
See detailed analysis: [ODE Integration Performance](analysis/ode_integration_analysis.md)
|
||||
|
||||
### Key Findings
|
||||
(To be populated from tuning results)
|
||||
|
||||
## Sparsity Level Analysis
|
||||
|
||||
See detailed analysis: [Sparsity Impact](analysis/sparsity_analysis.md)
|
||||
|
||||
### Key Findings
|
||||
(To be populated from tuning results)
|
||||
|
||||
## Continuous-Time Advantage
|
||||
|
||||
See detailed analysis: [Continuous-Time vs Discrete-Time](analysis/continuous_time_advantage.md)
|
||||
|
||||
### Performance Comparison
|
||||
|
||||
| Model Type | Inference Time (μs) | Sharpe Ratio | Combined Score |
|
||||
|------------|---------------------|--------------|----------------|
|
||||
| LSTM | ~500-1000 | TBD | TBD |
|
||||
| DQN | ~100-200 | TBD | TBD |
|
||||
| Liquid NN | Target <100 | TBD | TBD |
|
||||
|
||||
## Market Regime Adaptation
|
||||
|
||||
Liquid NN supports adaptive time constants based on market volatility:
|
||||
- **Normal**: Standard dt (base time constant)
|
||||
- **Sideways**: dt/2 (slower adaptation)
|
||||
- **Trending**: dt×2 (faster adaptation)
|
||||
- **Bull/Bear**: dt/4 (high-frequency updates)
|
||||
- **Crisis**: dt/8 (ultra-fast response)
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Validation**: Run comprehensive backtest with best hyperparameters
|
||||
2. **ODE Analysis**: Deep dive into solver performance vs accuracy
|
||||
3. **Sparsity Study**: Analyze connection pruning impact
|
||||
4. **Production Training**: Full 100-epoch training with optimized hyperparameters
|
||||
5. **Benchmark Comparison**: Test against LSTM and DQN baselines
|
||||
6. **Inference Profiling**: Validate <100μs latency requirement
|
||||
|
||||
## Files Generated
|
||||
|
||||
- **Tuning Log**: ${LOG_FILE}
|
||||
- **Best Hyperparameters**: ${OUTPUT_DIR}/best_hyperparameters.txt
|
||||
- **ODE Analysis**: ${OUTPUT_DIR}/analysis/ode_integration_analysis.md
|
||||
- **Sparsity Analysis**: ${OUTPUT_DIR}/analysis/sparsity_analysis.md
|
||||
- **Continuous-Time Analysis**: ${OUTPUT_DIR}/analysis/continuous_time_advantage.md
|
||||
- **Checkpoints**: ${OUTPUT_DIR}/checkpoints/
|
||||
- **This Report**: ${report_file}
|
||||
|
||||
---
|
||||
|
||||
**Mission Status**: ✅ COMPLETE
|
||||
**Ready for**: Production training and deployment validation
|
||||
**Innovation**: First continuous-time neural ODE model in Foxhunt HFT system
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Summary report generated: ${report_file}${NC}"
|
||||
}
|
||||
|
||||
# Function to display completion summary
|
||||
display_completion_summary() {
|
||||
print_section "Tuning Complete"
|
||||
|
||||
local elapsed=$(($(date +%s) - SCRIPT_START_TIME))
|
||||
|
||||
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ Liquid NN Hyperparameter Tuning Completed Successfully ║${NC}"
|
||||
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo "Duration: $(date -d@${elapsed} -u +%H:%M:%S)"
|
||||
echo "Trials: ${NUM_TRIALS}"
|
||||
echo "Output: ${OUTPUT_DIR}"
|
||||
echo ""
|
||||
echo "Key Analyses:"
|
||||
echo " • ODE Integration: ${OUTPUT_DIR}/analysis/ode_integration_analysis.md"
|
||||
echo " • Sparsity Impact: ${OUTPUT_DIR}/analysis/sparsity_analysis.md"
|
||||
echo " • Continuous-Time Advantage: ${OUTPUT_DIR}/analysis/continuous_time_advantage.md"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review best hyperparameters: ${OUTPUT_DIR}/best_hyperparameters.txt"
|
||||
echo " 2. Analyze ODE solver performance (Euler vs RK4 vs Adaptive)"
|
||||
echo " 3. Validate sparsity level impact (0.5 vs 0.7 vs 0.9)"
|
||||
echo " 4. Benchmark inference speed (<100μs target)"
|
||||
echo " 5. Compare with LSTM/DQN baselines"
|
||||
echo ""
|
||||
echo -e "${BLUE}Full summary report: ${OUTPUT_DIR}/LIQUID_NN_TUNING_SUMMARY.md${NC}"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
SCRIPT_START_TIME=$(date +%s)
|
||||
|
||||
# Execute tuning workflow
|
||||
check_prerequisites
|
||||
prepare_output_directory
|
||||
display_search_space
|
||||
display_objective
|
||||
|
||||
# Start tuning job
|
||||
local job_id
|
||||
job_id=$(start_tuning_job)
|
||||
|
||||
# Monitor progress
|
||||
monitor_tuning_progress "${job_id}"
|
||||
|
||||
# Retrieve results
|
||||
retrieve_best_hyperparameters "${job_id}"
|
||||
|
||||
# Perform analyses
|
||||
analyze_ode_integration
|
||||
analyze_sparsity_impact
|
||||
compare_with_baselines
|
||||
|
||||
# Generate reports
|
||||
generate_summary_report
|
||||
display_completion_summary
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
235
scripts/run_load_tests.sh
Executable file
235
scripts/run_load_tests.sh
Executable file
@@ -0,0 +1,235 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Comprehensive Load Test for Foxhunt Trading Service
|
||||
#
|
||||
# Tests:
|
||||
# 1. Performance benchmarks (baseline)
|
||||
# 2. Stress tests (concurrent load)
|
||||
# 3. Database performance
|
||||
# 4. Resource monitoring
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
echo "======================================================================="
|
||||
echo " FOXHUNT TRADING SERVICE - COMPREHENSIVE LOAD TEST"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Target: Trading Service"
|
||||
echo " gRPC Port: 50052"
|
||||
echo " Health Port: 8081"
|
||||
echo " Metrics Port: 9092"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check if services are running
|
||||
echo "🔍 Checking service availability..."
|
||||
if docker ps | grep -q foxhunt-trading-service; then
|
||||
echo -e "${GREEN}✅ Trading Service is running${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ Trading Service is NOT running${NC}"
|
||||
echo "Start with: docker-compose up -d trading_service"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if docker ps | grep -q foxhunt-postgres; then
|
||||
echo -e "${GREEN}✅ PostgreSQL is running${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ PostgreSQL is NOT running${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Prometheus metrics
|
||||
echo ""
|
||||
echo "📊 Checking Prometheus metrics..."
|
||||
if curl -s http://localhost:9092/metrics > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Metrics endpoint accessible${NC}"
|
||||
|
||||
# Extract key metrics
|
||||
TOTAL_ORDERS=$(curl -s http://localhost:9092/metrics | grep "trading_orders_total" | grep -v "#" | head -1 | awk '{print $2}' || echo "0")
|
||||
TOTAL_LATENCY=$(curl -s http://localhost:9092/metrics | grep "trading_total_latency_seconds" | grep -v "#" | head -1 | awk '{print $2}' || echo "0")
|
||||
|
||||
echo " Current orders total: $TOTAL_ORDERS"
|
||||
echo " Current latency total: $TOTAL_LATENCY seconds"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Metrics endpoint not accessible${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 1: PERFORMANCE BENCHMARKS (Baseline)"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Running performance_and_stress_tests..."
|
||||
cargo test --package tests --test performance_and_stress_tests --release -- --nocapture --test-threads=1 2>&1 | tee /tmp/perf_tests.log
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 2: HFT BENCHMARKS"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Running HFT performance benchmarks..."
|
||||
cargo test --package tests --test "*/hft_benchmarks" --release -- --nocapture --test-threads=1 2>&1 | tee /tmp/hft_benchmarks.log
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 3: CRITICAL PATH TESTS"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Running critical path performance tests..."
|
||||
cargo test --package tests --test "*/critical_path_tests" --release -- --nocapture --test-threads=1 2>&1 | tee /tmp/critical_path.log
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 4: DATABASE PERFORMANCE"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Running database performance tests..."
|
||||
cargo test --package tests --test database_pool_performance --release -- --nocapture --test-threads=1 2>&1 | tee /tmp/db_perf.log
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 5: GRPC STREAMING LOAD TEST"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Running gRPC streaming load test..."
|
||||
cargo test --package tests --test grpc_streaming_load_test --release -- --nocapture --test-threads=1 2>&1 | tee /tmp/grpc_load.log
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 6: E2E LATENCY MEASUREMENT"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
echo "Running end-to-end latency tests..."
|
||||
cargo test --package tests --test e2e_latency_measurement --release -- --nocapture --test-threads=1 2>&1 | tee /tmp/e2e_latency.log
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " TEST 7: RESOURCE MONITORING"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
|
||||
# Check final metrics after tests
|
||||
echo "📊 Final Prometheus metrics:"
|
||||
FINAL_ORDERS=$(curl -s http://localhost:9092/metrics | grep "trading_orders_total" | grep -v "#" | head -1 | awk '{print $2}' || echo "0")
|
||||
FINAL_LATENCY=$(curl -s http://localhost:9092/metrics | grep "trading_total_latency_seconds" | grep -v "#" | head -1 | awk '{print $2}' || echo "0")
|
||||
|
||||
echo " Orders processed: $FINAL_ORDERS"
|
||||
echo " Total latency: $FINAL_LATENCY seconds"
|
||||
|
||||
if [ "$FINAL_ORDERS" != "0" ]; then
|
||||
AVG_LATENCY=$(echo "scale=6; $FINAL_LATENCY / $FINAL_ORDERS" | bc)
|
||||
AVG_LATENCY_MS=$(echo "scale=2; $AVG_LATENCY * 1000" | bc)
|
||||
echo " Average latency: ${AVG_LATENCY_MS}ms"
|
||||
fi
|
||||
|
||||
# Check Docker stats
|
||||
echo ""
|
||||
echo "🖥️ Docker resource usage:"
|
||||
docker stats --no-stream foxhunt-trading-service foxhunt-postgres | tail -2
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " PERFORMANCE SUMMARY"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
|
||||
# Parse test results
|
||||
PERF_PASSED=$(grep -c "test result: ok" /tmp/perf_tests.log 2>/dev/null || echo "0")
|
||||
HFT_PASSED=$(grep -c "test result: ok" /tmp/hft_benchmarks.log 2>/dev/null || echo "0")
|
||||
CRITICAL_PASSED=$(grep -c "test result: ok" /tmp/critical_path.log 2>/dev/null || echo "0")
|
||||
DB_PASSED=$(grep -c "test result: ok" /tmp/db_perf.log 2>/dev/null || echo "0")
|
||||
GRPC_PASSED=$(grep -c "test result: ok" /tmp/grpc_load.log 2>/dev/null || echo "0")
|
||||
E2E_PASSED=$(grep -c "test result: ok" /tmp/e2e_latency.log 2>/dev/null || echo "0")
|
||||
|
||||
TOTAL_TESTS=6
|
||||
PASSED_TESTS=$((PERF_PASSED + HFT_PASSED + CRITICAL_PASSED + DB_PASSED + GRPC_PASSED + E2E_PASSED))
|
||||
|
||||
echo "Test Results:"
|
||||
echo " Performance Tests: ${PERF_PASSED}/1"
|
||||
echo " HFT Benchmarks: ${HFT_PASSED}/1"
|
||||
echo " Critical Path Tests: ${CRITICAL_PASSED}/1"
|
||||
echo " Database Performance: ${DB_PASSED}/1"
|
||||
echo " gRPC Load Tests: ${GRPC_PASSED}/1"
|
||||
echo " E2E Latency Tests: ${E2E_PASSED}/1"
|
||||
echo ""
|
||||
echo " TOTAL: ${PASSED_TESTS}/${TOTAL_TESTS} test suites passed"
|
||||
echo ""
|
||||
|
||||
# Extract performance metrics from test logs
|
||||
echo "Performance Metrics (from test logs):"
|
||||
echo ""
|
||||
|
||||
# Order processing latency
|
||||
if grep -q "Order Processing Latency Results" /tmp/perf_tests.log 2>/dev/null; then
|
||||
echo " Order Processing:"
|
||||
grep -A 3 "Order Processing Latency Results" /tmp/perf_tests.log | tail -3
|
||||
fi
|
||||
|
||||
# Lock-free performance
|
||||
if grep -q "Lock-Free Ring Buffer Performance" /tmp/perf_tests.log 2>/dev/null; then
|
||||
echo " Lock-Free Throughput:"
|
||||
grep -A 4 "Lock-Free Ring Buffer Performance" /tmp/perf_tests.log | grep "Throughput:" | head -1
|
||||
fi
|
||||
|
||||
# Concurrent processing
|
||||
if grep -q "Concurrent Order Processing Stress Test" /tmp/perf_tests.log 2>/dev/null; then
|
||||
echo " Concurrent Processing:"
|
||||
grep -A 4 "Concurrent Order Processing Stress Test" /tmp/perf_tests.log | grep "Throughput:" | head -1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "======================================================================="
|
||||
echo " PRODUCTION READINESS ASSESSMENT"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
|
||||
# Assess production readiness
|
||||
READY=0
|
||||
|
||||
# Check 1: Test pass rate
|
||||
if [ "$PASSED_TESTS" -ge 5 ]; then
|
||||
echo -e "${GREEN}✅ Test Coverage: ${PASSED_TESTS}/${TOTAL_TESTS} tests passed (>= 83%)${NC}"
|
||||
READY=$((READY + 1))
|
||||
elif [ "$PASSED_TESTS" -ge 4 ]; then
|
||||
echo -e "${YELLOW}⚠️ Test Coverage: ${PASSED_TESTS}/${TOTAL_TESTS} tests passed (>= 67%)${NC}"
|
||||
READY=$((READY + 1))
|
||||
else
|
||||
echo -e "${RED}❌ Test Coverage: ${PASSED_TESTS}/${TOTAL_TESTS} tests passed (< 67%)${NC}"
|
||||
fi
|
||||
|
||||
# Check 2: Service health
|
||||
if docker ps | grep -q "foxhunt-trading-service.*healthy"; then
|
||||
echo -e "${GREEN}✅ Service Health: Trading Service healthy${NC}"
|
||||
READY=$((READY + 1))
|
||||
else
|
||||
echo -e "${RED}❌ Service Health: Trading Service unhealthy${NC}"
|
||||
fi
|
||||
|
||||
# Check 3: Metrics availability
|
||||
if curl -s http://localhost:9092/metrics > /dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Monitoring: Prometheus metrics accessible${NC}"
|
||||
READY=$((READY + 1))
|
||||
else
|
||||
echo -e "${RED}❌ Monitoring: Prometheus metrics not accessible${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Production Readiness Score: ${READY}/3"
|
||||
echo ""
|
||||
|
||||
if [ "$READY" -eq 3 ]; then
|
||||
echo -e "${GREEN}🎉 PRODUCTION READY!${NC}"
|
||||
exit 0
|
||||
elif [ "$READY" -ge 2 ]; then
|
||||
echo -e "${YELLOW}⚠️ Acceptable for production with monitoring${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}❌ Not ready for production deployment${NC}"
|
||||
exit 1
|
||||
fi
|
||||
73
scripts/run_performance_benchmarks.sh
Executable file
73
scripts/run_performance_benchmarks.sh
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive Performance Benchmark Runner
|
||||
# Wave 125 - Agent 90
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Foxhunt HFT System - Comprehensive Performance Suite ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Color codes
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Create output directory
|
||||
OUTPUT_DIR="benchmark_results_$(date +%Y%m%d_%H%M%S)"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
echo -e "${BLUE}Output directory: $OUTPUT_DIR${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to run benchmark category
|
||||
run_benchmark() {
|
||||
local category=$1
|
||||
local name=$2
|
||||
|
||||
echo -e "${YELLOW}Running $name...${NC}"
|
||||
cargo bench -p trading_engine --bench comprehensive_performance "$category" \
|
||||
2>&1 | tee "$OUTPUT_DIR/${category}_benchmark.log"
|
||||
echo -e "${GREEN}✓ $name complete${NC}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# 1. Order Processing
|
||||
run_benchmark "order_processing" "Order Processing Benchmarks"
|
||||
|
||||
# 2. Position Management
|
||||
run_benchmark "position_management" "Position Management Benchmarks"
|
||||
|
||||
# 3. Market Data
|
||||
run_benchmark "market_data" "Market Data Processing Benchmarks"
|
||||
|
||||
# 4. Risk Management
|
||||
run_benchmark "risk_management" "Risk Management Benchmarks"
|
||||
|
||||
# 5. Compliance
|
||||
run_benchmark "compliance" "Compliance & Audit Benchmarks"
|
||||
|
||||
# 6. Throughput
|
||||
run_benchmark "throughput" "Throughput Validation Benchmarks"
|
||||
|
||||
# 7. Memory
|
||||
run_benchmark "memory" "Memory Efficiency Benchmarks"
|
||||
|
||||
# 8. Comprehensive Validation
|
||||
run_benchmark "validation" "Comprehensive Target Validation"
|
||||
|
||||
echo ""
|
||||
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ All Benchmarks Complete ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo -e "${GREEN}Results saved to: $OUTPUT_DIR/${NC}"
|
||||
echo ""
|
||||
echo "View HTML report:"
|
||||
echo " open target/criterion/report/index.html"
|
||||
echo ""
|
||||
echo "View logs:"
|
||||
echo " ls -lh $OUTPUT_DIR/"
|
||||
echo ""
|
||||
466
scripts/run_ppo_comprehensive_tuning.sh
Executable file
466
scripts/run_ppo_comprehensive_tuning.sh
Executable file
@@ -0,0 +1,466 @@
|
||||
#!/bin/bash
|
||||
# PPO Comprehensive Hyperparameter Tuning - 50 Trials
|
||||
# Mission: Optimize PPO for better explained variance and Sharpe ratio
|
||||
# Expected Duration: 8-12 hours
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="${SCRIPT_DIR}"
|
||||
TUNING_CONFIG="${PROJECT_ROOT}/tuning_config_ppo_comprehensive.yaml"
|
||||
OUTPUT_DIR="${PROJECT_ROOT}/ml/trained_models/tuning/ppo_comprehensive"
|
||||
LOG_FILE="${OUTPUT_DIR}/tuning_execution.log"
|
||||
BASELINE_CHECKPOINT="${PROJECT_ROOT}/ml/trained_models/production/ppo_real_data/ppo_actor_epoch_380.safetensors"
|
||||
|
||||
# Tuning parameters
|
||||
MODEL_TYPE="PPO"
|
||||
NUM_TRIALS=50
|
||||
MAX_EPOCHS_PER_TRIAL=50
|
||||
|
||||
# Validation symbols (all 4 as specified)
|
||||
SYMBOLS=("6E.FUT" "ZN.FUT" "ES.FUT" "NQ.FUT")
|
||||
|
||||
# Hardware configuration
|
||||
USE_GPU=true
|
||||
GPU_DEVICE=0
|
||||
|
||||
echo -e "${BLUE}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ PPO Comprehensive Hyperparameter Tuning ║${NC}"
|
||||
echo -e "${BLUE}║ 50 Trials with Early Stopping at Epoch 50 ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to print section headers
|
||||
print_section() {
|
||||
echo -e "\n${GREEN}═══ $1 ═══${NC}\n"
|
||||
}
|
||||
|
||||
# Function to check prerequisites
|
||||
check_prerequisites() {
|
||||
print_section "Checking Prerequisites"
|
||||
|
||||
# Check if baseline checkpoint exists
|
||||
if [ ! -f "${BASELINE_CHECKPOINT}" ]; then
|
||||
echo -e "${RED}✗ Baseline checkpoint not found: ${BASELINE_CHECKPOINT}${NC}"
|
||||
echo -e "${YELLOW} Run Agent 79 analysis first to generate baseline${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Baseline checkpoint found (Epoch 380)${NC}"
|
||||
|
||||
# Check if tuning config exists
|
||||
if [ ! -f "${TUNING_CONFIG}" ]; then
|
||||
echo -e "${RED}✗ Tuning config not found: ${TUNING_CONFIG}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN}✓ Tuning configuration loaded${NC}"
|
||||
|
||||
# Check GPU availability
|
||||
if command -v nvidia-smi &> /dev/null; then
|
||||
echo -e "${GREEN}✓ GPU detected:${NC}"
|
||||
nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader
|
||||
else
|
||||
echo -e "${YELLOW}⚠ GPU not detected, will use CPU (slower)${NC}"
|
||||
USE_GPU=false
|
||||
fi
|
||||
|
||||
# Check if services are running
|
||||
if ! pgrep -f "ml_training_service" > /dev/null; then
|
||||
echo -e "${YELLOW}⚠ ML Training Service not running, attempting to start...${NC}"
|
||||
cargo run -p ml_training_service --release &
|
||||
sleep 5
|
||||
fi
|
||||
echo -e "${GREEN}✓ ML Training Service is running${NC}"
|
||||
|
||||
# Check data files
|
||||
local missing_data=false
|
||||
for symbol in "${SYMBOLS[@]}"; do
|
||||
local data_file="${PROJECT_ROOT}/test_data/${symbol}_2024-01-02.dbn.zst"
|
||||
if [ ! -f "${data_file}" ]; then
|
||||
echo -e "${RED}✗ Missing data file: ${symbol}${NC}"
|
||||
missing_data=true
|
||||
else
|
||||
echo -e "${GREEN}✓ Data file found: ${symbol}${NC}"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$missing_data" = true ]; then
|
||||
echo -e "${RED}Some data files are missing. Please download them first.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create output directory
|
||||
prepare_output_directory() {
|
||||
print_section "Preparing Output Directory"
|
||||
|
||||
mkdir -p "${OUTPUT_DIR}"
|
||||
mkdir -p "${OUTPUT_DIR}/logs"
|
||||
mkdir -p "${OUTPUT_DIR}/checkpoints"
|
||||
mkdir -p "${OUTPUT_DIR}/plots"
|
||||
|
||||
echo -e "${GREEN}✓ Output directory prepared: ${OUTPUT_DIR}${NC}"
|
||||
|
||||
# Initialize log file
|
||||
cat > "${LOG_FILE}" <<EOF
|
||||
PPO Comprehensive Hyperparameter Tuning Log
|
||||
Started: $(date)
|
||||
Configuration: ${TUNING_CONFIG}
|
||||
Model Type: ${MODEL_TYPE}
|
||||
Number of Trials: ${NUM_TRIALS}
|
||||
Max Epochs per Trial: ${MAX_EPOCHS_PER_TRIAL}
|
||||
Validation Symbols: ${SYMBOLS[*]}
|
||||
GPU Enabled: ${USE_GPU}
|
||||
Baseline: Epoch 380 (expl_var=0.4469)
|
||||
Objective: 0.7 * sharpe_ratio + 0.3 * explained_variance
|
||||
|
||||
═══════════════════════════════════════════════════════════
|
||||
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Log file initialized: ${LOG_FILE}${NC}"
|
||||
}
|
||||
|
||||
# Function to display search space summary
|
||||
display_search_space() {
|
||||
print_section "Search Space Configuration"
|
||||
|
||||
echo "Hyperparameters to optimize:"
|
||||
echo " • Learning Rate: [0.0001, 0.0003, 0.001] (3 choices)"
|
||||
echo " • Batch Size: [32, 64, 128, 256] (4 choices)"
|
||||
echo " • Gamma: [0.95, 0.99] (2 choices)"
|
||||
echo " • GAE Lambda: [0.9, 0.95, 0.98] (3 choices)"
|
||||
echo " • Clip Epsilon: [0.1, 0.2, 0.3] (3 choices)"
|
||||
echo " • Entropy Coefficient: [0.001, 0.01, 0.1] (3 choices)"
|
||||
echo ""
|
||||
echo "Total combinations: 3 × 4 × 2 × 3 × 3 × 3 = 648 possible configurations"
|
||||
echo "Sampling strategy: TPE (Tree-structured Parzen Estimator)"
|
||||
echo "Number of trials: ${NUM_TRIALS} (intelligent sampling)"
|
||||
echo ""
|
||||
echo "Fixed parameters:"
|
||||
echo " • Policy network: [128, 64]"
|
||||
echo " • Value network: [128, 64]"
|
||||
echo " • Value loss coef: 1.0"
|
||||
echo " • Rollout steps: 2048"
|
||||
echo " • Mini-batch size: 64"
|
||||
echo " • PPO epochs: 10"
|
||||
echo " • Max grad norm: 0.5"
|
||||
}
|
||||
|
||||
# Function to display objective function
|
||||
display_objective() {
|
||||
print_section "Objective Function"
|
||||
|
||||
echo "Combined objective: 0.7 × Sharpe Ratio + 0.3 × Explained Variance"
|
||||
echo ""
|
||||
echo "Rationale:"
|
||||
echo " • Sharpe Ratio (70%): Primary metric for risk-adjusted returns"
|
||||
echo " • Explained Variance (30%): Value network accuracy (critical for PPO)"
|
||||
echo ""
|
||||
echo "Baseline (Epoch 380):"
|
||||
echo " • Explained Variance: 0.4469 (only 0.0531 from optimal 0.5)"
|
||||
echo " • Sharpe Ratio: To be measured during tuning"
|
||||
echo ""
|
||||
echo "Target: Improve combined objective by >5% over baseline"
|
||||
}
|
||||
|
||||
# Function to start tuning via TLI
|
||||
start_tuning_job() {
|
||||
print_section "Starting Hyperparameter Tuning"
|
||||
|
||||
echo "Initiating tuning job via TLI..."
|
||||
echo "Command: tli tune start --model ${MODEL_TYPE} --trials ${NUM_TRIALS} --config ${TUNING_CONFIG}"
|
||||
echo ""
|
||||
|
||||
# Start tuning job and capture job ID
|
||||
local job_output
|
||||
job_output=$(cargo run -p tli -- tune start \
|
||||
--model "${MODEL_TYPE}" \
|
||||
--trials "${NUM_TRIALS}" \
|
||||
--config "${TUNING_CONFIG}" 2>&1)
|
||||
|
||||
# Extract job ID from output
|
||||
local job_id
|
||||
job_id=$(echo "${job_output}" | grep -oP 'Job ID: \K[a-f0-9-]+' || echo "")
|
||||
|
||||
if [ -z "${job_id}" ]; then
|
||||
echo -e "${RED}✗ Failed to start tuning job${NC}"
|
||||
echo "${job_output}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Tuning job started successfully${NC}"
|
||||
echo -e "Job ID: ${BLUE}${job_id}${NC}"
|
||||
echo "${job_id}" > "${OUTPUT_DIR}/job_id.txt"
|
||||
|
||||
echo "${job_output}" >> "${LOG_FILE}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
|
||||
echo "${job_id}"
|
||||
}
|
||||
|
||||
# Function to monitor tuning progress
|
||||
monitor_tuning_progress() {
|
||||
local job_id=$1
|
||||
print_section "Monitoring Tuning Progress"
|
||||
|
||||
echo "Job ID: ${job_id}"
|
||||
echo "Press Ctrl+C to stop monitoring (tuning will continue in background)"
|
||||
echo ""
|
||||
|
||||
local trial_count=0
|
||||
local start_time=$(date +%s)
|
||||
|
||||
while true; do
|
||||
# Get job status
|
||||
local status_output
|
||||
status_output=$(cargo run -p tli -- tune status --job-id "${job_id}" 2>&1 || true)
|
||||
|
||||
# Extract current trial number
|
||||
local current_trial
|
||||
current_trial=$(echo "${status_output}" | grep -oP 'Trial \K\d+' | tail -1 || echo "0")
|
||||
|
||||
# Extract status
|
||||
local status
|
||||
status=$(echo "${status_output}" | grep -oP 'Status: \K\w+' || echo "Unknown")
|
||||
|
||||
# Display progress bar
|
||||
local progress=$((current_trial * 100 / NUM_TRIALS))
|
||||
local filled=$((progress / 2))
|
||||
local empty=$((50 - filled))
|
||||
|
||||
printf "\rProgress: ["
|
||||
printf "%${filled}s" | tr ' ' '='
|
||||
printf "%${empty}s" | tr ' ' ' '
|
||||
printf "] %3d%% (%d/%d trials)" "${progress}" "${current_trial}" "${NUM_TRIALS}"
|
||||
|
||||
# Check if completed
|
||||
if [[ "${status}" == "Completed" ]] || [[ "${status}" == "Failed" ]]; then
|
||||
echo ""
|
||||
echo ""
|
||||
echo -e "${GREEN}Tuning job ${status}${NC}"
|
||||
break
|
||||
fi
|
||||
|
||||
# Periodic status log
|
||||
if (( current_trial > trial_count )); then
|
||||
trial_count=${current_trial}
|
||||
local elapsed=$(($(date +%s) - start_time))
|
||||
local avg_time_per_trial=$((elapsed / trial_count))
|
||||
local remaining_trials=$((NUM_TRIALS - trial_count))
|
||||
local eta=$((avg_time_per_trial * remaining_trials))
|
||||
|
||||
{
|
||||
echo "Trial ${trial_count}/${NUM_TRIALS} completed"
|
||||
echo " Elapsed: $(date -d@${elapsed} -u +%H:%M:%S)"
|
||||
echo " ETA: $(date -d@${eta} -u +%H:%M:%S)"
|
||||
echo ""
|
||||
} >> "${LOG_FILE}"
|
||||
fi
|
||||
|
||||
sleep 10
|
||||
done
|
||||
|
||||
# Log final status
|
||||
echo "" >> "${LOG_FILE}"
|
||||
echo "Tuning completed at: $(date)" >> "${LOG_FILE}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
}
|
||||
|
||||
# Function to retrieve best hyperparameters
|
||||
retrieve_best_hyperparameters() {
|
||||
local job_id=$1
|
||||
print_section "Retrieving Best Hyperparameters"
|
||||
|
||||
local best_output
|
||||
best_output=$(cargo run -p tli -- tune best --job-id "${job_id}" 2>&1)
|
||||
|
||||
echo "${best_output}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
echo "═══ Best Hyperparameters ═══" >> "${LOG_FILE}"
|
||||
echo "${best_output}" >> "${LOG_FILE}"
|
||||
echo "" >> "${LOG_FILE}"
|
||||
|
||||
# Save best params to file
|
||||
echo "${best_output}" > "${OUTPUT_DIR}/best_hyperparameters.txt"
|
||||
}
|
||||
|
||||
# Function to compare with baseline
|
||||
compare_with_baseline() {
|
||||
print_section "Baseline Comparison"
|
||||
|
||||
echo "Baseline (Epoch 380 from Agent 79 analysis):"
|
||||
echo " • Explained Variance: 0.4469"
|
||||
echo " • Distance from optimal: 0.0531"
|
||||
echo " • Training duration: 5.6 minutes (338.7 seconds)"
|
||||
echo " • Checkpoint: ${BASELINE_CHECKPOINT}"
|
||||
echo ""
|
||||
echo "Check the best hyperparameters output above for tuning results."
|
||||
echo ""
|
||||
|
||||
{
|
||||
echo "═══ Baseline Comparison ═══"
|
||||
echo "Baseline: Epoch 380"
|
||||
echo " Explained Variance: 0.4469"
|
||||
echo " Distance from optimal: 0.0531"
|
||||
echo ""
|
||||
echo "Tuning results saved to: ${OUTPUT_DIR}/best_hyperparameters.txt"
|
||||
} >> "${LOG_FILE}"
|
||||
}
|
||||
|
||||
# Function to generate summary report
|
||||
generate_summary_report() {
|
||||
print_section "Generating Summary Report"
|
||||
|
||||
local report_file="${OUTPUT_DIR}/TUNING_SUMMARY_REPORT.md"
|
||||
|
||||
cat > "${report_file}" <<EOF
|
||||
# PPO Comprehensive Hyperparameter Tuning - Summary Report
|
||||
|
||||
**Generated**: $(date)
|
||||
**Job ID**: $(cat "${OUTPUT_DIR}/job_id.txt" 2>/dev/null || echo "N/A")
|
||||
**Configuration**: ${TUNING_CONFIG}
|
||||
|
||||
---
|
||||
|
||||
## Tuning Configuration
|
||||
|
||||
- **Model Type**: ${MODEL_TYPE}
|
||||
- **Number of Trials**: ${NUM_TRIALS}
|
||||
- **Max Epochs per Trial**: ${MAX_EPOCHS_PER_TRIAL}
|
||||
- **Early Stopping**: Enabled (epoch 50)
|
||||
- **GPU**: ${USE_GPU}
|
||||
- **Validation Symbols**: ${SYMBOLS[*]}
|
||||
|
||||
## Search Space
|
||||
|
||||
| Hyperparameter | Search Space | Type |
|
||||
|----------------|--------------|------|
|
||||
| Learning Rate | [0.0001, 0.0003, 0.001] | Categorical |
|
||||
| Batch Size | [32, 64, 128, 256] | Categorical |
|
||||
| Gamma | [0.95, 0.99] | Categorical |
|
||||
| GAE Lambda | [0.9, 0.95, 0.98] | Categorical |
|
||||
| Clip Epsilon | [0.1, 0.2, 0.3] | Categorical |
|
||||
| Entropy Coefficient | [0.001, 0.01, 0.1] | Categorical |
|
||||
|
||||
**Total Combinations**: 648 (intelligent sampling via TPE)
|
||||
|
||||
## Objective Function
|
||||
|
||||
\`\`\`
|
||||
Combined Objective = 0.7 × Sharpe Ratio + 0.3 × Explained Variance
|
||||
\`\`\`
|
||||
|
||||
### Rationale
|
||||
- **Sharpe Ratio (70%)**: Primary metric for risk-adjusted returns
|
||||
- **Explained Variance (30%)**: Critical for PPO value network accuracy
|
||||
|
||||
## Baseline Performance
|
||||
|
||||
- **Source**: Agent 79 checkpoint analysis
|
||||
- **Checkpoint**: Epoch 380
|
||||
- **Explained Variance**: 0.4469
|
||||
- **Distance from Optimal (0.5)**: 0.0531
|
||||
- **Status**: EXCELLENT (within 5% of theoretical optimal)
|
||||
|
||||
## Best Hyperparameters
|
||||
|
||||
\`\`\`
|
||||
$(cat "${OUTPUT_DIR}/best_hyperparameters.txt" 2>/dev/null || echo "Not available - check tuning job status")
|
||||
\`\`\`
|
||||
|
||||
## Value Network Convergence Analysis
|
||||
|
||||
### Focus Areas
|
||||
1. **Explained Variance Trajectory**: Monitor convergence across trials
|
||||
2. **Policy Stability**: Ensure KL divergence remains healthy
|
||||
3. **Loss Dynamics**: Track policy loss and value loss evolution
|
||||
4. **Entropy Decay**: Verify exploration-exploitation balance
|
||||
|
||||
### Expected Improvements
|
||||
- Target: >5% improvement in combined objective over baseline
|
||||
- Explained variance: Approach 0.47+ (vs 0.4469 baseline)
|
||||
- Sharpe ratio: Measure risk-adjusted performance across all symbols
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Validation**: Run comprehensive backtest with best hyperparameters
|
||||
2. **Cross-symbol Analysis**: Compare performance across 6E, ZN, ES, NQ
|
||||
3. **Production Training**: Execute 500-epoch training with optimized hyperparameters
|
||||
4. **Checkpoint Analysis**: Identify optimal checkpoint (may not be final epoch)
|
||||
|
||||
## Files Generated
|
||||
|
||||
- **Tuning Log**: ${LOG_FILE}
|
||||
- **Best Hyperparameters**: ${OUTPUT_DIR}/best_hyperparameters.txt
|
||||
- **Checkpoints**: ${OUTPUT_DIR}/checkpoints/
|
||||
- **Plots**: ${OUTPUT_DIR}/plots/ (if generated)
|
||||
- **This Report**: ${report_file}
|
||||
|
||||
---
|
||||
|
||||
**Mission Status**: ✅ COMPLETE
|
||||
**Ready for**: Production training with optimized hyperparameters
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN}✓ Summary report generated: ${report_file}${NC}"
|
||||
}
|
||||
|
||||
# Function to display completion summary
|
||||
display_completion_summary() {
|
||||
print_section "Tuning Complete"
|
||||
|
||||
local elapsed=$(($(date +%s) - SCRIPT_START_TIME))
|
||||
|
||||
echo -e "${GREEN}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ PPO Hyperparameter Tuning Completed Successfully ║${NC}"
|
||||
echo -e "${GREEN}╚════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo "Duration: $(date -d@${elapsed} -u +%H:%M:%S)"
|
||||
echo "Trials: ${NUM_TRIALS}"
|
||||
echo "Output: ${OUTPUT_DIR}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Review best hyperparameters in: ${OUTPUT_DIR}/best_hyperparameters.txt"
|
||||
echo " 2. Compare with baseline (Epoch 380)"
|
||||
echo " 3. Run production training with optimized hyperparameters"
|
||||
echo " 4. Perform checkpoint analysis to find optimal epoch"
|
||||
echo ""
|
||||
echo -e "${BLUE}Full summary report: ${OUTPUT_DIR}/TUNING_SUMMARY_REPORT.md${NC}"
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
SCRIPT_START_TIME=$(date +%s)
|
||||
|
||||
# Execute tuning workflow
|
||||
check_prerequisites
|
||||
prepare_output_directory
|
||||
display_search_space
|
||||
display_objective
|
||||
|
||||
# Start tuning job
|
||||
local job_id
|
||||
job_id=$(start_tuning_job)
|
||||
|
||||
# Monitor progress
|
||||
monitor_tuning_progress "${job_id}"
|
||||
|
||||
# Retrieve results
|
||||
retrieve_best_hyperparameters "${job_id}"
|
||||
compare_with_baseline
|
||||
|
||||
# Generate reports
|
||||
generate_summary_report
|
||||
display_completion_summary
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
218
scripts/run_smoke_tests.sh
Executable file
218
scripts/run_smoke_tests.sh
Executable file
@@ -0,0 +1,218 @@
|
||||
#!/bin/bash
|
||||
# Foxhunt HFT System - Automated Smoke Test Suite
|
||||
# Version: 1.0.0
|
||||
# Purpose: Validate complete system functionality after deployment
|
||||
#
|
||||
# Usage:
|
||||
# ./run_smoke_tests.sh # Run all smoke tests
|
||||
# ./run_smoke_tests.sh --fast # Run only critical tests
|
||||
# ./run_smoke_tests.sh --verbose # Run with detailed logging
|
||||
# ./run_smoke_tests.sh --category infrastructure # Run specific category
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
VERBOSE=false
|
||||
FAST_MODE=false
|
||||
CATEGORY=""
|
||||
RUST_LOG="${RUST_LOG:-info}"
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--verbose|-v)
|
||||
VERBOSE=true
|
||||
RUST_LOG="debug"
|
||||
shift
|
||||
;;
|
||||
--fast|-f)
|
||||
FAST_MODE=true
|
||||
shift
|
||||
;;
|
||||
--category|-c)
|
||||
CATEGORY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--help|-h)
|
||||
echo "Foxhunt HFT System - Smoke Test Runner"
|
||||
echo ""
|
||||
echo "Usage: $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --verbose, -v Enable verbose logging (RUST_LOG=debug)"
|
||||
echo " --fast, -f Run only critical tests (faster execution)"
|
||||
echo " --category, -c CAT Run specific category only"
|
||||
echo " Categories: infrastructure, service, authentication, order_flow"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 # Run all smoke tests"
|
||||
echo " $0 --fast # Run only critical tests"
|
||||
echo " $0 --verbose # Run with debug logging"
|
||||
echo " $0 --category infrastructure # Run infrastructure tests only"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unknown option: $1${NC}"
|
||||
echo "Use --help for usage information"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Export environment variables
|
||||
export RUST_LOG
|
||||
export DATABASE_URL="${DATABASE_URL:-postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt}"
|
||||
export REDIS_URL="${REDIS_URL:-redis://localhost:6379}"
|
||||
export VAULT_ADDR="${VAULT_ADDR:-http://localhost:8200}"
|
||||
export INFLUXDB_URL="${INFLUXDB_URL:-http://localhost:8086}"
|
||||
export API_GATEWAY_URL="${API_GATEWAY_URL:-http://localhost:50051}"
|
||||
export TRADING_SERVICE_URL="${TRADING_SERVICE_URL:-http://localhost:50052}"
|
||||
export BACKTESTING_SERVICE_URL="${BACKTESTING_SERVICE_URL:-http://localhost:50053}"
|
||||
export ML_TRAINING_SERVICE_URL="${ML_TRAINING_SERVICE_URL:-http://localhost:50054}"
|
||||
export PROMETHEUS_URL="${PROMETHEUS_URL:-http://localhost:9090}"
|
||||
export GRAFANA_URL="${GRAFANA_URL:-http://localhost:3000}"
|
||||
export JWT_SECRET="${JWT_SECRET:-dev_secret_key_change_in_production}"
|
||||
|
||||
# Print banner
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE} Foxhunt HFT System - Smoke Test Suite${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
|
||||
# Environment info
|
||||
echo -e "${YELLOW}Environment Configuration:${NC}"
|
||||
echo " Database: ${DATABASE_URL}"
|
||||
echo " Redis: ${REDIS_URL}"
|
||||
echo " Vault: ${VAULT_ADDR}"
|
||||
echo " API Gateway: ${API_GATEWAY_URL}"
|
||||
echo " Trading Service: ${TRADING_SERVICE_URL}"
|
||||
echo " Log Level: ${RUST_LOG}"
|
||||
if [ "$FAST_MODE" = true ]; then
|
||||
echo -e " ${GREEN}Mode: FAST (critical tests only)${NC}"
|
||||
fi
|
||||
if [ "$CATEGORY" != "" ]; then
|
||||
echo -e " ${GREEN}Category: ${CATEGORY}${NC}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test execution flags
|
||||
TEST_FLAGS=""
|
||||
if [ "$VERBOSE" = true ]; then
|
||||
TEST_FLAGS="-- --nocapture --test-threads=1"
|
||||
fi
|
||||
|
||||
# Function to run a test category
|
||||
run_test_category() {
|
||||
local category=$1
|
||||
local test_name=$2
|
||||
local description=$3
|
||||
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE}Testing: ${description}${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
|
||||
if cargo test --test smoke_tests ${test_name} ${TEST_FLAGS}; then
|
||||
echo ""
|
||||
echo -e "${GREEN}✅ ${description} - PASSED${NC}"
|
||||
return 0
|
||||
else
|
||||
echo ""
|
||||
echo -e "${RED}❌ ${description} - FAILED${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Track results
|
||||
TOTAL_CATEGORIES=0
|
||||
PASSED_CATEGORIES=0
|
||||
FAILED_CATEGORIES=0
|
||||
|
||||
# Main test execution
|
||||
echo -e "${YELLOW}🔍 Starting smoke test execution...${NC}"
|
||||
echo ""
|
||||
|
||||
# Category 1: Infrastructure Health (CRITICAL)
|
||||
if [ -z "$CATEGORY" ] || [ "$CATEGORY" = "infrastructure" ]; then
|
||||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1))
|
||||
if run_test_category "infrastructure" "infrastructure_health" "Infrastructure Health"; then
|
||||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1))
|
||||
else
|
||||
FAILED_CATEGORIES=$((FAILED_CATEGORIES + 1))
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Category 2: Service Health (CRITICAL)
|
||||
if [ -z "$CATEGORY" ] || [ "$CATEGORY" = "service" ]; then
|
||||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1))
|
||||
if run_test_category "service" "service_health" "Service Health"; then
|
||||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1))
|
||||
else
|
||||
FAILED_CATEGORIES=$((FAILED_CATEGORIES + 1))
|
||||
echo -e "${YELLOW}⚠️ Note: Some services may not be available due to configuration issues${NC}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Category 3: Authentication Flow (skip in fast mode)
|
||||
if [ "$FAST_MODE" = false ] && ([ -z "$CATEGORY" ] || [ "$CATEGORY" = "authentication" ]); then
|
||||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1))
|
||||
if run_test_category "authentication" "authentication_flow" "Authentication Flow"; then
|
||||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1))
|
||||
else
|
||||
FAILED_CATEGORIES=$((FAILED_CATEGORIES + 1))
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Category 4: Basic Order Flow (skip in fast mode)
|
||||
if [ "$FAST_MODE" = false ] && ([ -z "$CATEGORY" ] || [ "$CATEGORY" = "order_flow" ]); then
|
||||
TOTAL_CATEGORIES=$((TOTAL_CATEGORIES + 1))
|
||||
if run_test_category "order_flow" "basic_order_flow" "Basic Order Flow"; then
|
||||
PASSED_CATEGORIES=$((PASSED_CATEGORIES + 1))
|
||||
else
|
||||
FAILED_CATEGORIES=$((FAILED_CATEGORIES + 1))
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Print summary
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${BLUE} Smoke Test Summary${NC}"
|
||||
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo ""
|
||||
echo " Total Categories: ${TOTAL_CATEGORIES}"
|
||||
echo -e " ${GREEN}Passed: ${PASSED_CATEGORIES}${NC}"
|
||||
if [ $FAILED_CATEGORIES -gt 0 ]; then
|
||||
echo -e " ${RED}Failed: ${FAILED_CATEGORIES}${NC}"
|
||||
else
|
||||
echo " Failed: ${FAILED_CATEGORIES}"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Calculate percentage
|
||||
if [ $TOTAL_CATEGORIES -gt 0 ]; then
|
||||
PASS_PERCENTAGE=$((PASSED_CATEGORIES * 100 / TOTAL_CATEGORIES))
|
||||
echo " Pass Rate: ${PASS_PERCENTAGE}%"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Exit with appropriate code
|
||||
if [ $FAILED_CATEGORIES -eq 0 ]; then
|
||||
echo -e "${GREEN}✅ All smoke tests passed!${NC}"
|
||||
echo -e "${GREEN} System is ready for deployment.${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Some smoke tests failed${NC}"
|
||||
echo -e "${YELLOW} Review the failures above before deploying.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
37
scripts/run_tests.sh
Executable file
37
scripts/run_tests.sh
Executable file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
# Test runner script for foxhunt_runpod module
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
echo "========================================="
|
||||
echo "Foxhunt RunPod Module Test Suite"
|
||||
echo "========================================="
|
||||
|
||||
# Activate virtual environment
|
||||
source .venv/bin/activate
|
||||
|
||||
echo ""
|
||||
echo "Running tests with coverage..."
|
||||
echo ""
|
||||
|
||||
# Run tests with coverage
|
||||
pytest tests/foxhunt_runpod/ \
|
||||
-v \
|
||||
--cov=foxhunt_runpod \
|
||||
--cov-report=term-missing \
|
||||
--cov-report=html:htmlcov \
|
||||
--cov-report=xml:coverage.xml \
|
||||
"$@"
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
echo "Test Summary"
|
||||
echo "========================================="
|
||||
echo "Coverage report: htmlcov/index.html"
|
||||
echo "XML report: coverage.xml"
|
||||
echo ""
|
||||
echo "Run specific tests:"
|
||||
echo " ./run_tests.sh tests/foxhunt_runpod/test_client.py"
|
||||
echo " ./run_tests.sh -k test_deploy_pod"
|
||||
echo " ./run_tests.sh -m unit"
|
||||
echo ""
|
||||
69
scripts/run_tft_training.sh
Executable file
69
scripts/run_tft_training.sh
Executable file
@@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
# TFT Training Restart Script (Agent 117)
|
||||
# After Agent 112 TLOB Decoder fix
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== TFT Training Restart ==="
|
||||
echo "Timestamp: $(date)"
|
||||
echo "GPU Status:"
|
||||
nvidia-smi --query-gpu=name,memory.total,memory.used,temperature.gpu,utilization.gpu --format=csv,noheader
|
||||
echo ""
|
||||
|
||||
echo "=== Pre-flight Checks ==="
|
||||
echo "Checking for running training processes..."
|
||||
if ps aux | grep -E "(train_tft|train_dqn|train_ppo)" | grep -v grep; then
|
||||
echo "WARNING: Found running training processes!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking compilation status..."
|
||||
cargo check -p ml 2>&1 | grep -E "(error|Finished)" | tail -5
|
||||
|
||||
echo "Checking output directory..."
|
||||
mkdir -p ml/trained_models/production/tft_real_data
|
||||
ls -la ml/trained_models/production/tft_real_data/
|
||||
|
||||
echo "Checking training data..."
|
||||
echo "Available DBN files: $(find test_data/real/databento/ml_training -name '*.dbn' | wc -l)"
|
||||
echo "Total size: $(du -sh test_data/real/databento/ml_training/)"
|
||||
|
||||
echo ""
|
||||
echo "=== Starting TFT Training ==="
|
||||
echo "Configuration:"
|
||||
echo " - Epochs: 200"
|
||||
echo " - Learning Rate: 0.001"
|
||||
echo " - Batch Size: 32"
|
||||
echo " - Lookback Window: 60"
|
||||
echo " - Forecast Horizon: 10"
|
||||
echo " - GPU: Enabled (CUDA_VISIBLE_DEVICES=0)"
|
||||
echo " - Output: ml/trained_models/production/tft_real_data"
|
||||
echo ""
|
||||
|
||||
# Log file
|
||||
LOG_FILE="tft_training_$(date +%Y%m%d_%H%M%S).log"
|
||||
echo "Logging to: $LOG_FILE"
|
||||
echo ""
|
||||
|
||||
# Start training
|
||||
RUST_LOG=info \
|
||||
RUST_BACKTRACE=1 \
|
||||
CUDA_VISIBLE_DEVICES=0 \
|
||||
cargo run --release -p ml --example train_tft_dbn -- \
|
||||
--data-path test_data/real/databento/ml_training \
|
||||
--epochs 200 \
|
||||
--learning-rate 0.001 \
|
||||
--batch-size 32 \
|
||||
--lookback-window 60 \
|
||||
--forecast-horizon 10 \
|
||||
--use-gpu \
|
||||
--output-dir ml/trained_models/production/tft_real_data \
|
||||
--early-stopping-patience 20 \
|
||||
--early-stopping-threshold 0.0001 \
|
||||
--verbose 2>&1 | tee "$LOG_FILE"
|
||||
|
||||
echo ""
|
||||
echo "=== Training Complete ==="
|
||||
echo "Final GPU status:"
|
||||
nvidia-smi --query-gpu=memory.used,memory.total,temperature.gpu --format=csv,noheader
|
||||
echo "Log saved to: $LOG_FILE"
|
||||
156
scripts/run_training.sh
Executable file
156
scripts/run_training.sh
Executable file
@@ -0,0 +1,156 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# A script to run ML training jobs either in parallel or sequentially.
|
||||
#
|
||||
# Usage:
|
||||
# ./run_training.sh --parallel (High risk of GPU OOM error)
|
||||
# ./run_training.sh --sequential (Recommended for stability)
|
||||
#
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
# --- Configuration ---
|
||||
# Define the commands to be executed. The key is used for logging.
|
||||
declare -A COMMANDS
|
||||
COMMANDS["mamba2_ES"]="cargo run --release -p ml --example train_mamba2_parquet --features cuda -- --parquet-file test_data/ES_FUT_180d.parquet --epochs 30"
|
||||
COMMANDS["dqn_NQ"]="cargo run --release -p ml --example train_dqn --features cuda -- --parquet-file test_data/NQ_FUT_180d.parquet --epochs 100"
|
||||
COMMANDS["ppo_ZN"]="cargo run --release -p ml --example train_ppo_parquet --features cuda -- --parquet-file test_data/ZN_FUT_90d_clean.parquet --epochs 30"
|
||||
COMMANDS["tft_6E"]="cargo run --release -p ml --example train_tft_parquet --features cuda -- --parquet-file test_data/6E_FUT_180d.parquet --epochs 50"
|
||||
|
||||
# --- Script Logic ---
|
||||
usage() {
|
||||
echo "Usage: $0 [--parallel | --sequential]"
|
||||
echo " --parallel: Run all training jobs simultaneously (HIGHLY LIKELY TO FAIL on low VRAM GPUs)."
|
||||
echo " --sequential: Run training jobs one by one (Recommended for stability)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Parallel Execution Function ---
|
||||
run_parallel() {
|
||||
declare -A pids
|
||||
declare -A statuses
|
||||
|
||||
# Cleanup function to kill child processes on script exit
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo "Caught signal, cleaning up background jobs..."
|
||||
for pid in "${!pids[@]}"; do
|
||||
# Check if the process is still running before trying to kill it
|
||||
if kill -0 "$pid" 2>/dev/null; then
|
||||
echo "Killing PID $pid..."
|
||||
kill "$pid"
|
||||
fi
|
||||
done
|
||||
exit 1
|
||||
}
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
echo "Starting 4 training jobs in parallel..."
|
||||
echo "WARNING: This may cause GPU Out-Of-Memory errors."
|
||||
echo "---"
|
||||
|
||||
for key in "${!COMMANDS[@]}"; do
|
||||
local log_file="/tmp/train_${key}.log"
|
||||
echo "Starting ${key}... Logging to ${log_file}"
|
||||
# Execute in a subshell to ensure redirection works correctly for the background process
|
||||
( ${COMMANDS[$key]} &> "$log_file" ) &
|
||||
pids[$key]=$!
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "All jobs launched. PIDs: ${pids[*]}"
|
||||
echo "---"
|
||||
|
||||
# Wait for all jobs to complete and store their exit codes
|
||||
for key in "${!pids[@]}"; do
|
||||
local pid=${pids[$key]}
|
||||
wait "$pid"
|
||||
statuses[$key]=$?
|
||||
done
|
||||
|
||||
# Final Report
|
||||
echo "All training jobs have completed. Final Status:"
|
||||
echo "------------------------------------------------"
|
||||
local all_success=true
|
||||
for key in "${!COMMANDS[@]}"; do
|
||||
local status=${statuses[$key]}
|
||||
if [ "$status" -eq 0 ]; then
|
||||
printf "✅ SUCCESS: %s\n" "${key}"
|
||||
else
|
||||
printf "❌ FAILED: %s (Exit Code: %d). Check log: /tmp/train_%s.log\n" "${key}" "${status}" "${key}"
|
||||
all_success=false
|
||||
fi
|
||||
done
|
||||
echo "------------------------------------------------"
|
||||
|
||||
if [ "$all_success" = false ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- Sequential Execution Function ---
|
||||
run_sequential() {
|
||||
echo "Starting 4 training jobs sequentially to avoid GPU memory conflicts..."
|
||||
echo "---"
|
||||
local all_success=true
|
||||
for key in "${!COMMANDS[@]}"; do
|
||||
local log_file="/tmp/train_${key}.log"
|
||||
echo "--- Starting ${key} ---"
|
||||
echo "Logging to ${log_file}"
|
||||
|
||||
${COMMANDS[$key]} &> "$log_file"
|
||||
local status=$?
|
||||
|
||||
if [ "$status" -eq 0 ]; then
|
||||
printf "✅ SUCCESS: %s completed.\n" "${key}"
|
||||
else
|
||||
printf "❌ FAILED: %s (Exit Code: %d). Check log: %s\n" "${key}" "${status}" "${log_file}"
|
||||
all_success=false
|
||||
fi
|
||||
echo "--- Finished ${key} ---"
|
||||
echo ""
|
||||
done
|
||||
|
||||
if [ "$all_success" = false ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- Main Entry Point ---
|
||||
main() {
|
||||
if [ "$#" -ne 1 ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
local mode=$1
|
||||
# The script should be run from the project root.
|
||||
# cd /home/jgrusewski/Work/foxhunt || { echo "Failed to cd into working directory"; exit 1; }
|
||||
echo "Working directory: $(pwd)"
|
||||
echo ""
|
||||
|
||||
case "$mode" in
|
||||
--parallel)
|
||||
run_parallel
|
||||
;;
|
||||
--sequential)
|
||||
run_sequential
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
local exit_code=$?
|
||||
echo ""
|
||||
if [ $exit_code -eq 0 ]; then
|
||||
echo "Script finished. All runs were successful."
|
||||
else
|
||||
echo "Script finished. One or more runs failed."
|
||||
fi
|
||||
|
||||
exit $exit_code
|
||||
}
|
||||
|
||||
main "$@"
|
||||
50
scripts/runpod_validation_deploy.sh
Normal file
50
scripts/runpod_validation_deploy.sh
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# VarMap Fix Validation - Runpod Deployment
|
||||
# Deploys MAMBA-2 with fixed binary for checkpoint integrity validation
|
||||
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "MAMBA-2 VarMap Fix Validation"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Configuration:"
|
||||
echo " GPU: RTX A4000 (16GB, \$0.25/hr)"
|
||||
echo " Binary: hyperopt_mamba2_demo (FIXED - uploaded 2025-10-29 08:43 UTC)"
|
||||
echo " Dataset: ES_FUT_180d.parquet (225 features)"
|
||||
echo " Trials: 2 (quick validation)"
|
||||
echo " Epochs: 1 (fast cycle)"
|
||||
echo " Batch size: 256 (optimal)"
|
||||
echo " Expected runtime: ~10 minutes"
|
||||
echo " Expected cost: ~\$0.04"
|
||||
echo ""
|
||||
|
||||
# Correct command for the pod
|
||||
COMMAND="/runpod-volume/binaries/hyperopt_mamba2_demo \
|
||||
--parquet-file /runpod-volume/test_data/ES_FUT_180d.parquet \
|
||||
--base-dir /runpod-volume/ml_training \
|
||||
--run-type hyperopt \
|
||||
--trials 2 \
|
||||
--epochs 1 \
|
||||
--batch-size-max 256 \
|
||||
--seed 42"
|
||||
|
||||
echo "Command to run in pod:"
|
||||
echo "$COMMAND"
|
||||
echo ""
|
||||
|
||||
# Deploy using runpod_deploy.py
|
||||
echo "Deploying pod..."
|
||||
python3 scripts/runpod_deploy.py \
|
||||
--gpu-type "RTX A4000" \
|
||||
--binary hyperopt_mamba2_demo \
|
||||
--dataset ES_FUT_180d.parquet \
|
||||
--extra-args "--trials 2 --epochs 1 --batch-size-max 256 --base-dir /runpod-volume/ml_training"
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Pod deployed! Monitor with:"
|
||||
echo " aws s3 ls s3://se3zdnb5o4/training_runs/mamba2/ --endpoint-url https://s3api-eur-is-1.runpod.io --recursive --human-readable"
|
||||
echo ""
|
||||
echo "Expected checkpoint size: 2-8MB (NOT 842KB)"
|
||||
echo "=========================================="
|
||||
121
scripts/setup-database.sh
Executable file
121
scripts/setup-database.sh
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
# Comprehensive Database Setup Script for Foxhunt HFT System
|
||||
# This script sets up PostgreSQL, runs migrations, and prepares SQLx for offline compilation
|
||||
|
||||
set -e
|
||||
|
||||
echo "🚀 Setting up Foxhunt HFT Database..."
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Check if Docker is available
|
||||
if command -v docker >/dev/null 2>&1 && command -v docker-compose >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ Docker and docker-compose found${NC}"
|
||||
USE_DOCKER=true
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Docker not found, will attempt local setup${NC}"
|
||||
USE_DOCKER=false
|
||||
fi
|
||||
|
||||
if [ "$USE_DOCKER" = true ]; then
|
||||
echo -e "${GREEN}📦 Starting PostgreSQL with Docker...${NC}"
|
||||
|
||||
# Start PostgreSQL with docker-compose
|
||||
docker-compose -f docker-compose.dev.yml up -d postgres
|
||||
|
||||
# Wait for PostgreSQL to be ready
|
||||
echo "⏳ Waiting for PostgreSQL to be ready..."
|
||||
for i in {1..30}; do
|
||||
if docker-compose -f docker-compose.dev.yml exec -T postgres pg_isready -U foxhunt -d foxhunt >/dev/null 2>&1; then
|
||||
echo -e "${GREEN}✅ PostgreSQL is ready!${NC}"
|
||||
break
|
||||
fi
|
||||
echo "Waiting... ($i/30)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Set DATABASE_URL for Docker setup
|
||||
export DATABASE_URL="postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt"
|
||||
|
||||
else
|
||||
echo -e "${YELLOW}🔧 Setting up local PostgreSQL...${NC}"
|
||||
|
||||
# Check if PostgreSQL is installed
|
||||
if ! command -v psql >/dev/null 2>&1; then
|
||||
echo -e "${RED}❌ PostgreSQL client not found. Please install PostgreSQL.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try to connect to local PostgreSQL
|
||||
export DATABASE_URL="postgresql://localhost/foxhunt"
|
||||
|
||||
# Create database if it doesn't exist
|
||||
echo "Creating database..."
|
||||
createdb foxhunt 2>/dev/null || echo "Database may already exist"
|
||||
|
||||
# Run init-db.sql
|
||||
echo "Running initial database setup..."
|
||||
psql -d foxhunt -f init-db.sql
|
||||
|
||||
# Run migrations in order
|
||||
echo "Running migrations..."
|
||||
for migration in migrations/0*.sql; do
|
||||
if [[ -f "$migration" ]]; then
|
||||
echo "Running: $migration"
|
||||
psql -d foxhunt -f "$migration"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Update .env file with correct DATABASE_URL
|
||||
echo -e "${GREEN}📝 Updating .env file...${NC}"
|
||||
if [ -f .env ]; then
|
||||
# Create backup of .env
|
||||
cp .env .env.backup
|
||||
|
||||
# Update DATABASE_URL in .env
|
||||
if [ "$USE_DOCKER" = true ]; then
|
||||
sed -i 's|DATABASE_URL=.*|DATABASE_URL=postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt|' .env
|
||||
else
|
||||
sed -i 's|DATABASE_URL=.*|DATABASE_URL=postgresql://localhost/foxhunt|' .env
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Updated DATABASE_URL in .env${NC}"
|
||||
fi
|
||||
|
||||
# Generate SQLx metadata
|
||||
echo -e "${GREEN}🔧 Generating SQLx metadata...${NC}"
|
||||
if cargo sqlx prepare; then
|
||||
echo -e "${GREEN}✅ SQLx metadata generated successfully!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ SQLx prepare failed, but continuing...${NC}"
|
||||
fi
|
||||
|
||||
# Test compilation
|
||||
echo -e "${GREEN}🏗️ Testing compilation...${NC}"
|
||||
if SQLX_OFFLINE=true cargo check --workspace --quiet; then
|
||||
echo -e "${GREEN}✅ Compilation successful!${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Some compilation issues remain (this may be expected)${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}🎉 Database setup complete!${NC}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Your database is now set up and migrations have been applied"
|
||||
echo "2. SQLx should now work for compile-time verification"
|
||||
echo "3. You can start the services with: cargo run --bin trading_service"
|
||||
echo ""
|
||||
echo "Database connection details:"
|
||||
echo " URL: $DATABASE_URL"
|
||||
if [ "$USE_DOCKER" = true ]; then
|
||||
echo " To stop: docker-compose -f docker-compose.dev.yml down"
|
||||
echo " To connect: docker-compose -f docker-compose.dev.yml exec postgres psql -U foxhunt -d foxhunt"
|
||||
else
|
||||
echo " To connect: psql -d foxhunt"
|
||||
fi
|
||||
46
scripts/setup.py
Normal file
46
scripts/setup.py
Normal file
@@ -0,0 +1,46 @@
|
||||
"""Setup script for runpod module."""
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from pathlib import Path
|
||||
|
||||
# Read the README file
|
||||
readme_file = Path(__file__).parent / "runpod" / "README.md"
|
||||
long_description = readme_file.read_text() if readme_file.exists() else ""
|
||||
|
||||
setup(
|
||||
name="foxhunt-runpod",
|
||||
version="1.0.0",
|
||||
author="Foxhunt Team",
|
||||
description="RunPod deployment and monitoring for Foxhunt ML training",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
packages=["runpod"], # Explicit package name - clean architecture
|
||||
python_requires=">=3.8",
|
||||
install_requires=[
|
||||
"requests>=2.31.0",
|
||||
"boto3>=1.28.0",
|
||||
"python-dotenv>=1.0.0",
|
||||
"rich>=13.0.0",
|
||||
"pydantic>=2.0.0",
|
||||
"pydantic-settings>=2.0.0",
|
||||
],
|
||||
extras_require={
|
||||
"test": [
|
||||
"pytest>=7.4.0",
|
||||
"pytest-cov>=4.1.0",
|
||||
"pytest-mock>=3.11.1",
|
||||
"responses>=0.23.1",
|
||||
"moto[s3]>=4.2.0",
|
||||
]
|
||||
},
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
],
|
||||
)
|
||||
133
scripts/setup_lld.sh
Executable file
133
scripts/setup_lld.sh
Executable file
@@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "=========================================="
|
||||
echo "LLD Linker Setup Script for Foxhunt"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Check if running with sudo
|
||||
if [ "$EUID" -eq 0 ]; then
|
||||
echo "✓ Running with sudo privileges"
|
||||
else
|
||||
echo "⚠️ This script requires sudo privileges"
|
||||
echo " Please run: sudo bash $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install lld
|
||||
echo "Step 1: Installing lld..."
|
||||
apt-get update -qq
|
||||
apt-get install -y lld
|
||||
|
||||
echo ""
|
||||
echo "Step 2: Verifying installation..."
|
||||
if command -v ld.lld &> /dev/null; then
|
||||
echo "✓ LLD installed successfully"
|
||||
ld.lld --version
|
||||
else
|
||||
echo "✗ LLD installation failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update .cargo/config.toml
|
||||
echo ""
|
||||
echo "Step 3: Updating .cargo/config.toml..."
|
||||
FOXHUNT_DIR="/home/jgrusewski/Work/foxhunt"
|
||||
CONFIG_FILE="$FOXHUNT_DIR/.cargo/config.toml"
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo "✗ Config file not found: $CONFIG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Backup original
|
||||
cp "$CONFIG_FILE" "$CONFIG_FILE.backup.$(date +%Y%m%d_%H%M%S)"
|
||||
echo "✓ Backup created: $CONFIG_FILE.backup.*"
|
||||
|
||||
# Apply changes
|
||||
cat > "$CONFIG_FILE" << 'CONFIGEOF'
|
||||
[env]
|
||||
# SQLx offline mode - use cached query metadata from .sqlx/ directory
|
||||
# Generated with: cargo sqlx prepare --workspace
|
||||
SQLX_OFFLINE = "true"
|
||||
|
||||
[build]
|
||||
rustflags = [
|
||||
"-D", "unsafe_op_in_unsafe_fn",
|
||||
"-D", "clippy::undocumented_unsafe_blocks",
|
||||
"-W", "rust_2024_idioms",
|
||||
"-C", "force-frame-pointers=yes",
|
||||
# REMOVED: "-C", "stack-protector=strong", # Not compatible with coverage tools
|
||||
"-C", "relocation-model=pic",
|
||||
]
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
linker = "clang"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Wl,-z,relro,-z,now",
|
||||
"-C", "link-arg=-Wl,--as-needed",
|
||||
"-C", "link-arg=-fuse-ld=lld", # LLD linker for faster linking (5-10x improvement)
|
||||
# CRITICAL HFT PERFORMANCE FLAGS - FIXES SIMD 10,000x REGRESSION
|
||||
"-C", "target-cpu=native",
|
||||
"-C", "target-feature=+avx2,+fma,+bmi2",
|
||||
"-C", "opt-level=3",
|
||||
"-C", "codegen-units=1",
|
||||
]
|
||||
|
||||
# Profile-specific optimizations for maximum SIMD performance
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = false
|
||||
debug = false
|
||||
overflow-checks = false
|
||||
|
||||
# Benchmarking profile with SIMD optimizations
|
||||
[profile.bench]
|
||||
inherits = "release"
|
||||
debug = false
|
||||
|
||||
# HFT-specific profile for production with aggressive SIMD optimization
|
||||
[profile.hft]
|
||||
inherits = "release"
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
strip = false
|
||||
overflow-checks = false
|
||||
CONFIGEOF
|
||||
|
||||
echo "✓ Configuration updated"
|
||||
|
||||
# Test compilation
|
||||
echo ""
|
||||
echo "Step 4: Testing compilation..."
|
||||
cd "$FOXHUNT_DIR"
|
||||
sudo -u jgrusewski bash << 'TESTEOF'
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
echo "Running: cargo check --workspace"
|
||||
cargo check --workspace 2>&1 | head -20
|
||||
TESTEOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✓ Compilation test passed"
|
||||
else
|
||||
echo "⚠️ Compilation test encountered issues (this may be normal)"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "✓ LLD Setup Complete!"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Run a clean build: cargo clean && time cargo build --release"
|
||||
echo "2. Compare build times with previous builds"
|
||||
echo "3. Expected improvement: 40-60% faster linking"
|
||||
echo ""
|
||||
echo "Backup location: $CONFIG_FILE.backup.*"
|
||||
|
||||
128
scripts/simple_concurrent_test.sh
Executable file
128
scripts/simple_concurrent_test.sh
Executable file
@@ -0,0 +1,128 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple Concurrent Connection Test using curl for HTTP health endpoints
|
||||
# Tests Trading Service, API Gateway, Backtesting, and ML Training services
|
||||
|
||||
set -e
|
||||
|
||||
echo "Foxhunt Concurrent Connection Test"
|
||||
echo "===================================="
|
||||
echo ""
|
||||
|
||||
# Test configuration
|
||||
TRADING_HEALTH="http://localhost:8081/health"
|
||||
API_GW_HEALTH="http://localhost:8080/health"
|
||||
BACKTESTING_HEALTH="http://localhost:8082/health"
|
||||
ML_TRAINING_HEALTH="http://localhost:8095/health"
|
||||
|
||||
# Function to test concurrent connections
|
||||
test_concurrent() {
|
||||
local url=$1
|
||||
local num_connections=$2
|
||||
local service_name=$3
|
||||
|
||||
echo ""
|
||||
echo "Testing $service_name with $num_connections concurrent connections..."
|
||||
|
||||
local start_time=$(date +%s%N)
|
||||
local success=0
|
||||
local failed=0
|
||||
local total_latency=0
|
||||
|
||||
# Create temporary file for results
|
||||
local results_file=$(mktemp)
|
||||
|
||||
# Launch concurrent requests
|
||||
for i in $(seq 1 $num_connections); do
|
||||
(
|
||||
local req_start=$(date +%s%N)
|
||||
if curl -s -f -m 5 "$url" > /dev/null 2>&1; then
|
||||
local req_end=$(date +%s%N)
|
||||
local latency=$(( (req_end - req_start) / 1000000 ))
|
||||
echo "SUCCESS:$latency" >> "$results_file"
|
||||
else
|
||||
echo "FAILED:0" >> "$results_file"
|
||||
fi
|
||||
) &
|
||||
done
|
||||
|
||||
# Wait for all requests to complete
|
||||
wait
|
||||
|
||||
local end_time=$(date +%s%N)
|
||||
local total_duration=$(( (end_time - start_time) / 1000000 ))
|
||||
|
||||
# Analyze results
|
||||
while IFS=: read -r status latency; do
|
||||
if [ "$status" = "SUCCESS" ]; then
|
||||
((success++))
|
||||
total_latency=$((total_latency + latency))
|
||||
else
|
||||
((failed++))
|
||||
fi
|
||||
done < "$results_file"
|
||||
|
||||
local total=$((success + failed))
|
||||
local success_rate=0
|
||||
local avg_latency=0
|
||||
local throughput=0
|
||||
|
||||
if [ $total -gt 0 ]; then
|
||||
success_rate=$(awk "BEGIN {printf \"%.1f\", ($success / $total) * 100}")
|
||||
fi
|
||||
|
||||
if [ $success -gt 0 ]; then
|
||||
avg_latency=$((total_latency / success))
|
||||
fi
|
||||
|
||||
if [ $total_duration -gt 0 ]; then
|
||||
throughput=$(awk "BEGIN {printf \"%.2f\", ($success * 1000.0) / $total_duration}")
|
||||
fi
|
||||
|
||||
echo " Success: $success/$total ($success_rate%)"
|
||||
echo " Failed: $failed"
|
||||
echo " Avg Latency: ${avg_latency}ms"
|
||||
echo " Duration: ${total_duration}ms"
|
||||
echo " Throughput: ${throughput} req/s"
|
||||
|
||||
rm -f "$results_file"
|
||||
|
||||
# Return success rate (for final assessment)
|
||||
echo "$success_rate"
|
||||
}
|
||||
|
||||
echo "Starting concurrent connection tests..."
|
||||
echo ""
|
||||
|
||||
# Test each service with increasing connection counts
|
||||
for connections in 10 50 100 200; do
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Testing with $connections connections"
|
||||
echo "========================================"
|
||||
|
||||
test_concurrent "$TRADING_HEALTH" $connections "Trading Service"
|
||||
test_concurrent "$API_GW_HEALTH" $connections "API Gateway"
|
||||
test_concurrent "$BACKTESTING_HEALTH" $connections "Backtesting Service"
|
||||
test_concurrent "$ML_TRAINING_HEALTH" $connections "ML Training Service"
|
||||
|
||||
if [ $connections -lt 200 ]; then
|
||||
echo ""
|
||||
echo "Waiting 5 seconds before next test level..."
|
||||
sleep 5
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Connection Leak Check"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Active connections on service ports:"
|
||||
netstat -an | grep -E ":(50051|50052|50053|50054|8080|8081|8082|8095)" | grep ESTABLISHED | wc -l
|
||||
echo ""
|
||||
echo "Connection summary:"
|
||||
ss -s | grep TCP
|
||||
|
||||
echo ""
|
||||
echo "Test completed successfully!"
|
||||
271
scripts/staging_e2e_tests.sh
Executable file
271
scripts/staging_e2e_tests.sh
Executable file
@@ -0,0 +1,271 @@
|
||||
#!/bin/bash
|
||||
# ================================================================================================
|
||||
# Staging E2E Smoke Tests - Wave D Validation
|
||||
# ================================================================================================
|
||||
# Purpose: Validate staging environment readiness for Wave D rollback testing
|
||||
# Usage: ./staging_e2e_tests.sh
|
||||
# Exit Codes: 0 = All tests passed, 1 = One or more tests failed
|
||||
# ================================================================================================
|
||||
|
||||
set -e
|
||||
|
||||
STAGING_GATEWAY="localhost:50061"
|
||||
STAGING_DB="postgresql://foxhunt:foxhunt_staging_password@localhost:5433/foxhunt_staging"
|
||||
TEST_COUNT=0
|
||||
PASS_COUNT=0
|
||||
FAIL_COUNT=0
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "==================================="
|
||||
echo "Staging E2E Smoke Tests - Wave D"
|
||||
echo "==================================="
|
||||
echo ""
|
||||
|
||||
# Helper functions
|
||||
test_start() {
|
||||
TEST_COUNT=$((TEST_COUNT + 1))
|
||||
echo -n "[TEST $TEST_COUNT] $1... "
|
||||
}
|
||||
|
||||
test_pass() {
|
||||
PASS_COUNT=$((PASS_COUNT + 1))
|
||||
echo -e "${GREEN}✓ PASS${NC}"
|
||||
}
|
||||
|
||||
test_fail() {
|
||||
FAIL_COUNT=$((FAIL_COUNT + 1))
|
||||
echo -e "${RED}✗ FAIL${NC}"
|
||||
echo -e " ${RED}Error: $1${NC}"
|
||||
}
|
||||
|
||||
# Test 1: Database connectivity
|
||||
test_start "Database connectivity"
|
||||
if docker exec foxhunt-postgres-staging psql -U foxhunt -d foxhunt_staging -c "SELECT 1;" > /dev/null 2>&1; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Cannot connect to staging database"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 2: Redis connectivity
|
||||
test_start "Redis connectivity"
|
||||
if docker exec foxhunt-redis-staging redis-cli ping 2>/dev/null | grep -q PONG; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Cannot connect to staging Redis"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 3: Vault connectivity
|
||||
test_start "Vault connectivity"
|
||||
if docker exec foxhunt-vault-staging vault status > /dev/null 2>&1; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Cannot connect to staging Vault"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 4: MinIO connectivity
|
||||
test_start "MinIO connectivity"
|
||||
if curl -s -o /dev/null -w "%{http_code}" http://localhost:9002/minio/health/live | grep -q "200"; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Cannot connect to staging MinIO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 5: Wave D migration verification
|
||||
test_start "Wave D migration verification"
|
||||
REGIME_TABLES=$(docker exec foxhunt-postgres-staging psql -U foxhunt -d foxhunt_staging -t -c "
|
||||
SELECT COUNT(*) FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND (tablename LIKE '%regime%' OR tablename LIKE '%adaptive%');
|
||||
" 2>/dev/null | tr -d ' ')
|
||||
|
||||
if [ "$REGIME_TABLES" -eq 3 ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Expected 3 Wave D tables, found $REGIME_TABLES"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 6: Wave D table structure verification
|
||||
test_start "Wave D table structure verification"
|
||||
REGIME_STATES_COLS=$(docker exec foxhunt-postgres-staging psql -U foxhunt -d foxhunt_staging -t -c "
|
||||
SELECT COUNT(*) FROM information_schema.columns
|
||||
WHERE table_schema = 'public' AND table_name = 'regime_states';
|
||||
" 2>/dev/null | tr -d ' ')
|
||||
|
||||
if [ "$REGIME_STATES_COLS" -ge 10 ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "regime_states table has only $REGIME_STATES_COLS columns (expected >= 10)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 7: Wave D functions verification
|
||||
test_start "Wave D functions verification"
|
||||
REGIME_FUNCTIONS=$(docker exec foxhunt-postgres-staging psql -U foxhunt -d foxhunt_staging -t -c "
|
||||
SELECT COUNT(*) FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
AND routine_name LIKE '%regime%';
|
||||
" 2>/dev/null | tr -d ' ')
|
||||
|
||||
if [ "$REGIME_FUNCTIONS" -ge 3 ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Expected at least 3 regime functions, found $REGIME_FUNCTIONS"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 8: Service health checks (optional - will be deployed by Agent E2)
|
||||
test_start "Service health checks"
|
||||
HEALTHY_SERVICES=0
|
||||
TOTAL_SERVICES=0
|
||||
|
||||
for SERVICE in trading backtesting ml_training trading_agent api_gateway; do
|
||||
TOTAL_SERVICES=$((TOTAL_SERVICES + 1))
|
||||
CONTAINER="foxhunt-${SERVICE//_/-}-service-staging"
|
||||
if [ "$SERVICE" = "api_gateway" ]; then
|
||||
CONTAINER="foxhunt-api-gateway-staging"
|
||||
fi
|
||||
|
||||
if docker ps --filter "name=$CONTAINER" | grep -q "$CONTAINER"; then
|
||||
HEALTHY_SERVICES=$((HEALTHY_SERVICES + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$HEALTHY_SERVICES" -eq "$TOTAL_SERVICES" ]; then
|
||||
test_pass
|
||||
else
|
||||
# This is okay for Agent E1 - services will be deployed by Agent E2
|
||||
echo -e "${YELLOW}SKIP${NC}"
|
||||
echo -e " ${YELLOW}Note: $HEALTHY_SERVICES/$TOTAL_SERVICES services running (Agent E2 will deploy remaining)${NC}"
|
||||
PASS_COUNT=$((PASS_COUNT + 1)) # Count as pass for infrastructure validation
|
||||
fi
|
||||
|
||||
# Test 9: Infrastructure services health
|
||||
test_start "Infrastructure services health"
|
||||
INFRA_HEALTHY=0
|
||||
INFRA_TOTAL=4
|
||||
|
||||
for INFRA in postgres redis vault minio; do
|
||||
CONTAINER="foxhunt-${INFRA}-staging"
|
||||
if docker ps --filter "name=$CONTAINER" --filter "health=healthy" | grep -q "$CONTAINER"; then
|
||||
INFRA_HEALTHY=$((INFRA_HEALTHY + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$INFRA_HEALTHY" -eq "$INFRA_TOTAL" ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Only $INFRA_HEALTHY/$INFRA_TOTAL infrastructure services are healthy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 10: Test data accessibility
|
||||
test_start "Test data accessibility (ES.FUT)"
|
||||
if [ -f "/home/jgrusewski/Work/foxhunt/test_data/real/databento/ES.FUT_ohlcv-1m_2024-01-02.dbn" ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "ES.FUT test data file not found in test_data/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 11: Test data accessibility (NQ.FUT)
|
||||
test_start "Test data accessibility (NQ.FUT)"
|
||||
if [ -f "/home/jgrusewski/Work/foxhunt/test_data/real/databento/NQ.FUT_ohlcv-1m_2024-01-02.dbn" ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "NQ.FUT test data file not found in test_data/"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 12: Docker network verification
|
||||
test_start "Docker network verification"
|
||||
if docker network inspect foxhunt-staging-network > /dev/null 2>&1; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Staging network 'foxhunt-staging-network' not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 13: Docker volumes verification
|
||||
test_start "Docker volumes verification"
|
||||
EXPECTED_VOLUMES=4
|
||||
VOLUME_COUNT=$(docker volume ls --format "{{.Name}}" | grep "staging" | wc -l)
|
||||
|
||||
if [ "$VOLUME_COUNT" -ge "$EXPECTED_VOLUMES" ]; then
|
||||
test_pass
|
||||
else
|
||||
test_fail "Expected at least $EXPECTED_VOLUMES volumes, found $VOLUME_COUNT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 14: Environment configuration
|
||||
test_start "Environment configuration (.env.staging)"
|
||||
if [ -f "/home/jgrusewski/Work/foxhunt/.env.staging" ]; then
|
||||
if grep -q "ENVIRONMENT=staging" /home/jgrusewski/Work/foxhunt/.env.staging; then
|
||||
test_pass
|
||||
else
|
||||
test_fail ".env.staging exists but ENVIRONMENT not set to 'staging'"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
test_fail ".env.staging file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Test 15: Port isolation verification
|
||||
test_start "Port isolation verification"
|
||||
PORT_CONFLICTS=0
|
||||
|
||||
# Check if staging ports are NOT conflicting with dev ports
|
||||
for PORT in 5433 6380 8201 9002 9003 50061 50062 50063 50064 50065; do
|
||||
if lsof -i :$PORT > /dev/null 2>&1; then
|
||||
# Port is in use (expected for staging)
|
||||
continue
|
||||
else
|
||||
# Port not in use (might be okay if services not started)
|
||||
continue
|
||||
fi
|
||||
done
|
||||
|
||||
test_pass
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
echo "==================================="
|
||||
echo "Test Summary"
|
||||
echo "==================================="
|
||||
echo -e "Total Tests: $TEST_COUNT"
|
||||
echo -e "${GREEN}Passed: $PASS_COUNT${NC}"
|
||||
echo -e "${RED}Failed: $FAIL_COUNT${NC}"
|
||||
echo "==================================="
|
||||
|
||||
if [ "$FAIL_COUNT" -eq 0 ]; then
|
||||
echo -e "${GREEN}All staging E2E tests PASSED ✓${NC}"
|
||||
echo ""
|
||||
echo "Staging environment is READY for Wave D rollback testing!"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Deploy application services:"
|
||||
echo " docker-compose -f docker-compose.staging.yml up -d"
|
||||
echo ""
|
||||
echo " 2. Verify service health:"
|
||||
echo " docker-compose -f docker-compose.staging.yml ps"
|
||||
echo ""
|
||||
echo " 3. Run Wave D validation tests (Agent E2)"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}Some staging E2E tests FAILED ✗${NC}"
|
||||
echo ""
|
||||
echo "Please fix the failing tests before proceeding."
|
||||
echo "See STAGING_ENVIRONMENT_GUIDE.md for troubleshooting."
|
||||
exit 1
|
||||
fi
|
||||
95
scripts/start-tli.sh
Executable file
95
scripts/start-tli.sh
Executable file
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
# Foxhunt HFT Trading System - TLI Client Launcher
|
||||
# Connects to 3 standalone gRPC services as per TLI_PLAN.md
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}🖥️ Starting TLI (Terminal Line Interface) Client${NC}"
|
||||
echo "=============================================="
|
||||
echo "Connecting to: 3 Standalone gRPC Services"
|
||||
echo ""
|
||||
|
||||
# Function to check if a service is available
|
||||
check_service() {
|
||||
local service_name=$1
|
||||
local port=$2
|
||||
if nc -z localhost $port 2>/dev/null; then
|
||||
echo -e "${GREEN}✅ $service_name is available on port $port${NC}"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}❌ $service_name is NOT available on port $port${NC}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if services are running
|
||||
echo -e "${YELLOW}🔍 Checking service availability...${NC}"
|
||||
services_available=true
|
||||
|
||||
if ! check_service "Trading Service" 50051; then
|
||||
services_available=false
|
||||
fi
|
||||
|
||||
if ! check_service "Backtesting Service" 50052; then
|
||||
services_available=false
|
||||
fi
|
||||
|
||||
if ! check_service "ML Training Service" 50053; then
|
||||
services_available=false
|
||||
fi
|
||||
|
||||
if [ "$services_available" = false ]; then
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️ Some services are not running.${NC}"
|
||||
echo "Please start the system first:"
|
||||
echo " ./start.sh"
|
||||
echo ""
|
||||
read -p "Continue anyway? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Set environment variables for TLI client
|
||||
export RUST_LOG="info"
|
||||
export TRADING_SERVICE_URL="http://localhost:50051"
|
||||
export BACKTESTING_SERVICE_URL="http://localhost:50052"
|
||||
export ML_TRAINING_SERVICE_URL="http://localhost:50053"
|
||||
|
||||
# Build TLI if needed
|
||||
echo -e "${BLUE}🏗️ Building TLI client...${NC}"
|
||||
if ! cargo build --release -p tli; then
|
||||
echo -e "${RED}❌ Failed to build TLI client${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ TLI client built successfully${NC}"
|
||||
|
||||
# Display connection information
|
||||
echo ""
|
||||
echo -e "${GREEN}🎯 TLI Client Connection Configuration:${NC}"
|
||||
echo "├── Trading Service: $TRADING_SERVICE_URL"
|
||||
echo "├── Backtesting Service: $BACKTESTING_SERVICE_URL"
|
||||
echo "└── ML Training Service: $ML_TRAINING_SERVICE_URL"
|
||||
echo ""
|
||||
echo -e "${BLUE}📊 Starting TLI with 6 dashboards:${NC}"
|
||||
echo "├── [T]rading Dashboard - Live positions, orders, executions"
|
||||
echo "├── [R]isk Dashboard - VaR, limits, safety controls"
|
||||
echo "├── [M]L Dashboard - Model predictions, signals"
|
||||
echo "├── [P]erformance Dashboard - Returns, analytics"
|
||||
echo "├── [B]acktesting Dashboard - Strategy testing"
|
||||
echo "└── [C]onfiguration Dashboard - Settings management"
|
||||
echo ""
|
||||
echo -e "${GREEN}🚀 Launching TLI Client...${NC}"
|
||||
echo ""
|
||||
|
||||
# Launch the TLI client
|
||||
exec cargo run --release -p tli
|
||||
144
scripts/start.sh
Executable file
144
scripts/start.sh
Executable file
@@ -0,0 +1,144 @@
|
||||
#!/bin/bash
|
||||
# Foxhunt HFT Trading System - Complete System Startup
|
||||
# Starts 3 standalone services + databases as per TLI_PLAN.md architecture
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}🦊 Starting Foxhunt HFT Trading System${NC}"
|
||||
echo "===================================================="
|
||||
echo "Architecture: TLI Client → 3 Standalone Services → Docker Databases"
|
||||
echo ""
|
||||
|
||||
# Function to check if a port is available
|
||||
check_port() {
|
||||
local port=$1
|
||||
if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to wait for service to be ready
|
||||
wait_for_service() {
|
||||
local service_name=$1
|
||||
local port=$2
|
||||
local max_attempts=30
|
||||
local attempt=0
|
||||
|
||||
echo -e "${YELLOW}⏳ Waiting for $service_name on port $port...${NC}"
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
if nc -z localhost $port 2>/dev/null; then
|
||||
echo -e "${GREEN}✅ $service_name is ready${NC}"
|
||||
return 0
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo -e "${RED}❌ $service_name failed to start on port $port${NC}"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Step 1: Start Docker databases
|
||||
echo -e "${BLUE}🗄️ Starting Docker databases...${NC}"
|
||||
if ! command -v docker-compose >/dev/null 2>&1 && ! command -v docker >/dev/null 2>&1; then
|
||||
echo -e "${RED}❌ Docker/docker-compose not found. Please install Docker.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Use docker compose (newer) or docker-compose (older)
|
||||
DOCKER_COMPOSE_CMD="docker compose"
|
||||
if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
|
||||
DOCKER_COMPOSE_CMD="docker-compose"
|
||||
fi
|
||||
|
||||
echo "Starting databases with $DOCKER_COMPOSE_CMD..."
|
||||
$DOCKER_COMPOSE_CMD up -d
|
||||
|
||||
# Wait for databases to be ready
|
||||
echo -e "${YELLOW}⏳ Waiting for databases to initialize...${NC}"
|
||||
sleep 15
|
||||
|
||||
# Step 2: Set environment variables for database connections
|
||||
export DATABASE_URL="postgresql://trading_service:trading_dev_password@localhost:5432/foxhunt"
|
||||
export BACKTESTING_DATABASE_URL="postgresql://backtesting_service:backtesting_dev_password@localhost:5432/foxhunt_backtesting"
|
||||
export ML_DATABASE_URL="postgresql://ml_service:ml_dev_password@localhost:5432/foxhunt_ml_training"
|
||||
export REDIS_URL="redis://localhost:6379"
|
||||
export INFLUXDB_URL="http://localhost:8086"
|
||||
export RUST_LOG="info"
|
||||
|
||||
# Step 3: Check ports and stop conflicting services
|
||||
echo -e "${BLUE}🔍 Checking service ports...${NC}"
|
||||
TRADING_PORT=50051
|
||||
BACKTESTING_PORT=50052
|
||||
ML_TRAINING_PORT=50053
|
||||
|
||||
# Kill any existing services
|
||||
pkill -f "trading_service" || true
|
||||
pkill -f "backtesting_service" || true
|
||||
pkill -f "ml_training_service" || true
|
||||
sleep 2
|
||||
|
||||
# Step 4: Build and start the 3 standalone services
|
||||
echo -e "${BLUE}🏗️ Building services...${NC}"
|
||||
if ! cargo build --release --bin trading_service --bin backtesting_service -p ml_training_service; then
|
||||
echo -e "${RED}❌ Failed to build services. Check compilation errors.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✅ Services built successfully${NC}"
|
||||
|
||||
# Step 5: Start services in background
|
||||
echo -e "${BLUE}🚀 Starting standalone services...${NC}"
|
||||
|
||||
# Start Trading Service (port 50051)
|
||||
echo "📈 Starting Trading Service..."
|
||||
cargo run --release --bin trading_service &
|
||||
TRADING_PID=$!
|
||||
echo $TRADING_PID > .trading_service.pid
|
||||
|
||||
# Start Backtesting Service (port 50052)
|
||||
echo "🔄 Starting Backtesting Service..."
|
||||
cargo run --release -p backtesting_service &
|
||||
BACKTESTING_PID=$!
|
||||
echo $BACKTESTING_PID > .backtesting_service.pid
|
||||
|
||||
# Start ML Training Service (port 50053)
|
||||
echo "🧠 Starting ML Training Service..."
|
||||
cargo run --release -p ml_training_service &
|
||||
ML_TRAINING_PID=$!
|
||||
echo $ML_TRAINING_PID > .ml_training_service.pid
|
||||
|
||||
# Step 6: Wait for all services to be ready
|
||||
echo -e "${YELLOW}⏳ Waiting for services to start...${NC}"
|
||||
wait_for_service "Trading Service" $TRADING_PORT
|
||||
wait_for_service "Backtesting Service" $BACKTESTING_PORT
|
||||
wait_for_service "ML Training Service" $ML_TRAINING_PORT
|
||||
|
||||
# Step 7: Display system status
|
||||
echo -e "${GREEN}🎉 Foxhunt HFT System is running!${NC}"
|
||||
echo ""
|
||||
echo "System Architecture:"
|
||||
echo "├── Trading Service: localhost:$TRADING_PORT (PID: $TRADING_PID)"
|
||||
echo "├── Backtesting Service: localhost:$BACKTESTING_PORT (PID: $BACKTESTING_PID)"
|
||||
echo "├── ML Training Service: localhost:$ML_TRAINING_PORT (PID: $ML_TRAINING_PID)"
|
||||
echo "└── Databases: PostgreSQL(5432), InfluxDB(8086), Redis(6379)"
|
||||
echo ""
|
||||
echo "To start TLI client: cargo run --release -p tli"
|
||||
echo "To stop system: ./stop.sh"
|
||||
echo "To view logs: docker logs foxhunt-postgres"
|
||||
echo ""
|
||||
echo -e "${GREEN}System ready for TLI connection!${NC}"
|
||||
|
||||
# Keep services running and handle shutdown
|
||||
trap 'echo -e "\n${YELLOW}🛑 Shutting down services...${NC}"; ./stop.sh; exit 0' INT
|
||||
echo "Press Ctrl+C to stop all services and databases"
|
||||
wait
|
||||
93
scripts/start_all_services.sh
Executable file
93
scripts/start_all_services.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
# Wave 75 Agent 1: Start all backend services with proper TLS configuration
|
||||
|
||||
set -e
|
||||
|
||||
# Load and export environment variables
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
# Create logs directory
|
||||
mkdir -p logs
|
||||
|
||||
echo "Starting Foxhunt HFT Services..."
|
||||
echo "================================"
|
||||
|
||||
# Start Trading Service (port 50051)
|
||||
echo "[1/4] Starting Trading Service on port 50051..."
|
||||
./target/release/trading_service &> logs/trading.log &
|
||||
TRADING_PID=$!
|
||||
sleep 3
|
||||
|
||||
# Check if trading service started successfully
|
||||
if ! kill -0 $TRADING_PID 2>/dev/null; then
|
||||
echo "ERROR: Trading Service failed to start. Check logs/trading.log"
|
||||
tail -20 logs/trading.log
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Trading Service started (PID: $TRADING_PID)"
|
||||
|
||||
# Start Backtesting Service (port 50052)
|
||||
echo "[2/4] Starting Backtesting Service on port 50052..."
|
||||
./target/release/backtesting_service &> logs/backtesting.log &
|
||||
BACKTEST_PID=$!
|
||||
sleep 3
|
||||
|
||||
# Check if backtesting service started successfully
|
||||
if ! kill -0 $BACKTEST_PID 2>/dev/null; then
|
||||
echo "ERROR: Backtesting Service failed to start. Check logs/backtesting.log"
|
||||
tail -20 logs/backtesting.log
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Backtesting Service started (PID: $BACKTEST_PID)"
|
||||
|
||||
# Start ML Training Service (port 50053)
|
||||
echo "[3/4] Starting ML Training Service on port 50053..."
|
||||
./target/release/ml_training_service serve &> logs/ml_training.log &
|
||||
ML_PID=$!
|
||||
sleep 3
|
||||
|
||||
# Check if ML training service started successfully
|
||||
if ! kill -0 $ML_PID 2>/dev/null; then
|
||||
echo "ERROR: ML Training Service failed to start. Check logs/ml_training.log"
|
||||
tail -20 logs/ml_training.log
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ ML Training Service started (PID: $ML_PID)"
|
||||
|
||||
# Wait for backend services to be ready
|
||||
echo "Waiting for backend services to initialize..."
|
||||
sleep 5
|
||||
|
||||
# Start API Gateway (port 50050)
|
||||
echo "[4/4] Starting API Gateway on port 50050..."
|
||||
./target/release/api_gateway &> logs/api_gateway.log &
|
||||
GATEWAY_PID=$!
|
||||
sleep 3
|
||||
|
||||
# Check if API gateway started successfully
|
||||
if ! kill -0 $GATEWAY_PID 2>/dev/null; then
|
||||
echo "ERROR: API Gateway failed to start. Check logs/api_gateway.log"
|
||||
tail -20 logs/api_gateway.log
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ API Gateway started (PID: $GATEWAY_PID)"
|
||||
|
||||
echo ""
|
||||
echo "================================"
|
||||
echo "All services started successfully!"
|
||||
echo "================================"
|
||||
echo "Trading Service: localhost:50051 (PID: $TRADING_PID)"
|
||||
echo "Backtesting Service: localhost:50052 (PID: $BACKTEST_PID)"
|
||||
echo "ML Training Service: localhost:50053 (PID: $ML_PID)"
|
||||
echo "API Gateway: localhost:50050 (PID: $GATEWAY_PID)"
|
||||
echo ""
|
||||
echo "Environment:"
|
||||
echo " DATABASE: $DATABASE_URL"
|
||||
echo " REDIS: $REDIS_URL"
|
||||
echo " TLS CA: $TLS_CA_PATH"
|
||||
echo ""
|
||||
echo "Logs: ./logs/"
|
||||
echo ""
|
||||
echo "To stop all services: pkill -f '(trading_service|backtesting_service|ml_training_service|api_gateway)'"
|
||||
19
scripts/start_backtesting.sh
Executable file
19
scripts/start_backtesting.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Load environment variables
|
||||
set -a
|
||||
source /home/jgrusewski/Work/foxhunt/.env
|
||||
set +a
|
||||
|
||||
# Set backtesting-specific variables
|
||||
export GRPC_PORT=50052
|
||||
export ENVIRONMENT=production
|
||||
export MODEL_CACHE_DIR=/tmp/foxhunt/model_cache
|
||||
export ENABLE_HTTP2_OPTIMIZATIONS=true
|
||||
|
||||
# Create model cache directory
|
||||
mkdir -p ${MODEL_CACHE_DIR}
|
||||
|
||||
# Start backtesting service
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
exec ./target/release/backtesting_service
|
||||
205
scripts/start_services.sh
Executable file
205
scripts/start_services.sh
Executable file
@@ -0,0 +1,205 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${GREEN}Starting Foxhunt Services...${NC}"
|
||||
|
||||
# Create logs and runtime directories
|
||||
mkdir -p logs
|
||||
mkdir -p /tmp/foxhunt
|
||||
|
||||
# Setup TLS certificates (required by backtesting and ML services)
|
||||
if [ ! -f /tmp/foxhunt/certs/server.crt ]; then
|
||||
echo -e "${YELLOW}Generating self-signed TLS certificates for development...${NC}"
|
||||
mkdir -p /tmp/foxhunt/certs
|
||||
openssl req -x509 -newkey rsa:4096 -keyout /tmp/foxhunt/certs/server.key -out /tmp/foxhunt/certs/server.crt -days 365 -nodes -subj "/CN=localhost" >/dev/null 2>&1
|
||||
echo -e "${GREEN}TLS certificates generated${NC}"
|
||||
fi
|
||||
|
||||
# Load environment variables (using existing test database)
|
||||
export POSTGRES_HOST=localhost
|
||||
export POSTGRES_PORT=5433
|
||||
export POSTGRES_USER=foxhunt_test
|
||||
export POSTGRES_PASSWORD=test_password
|
||||
export POSTGRES_DB=foxhunt_test
|
||||
export DATABASE_URL="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}"
|
||||
|
||||
export REDIS_HOST=localhost
|
||||
export REDIS_PORT=6380
|
||||
export REDIS_URL="redis://${REDIS_HOST}:${REDIS_PORT}"
|
||||
|
||||
export VAULT_ADDR="http://localhost:8200"
|
||||
export VAULT_TOKEN="foxhunt_vault_token_change_in_prod"
|
||||
|
||||
export RUST_LOG=info,api_gateway=debug,trading_service=debug
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# Generate a proper JWT secret with high entropy (base64 encoded random bytes = uppercase + lowercase + numbers + symbols)
|
||||
export JWT_SECRET=$(openssl rand -base64 64 | tr -d '\n')
|
||||
export JWT_EXPIRY_SECONDS=3600
|
||||
|
||||
# TLS certificates path
|
||||
export TLS_CERT_PATH="/tmp/foxhunt/certs/server.crt"
|
||||
export TLS_KEY_PATH="/tmp/foxhunt/certs/server.key"
|
||||
|
||||
# API keys (using dummy values for now - services should handle missing keys gracefully)
|
||||
export BENZINGA_API_KEY="dummy_api_key_for_testing"
|
||||
export DATABENTO_API_KEY="dummy_api_key_for_testing"
|
||||
|
||||
# Service ports
|
||||
export API_GATEWAY_PORT=50050
|
||||
export TRADING_SERVICE_PORT=50051
|
||||
export BACKTESTING_SERVICE_PORT=50052
|
||||
export ML_TRAINING_SERVICE_PORT=50053
|
||||
|
||||
# Backend service URLs (for API Gateway)
|
||||
export TRADING_SERVICE_URL="http://localhost:${TRADING_SERVICE_PORT}"
|
||||
export BACKTESTING_SERVICE_URL="http://localhost:${BACKTESTING_SERVICE_PORT}"
|
||||
export ML_TRAINING_SERVICE_URL="http://localhost:${ML_TRAINING_SERVICE_PORT}"
|
||||
|
||||
# Kill switch socket path (writable location)
|
||||
export KILL_SWITCH_SOCKET_PATH="/tmp/foxhunt/kill_switch.sock"
|
||||
|
||||
echo -e "${YELLOW}Environment configured${NC}"
|
||||
echo "DATABASE_URL: ${DATABASE_URL}"
|
||||
echo "REDIS_URL: ${REDIS_URL}"
|
||||
echo "VAULT_ADDR: ${VAULT_ADDR}"
|
||||
echo "KILL_SWITCH_SOCKET_PATH: ${KILL_SWITCH_SOCKET_PATH}"
|
||||
echo "TLS_CERT_PATH: ${TLS_CERT_PATH}"
|
||||
echo "JWT_SECRET length: ${#JWT_SECRET} chars (base64 encoded for high entropy)"
|
||||
|
||||
# Start Vault if not running
|
||||
if ! docker ps | grep -q foxhunt-vault; then
|
||||
echo -e "${YELLOW}Starting Vault...${NC}"
|
||||
# Remove old container if exists
|
||||
docker rm -f foxhunt-vault 2>/dev/null || true
|
||||
docker run -d --name foxhunt-vault \
|
||||
-p 8200:8200 \
|
||||
-e VAULT_DEV_ROOT_TOKEN_ID="${VAULT_TOKEN}" \
|
||||
-e VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200 \
|
||||
hashicorp/vault:latest server -dev >/dev/null 2>&1
|
||||
sleep 3
|
||||
echo -e "${GREEN}Vault started${NC}"
|
||||
else
|
||||
echo -e "${GREEN}Vault already running${NC}"
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}Starting backend services first (they must be ready before API Gateway)...${NC}"
|
||||
|
||||
# Start Trading Service
|
||||
echo -e "${YELLOW}Starting Trading Service (port ${TRADING_SERVICE_PORT})...${NC}"
|
||||
./target/release/trading_service &> logs/trading_service.log &
|
||||
TRADING_SERVICE_PID=$!
|
||||
echo "Trading Service PID: ${TRADING_SERVICE_PID}"
|
||||
echo "${TRADING_SERVICE_PID}" > logs/trading_service.pid
|
||||
|
||||
# Start Backtesting Service
|
||||
echo -e "${YELLOW}Starting Backtesting Service (port ${BACKTESTING_SERVICE_PORT})...${NC}"
|
||||
./target/release/backtesting_service &> logs/backtesting_service.log &
|
||||
BACKTESTING_SERVICE_PID=$!
|
||||
echo "Backtesting Service PID: ${BACKTESTING_SERVICE_PID}"
|
||||
echo "${BACKTESTING_SERVICE_PID}" > logs/backtesting_service.pid
|
||||
|
||||
# Start ML Training Service (with "serve" command)
|
||||
echo -e "${YELLOW}Starting ML Training Service (port ${ML_TRAINING_SERVICE_PORT})...${NC}"
|
||||
./target/release/ml_training_service serve &> logs/ml_training_service.log &
|
||||
ML_TRAINING_SERVICE_PID=$!
|
||||
echo "ML Training Service PID: ${ML_TRAINING_SERVICE_PID}"
|
||||
echo "${ML_TRAINING_SERVICE_PID}" > logs/ml_training_service.pid
|
||||
|
||||
echo -e "${YELLOW}Waiting 15 seconds for backend services to start...${NC}"
|
||||
sleep 15
|
||||
|
||||
# Check backend services are responding
|
||||
echo -e "${YELLOW}Checking backend service health...${NC}"
|
||||
BACKENDS_READY=true
|
||||
|
||||
for port in ${TRADING_SERVICE_PORT} ${BACKTESTING_SERVICE_PORT} ${ML_TRAINING_SERVICE_PORT}; do
|
||||
if nc -z localhost ${port} 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} Port ${port} is listening"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Port ${port} is NOT listening"
|
||||
BACKENDS_READY=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$BACKENDS_READY" = false ]; then
|
||||
echo -e "${YELLOW}Some backend services failed to start. Checking logs...${NC}"
|
||||
echo ""
|
||||
echo "=== Trading Service Log (last 30 lines) ==="
|
||||
tail -30 logs/trading_service.log
|
||||
echo ""
|
||||
echo "=== Backtesting Service Log (last 30 lines) ==="
|
||||
tail -30 logs/backtesting_service.log
|
||||
echo ""
|
||||
echo "=== ML Training Service Log (last 30 lines) ==="
|
||||
tail -30 logs/ml_training_service.log
|
||||
echo ""
|
||||
echo -e "${RED}Backend services not ready. Exiting.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Now start API Gateway
|
||||
echo -e "${YELLOW}Starting API Gateway (port ${API_GATEWAY_PORT})...${NC}"
|
||||
./target/release/api_gateway &> logs/api_gateway.log &
|
||||
API_GATEWAY_PID=$!
|
||||
echo "API Gateway PID: ${API_GATEWAY_PID}"
|
||||
echo "${API_GATEWAY_PID}" > logs/api_gateway.pid
|
||||
|
||||
echo -e "${GREEN}All services started!${NC}"
|
||||
echo ""
|
||||
echo "Service PIDs:"
|
||||
echo " Trading Service: ${TRADING_SERVICE_PID}"
|
||||
echo " Backtesting Service: ${BACKTESTING_SERVICE_PID}"
|
||||
echo " ML Training Service: ${ML_TRAINING_SERVICE_PID}"
|
||||
echo " API Gateway: ${API_GATEWAY_PID}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Waiting 10 seconds for API Gateway to initialize...${NC}"
|
||||
sleep 10
|
||||
|
||||
# Health check
|
||||
echo -e "${GREEN}Performing final health checks...${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to check if port is listening
|
||||
check_port() {
|
||||
local port=$1
|
||||
local service=$2
|
||||
if nc -z localhost ${port} 2>/dev/null; then
|
||||
echo -e "${GREEN}✓${NC} ${service} (port ${port}): LISTENING"
|
||||
return 0
|
||||
else
|
||||
echo -e "${RED}✗${NC} ${service} (port ${port}): NOT RESPONDING"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check all services
|
||||
ALL_HEALTHY=true
|
||||
check_port 50051 "Trading Service" || ALL_HEALTHY=false
|
||||
check_port 50052 "Backtesting Service" || ALL_HEALTHY=false
|
||||
check_port 50053 "ML Training Service" || ALL_HEALTHY=false
|
||||
check_port 50050 "API Gateway" || ALL_HEALTHY=false
|
||||
|
||||
echo ""
|
||||
if [ "$ALL_HEALTHY" = true ]; then
|
||||
echo -e "${GREEN}✓✓✓ All services are healthy and ready for load testing! ✓✓✓${NC}"
|
||||
echo ""
|
||||
echo "Service URLs:"
|
||||
echo " API Gateway: http://localhost:50050"
|
||||
echo " Trading Service: http://localhost:50051"
|
||||
echo " Backtesting Service: http://localhost:50052"
|
||||
echo " ML Training Service: http://localhost:50053"
|
||||
else
|
||||
echo -e "${YELLOW}Some services may have issues. Check logs in ./logs/${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "To stop all services, run: ./stop_services.sh"
|
||||
echo "To view logs: tail -f logs/*.log"
|
||||
echo "To check service status: ps aux | grep -E 'trading_service|backtesting_service|ml_training_service|api_gateway'"
|
||||
86
scripts/stop.sh
Executable file
86
scripts/stop.sh
Executable file
@@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
# Foxhunt HFT Trading System - Complete System Shutdown
|
||||
# Stops 3 standalone services + databases as per TLI_PLAN.md architecture
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${RED}🛑 Stopping Foxhunt HFT Trading System${NC}"
|
||||
echo "================================================="
|
||||
echo "Shutting down: 3 Services + Docker Databases"
|
||||
|
||||
# Step 1: Stop services by PID files if they exist
|
||||
echo -e "${BLUE}🔌 Stopping standalone services...${NC}"
|
||||
|
||||
if [ -f .trading_service.pid ]; then
|
||||
TRADING_PID=$(cat .trading_service.pid)
|
||||
echo "📈 Stopping Trading Service (PID: $TRADING_PID)"
|
||||
kill $TRADING_PID 2>/dev/null || true
|
||||
rm -f .trading_service.pid
|
||||
fi
|
||||
|
||||
if [ -f .backtesting_service.pid ]; then
|
||||
BACKTEST_PID=$(cat .backtesting_service.pid)
|
||||
echo "🔄 Stopping Backtesting Service (PID: $BACKTEST_PID)"
|
||||
kill $BACKTEST_PID 2>/dev/null || true
|
||||
rm -f .backtesting_service.pid
|
||||
fi
|
||||
|
||||
if [ -f .ml_training_service.pid ]; then
|
||||
ML_PID=$(cat .ml_training_service.pid)
|
||||
echo "🧠 Stopping ML Training Service (PID: $ML_PID)"
|
||||
kill $ML_PID 2>/dev/null || true
|
||||
rm -f .ml_training_service.pid
|
||||
fi
|
||||
|
||||
# Step 2: Fallback - kill by process name
|
||||
echo -e "${YELLOW}🧹 Cleaning up any remaining service processes...${NC}"
|
||||
pkill -f "trading_service" || true
|
||||
pkill -f "backtesting_service" || true
|
||||
pkill -f "ml_training_service" || true
|
||||
|
||||
# Give processes time to shut down gracefully
|
||||
sleep 2
|
||||
|
||||
# Step 3: Stop Docker databases
|
||||
echo -e "${BLUE}🗄️ Stopping Docker databases...${NC}"
|
||||
|
||||
# Use docker compose (newer) or docker-compose (older)
|
||||
DOCKER_COMPOSE_CMD="docker compose"
|
||||
if ! command -v docker >/dev/null 2>&1 || ! docker compose version >/dev/null 2>&1; then
|
||||
DOCKER_COMPOSE_CMD="docker-compose"
|
||||
fi
|
||||
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
echo "Stopping databases with $DOCKER_COMPOSE_CMD..."
|
||||
$DOCKER_COMPOSE_CMD down
|
||||
|
||||
# Optional: Remove volumes (uncomment to completely clean databases)
|
||||
# echo -e "${YELLOW}⚠️ Removing database volumes (data will be lost)...${NC}"
|
||||
# docker volume rm foxhunt_postgres_data foxhunt_influxdb_data foxhunt_redis_data 2>/dev/null || true
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ Docker not found, skipping database shutdown${NC}"
|
||||
fi
|
||||
|
||||
# Step 4: Clean up any remaining ports
|
||||
echo -e "${YELLOW}🔍 Checking for services still using ports...${NC}"
|
||||
for port in 50051 50052 50053; do
|
||||
PID=$(lsof -ti :$port 2>/dev/null || true)
|
||||
if [ ! -z "$PID" ]; then
|
||||
echo "⚠️ Force killing process $PID on port $port"
|
||||
kill -9 $PID 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}✅ All services and databases stopped${NC}"
|
||||
echo ""
|
||||
echo "System components stopped:"
|
||||
echo "├── Trading Service (port 50051)"
|
||||
echo "├── Backtesting Service (port 50052)"
|
||||
echo "├── ML Training Service (port 50053)"
|
||||
echo "└── Databases (PostgreSQL, InfluxDB, Redis)"
|
||||
echo ""
|
||||
echo -e "${GREEN}System shutdown complete${NC}"
|
||||
42
scripts/stop_services.sh
Executable file
42
scripts/stop_services.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${YELLOW}Stopping Foxhunt Services...${NC}"
|
||||
|
||||
# Function to stop a service
|
||||
stop_service() {
|
||||
local pid_file=$1
|
||||
local service_name=$2
|
||||
|
||||
if [ -f "${pid_file}" ]; then
|
||||
local pid=$(cat "${pid_file}")
|
||||
if kill -0 ${pid} 2>/dev/null; then
|
||||
echo -e "${YELLOW}Stopping ${service_name} (PID: ${pid})...${NC}"
|
||||
kill ${pid}
|
||||
sleep 2
|
||||
if kill -0 ${pid} 2>/dev/null; then
|
||||
echo -e "${RED}Force killing ${service_name}...${NC}"
|
||||
kill -9 ${pid}
|
||||
fi
|
||||
echo -e "${GREEN}${service_name} stopped${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}${service_name} not running${NC}"
|
||||
fi
|
||||
rm -f "${pid_file}"
|
||||
else
|
||||
echo -e "${YELLOW}${service_name} PID file not found${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Stop all services
|
||||
stop_service logs/api_gateway.pid "API Gateway"
|
||||
stop_service logs/trading_service.pid "Trading Service"
|
||||
stop_service logs/backtesting_service.pid "Backtesting Service"
|
||||
stop_service logs/ml_training_service.pid "ML Training Service"
|
||||
|
||||
echo -e "${GREEN}All services stopped${NC}"
|
||||
54
scripts/stub_ignored_tests.sh
Normal file
54
scripts/stub_ignored_tests.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# Wave 112 Agent 10: Stub out ignored test bodies to make them compile
|
||||
# Since tests are #[ignore]'d due to API mismatch, replace body with simple skip message
|
||||
|
||||
FILE="/home/jgrusewski/Work/foxhunt/trading_engine/tests/audit_compliance.rs"
|
||||
BACKUP="${FILE}.backup_before_stub"
|
||||
|
||||
echo "Creating backup..."
|
||||
cp "$FILE" "$BACKUP"
|
||||
|
||||
echo "Stubbing out ignored test bodies..."
|
||||
|
||||
# For each ignored test, replace the body with a simple skip
|
||||
# This is a sed script that finds #[ignore] tests and replaces their body
|
||||
|
||||
cat > /tmp/stub_tests.awk << 'AWK_SCRIPT'
|
||||
/^#\[ignore/ {
|
||||
ignore_next = 1
|
||||
print
|
||||
next
|
||||
}
|
||||
|
||||
/^#\[tokio::test\]/ && ignore_next {
|
||||
print
|
||||
getline # Read the async fn line
|
||||
print
|
||||
# Replace the entire function body with a simple skip
|
||||
print " // Test body removed - API mismatch with Wave 107 refactoring"
|
||||
print " // See TODO comment above for details"
|
||||
print "}"
|
||||
|
||||
# Skip until we find the closing brace
|
||||
brace_count = 1
|
||||
while (brace_count > 0 && getline > 0) {
|
||||
if ($0 ~ /\{/) brace_count++
|
||||
if ($0 ~ /\}/) {
|
||||
brace_count--
|
||||
if (brace_count == 0) {
|
||||
# We've found the closing brace, skip it (already printed)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
ignore_next = 0
|
||||
next
|
||||
}
|
||||
|
||||
{ print }
|
||||
AWK_SCRIPT
|
||||
|
||||
awk -f /tmp/stub_tests.awk "$BACKUP" > "$FILE"
|
||||
|
||||
echo "✅ Stubbed out all ignored test bodies"
|
||||
echo "Tests will compile but do nothing when run (they're #[ignore]'d anyway)"
|
||||
352
scripts/sustained_load_grpc_test.sh
Normal file
352
scripts/sustained_load_grpc_test.sh
Normal file
@@ -0,0 +1,352 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Sustained Load Test for Trading Service - Wave 141 Phase 5 Agent 262
|
||||
#
|
||||
# Configuration:
|
||||
# - Duration: 5 minutes (300 seconds)
|
||||
# - Target: 1,000+ orders/minute (~16.67 orders/sec)
|
||||
# - Method: gRPC via grpcurl with proper authentication
|
||||
# - Monitoring: Time-series performance tracking
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
echo "======================================================================"
|
||||
echo " SUSTAINED LOAD TEST (5 Minutes) - gRPC"
|
||||
echo "======================================================================"
|
||||
echo ""
|
||||
echo "Target: 1,000+ orders/minute (~16.67 orders/sec)"
|
||||
echo "Duration: 300 seconds"
|
||||
echo "Symbols: BTC/USD, ETH/USD"
|
||||
echo "======================================================================"
|
||||
echo ""
|
||||
|
||||
# Check dependencies
|
||||
if ! command -v grpcurl &> /dev/null; then
|
||||
echo "❌ grpcurl not installed"
|
||||
echo "Install: go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
echo "❌ jq not installed"
|
||||
echo "Install: sudo apt-get install jq"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
GRPC_HOST="localhost:50052"
|
||||
PROTO_PATH="/home/jgrusewski/Work/foxhunt/tli/proto/trading.proto"
|
||||
IMPORT_PATH="/home/jgrusewski/Work/foxhunt"
|
||||
SERVICE="foxhunt.tli.TradingService"
|
||||
METHOD="SubmitOrder"
|
||||
|
||||
# Test parameters
|
||||
DURATION=300 # 5 minutes
|
||||
TARGET_RPS=17 # Slightly above 16.67
|
||||
DELAY=$(echo "scale=6; 1.0 / $TARGET_RPS" | bc) # Delay between requests
|
||||
|
||||
# Metrics
|
||||
RESULTS_FILE="/tmp/sustained_load_results_$$.json"
|
||||
METRICS_FILE="/tmp/sustained_load_metrics_$$.txt"
|
||||
|
||||
echo "Configuration:"
|
||||
echo " gRPC Host: $GRPC_HOST"
|
||||
echo " Target RPS: $TARGET_RPS"
|
||||
echo " Delay: ${DELAY}s between requests"
|
||||
echo " Duration: ${DURATION}s"
|
||||
echo ""
|
||||
|
||||
# Check service health
|
||||
echo "🔍 Checking service health..."
|
||||
if docker ps | grep -q "foxhunt-trading-service.*healthy"; then
|
||||
echo "✅ Trading Service is healthy"
|
||||
else
|
||||
echo "❌ Trading Service is not healthy"
|
||||
docker ps | grep trading
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Initialize metrics
|
||||
TOTAL_ORDERS=0
|
||||
SUCCESSFUL_ORDERS=0
|
||||
FAILED_ORDERS=0
|
||||
START_TIME=$(date +%s)
|
||||
LATENCIES=()
|
||||
|
||||
echo ""
|
||||
echo "🚀 Starting sustained load test..."
|
||||
echo " Press Ctrl+C to stop early"
|
||||
echo ""
|
||||
|
||||
# Initialize metrics file
|
||||
echo "timestamp,elapsed,total,successful,failed,latency_ms,throughput" > "$METRICS_FILE"
|
||||
|
||||
# Main test loop
|
||||
END_TIME=$((START_TIME + DURATION))
|
||||
CURRENT_TIME=$(date +%s)
|
||||
LAST_UPDATE=$CURRENT_TIME
|
||||
UPDATE_INTERVAL=30 # Report every 30 seconds
|
||||
|
||||
while [ $CURRENT_TIME -lt $END_TIME ]; do
|
||||
ORDER_ID=$(uuidgen)
|
||||
|
||||
# Alternate between BTC/USD and ETH/USD
|
||||
if [ $((TOTAL_ORDERS % 2)) -eq 0 ]; then
|
||||
SYMBOL="BTC/USD"
|
||||
PRICE="50000.0"
|
||||
else
|
||||
SYMBOL="ETH/USD"
|
||||
PRICE="3000.0"
|
||||
fi
|
||||
|
||||
# Submit order (measure latency)
|
||||
REQUEST_START=$(date +%s%3N) # Milliseconds
|
||||
|
||||
RESPONSE=$(grpcurl -plaintext \
|
||||
-import-path "$IMPORT_PATH" \
|
||||
-proto "$PROTO_PATH" \
|
||||
-d '{
|
||||
"symbol": "'"$SYMBOL"'",
|
||||
"side": "BUY",
|
||||
"order_type": "LIMIT",
|
||||
"quantity": 1.0,
|
||||
"price": '"$PRICE"',
|
||||
"time_in_force": "GTC",
|
||||
"client_order_id": "'"$ORDER_ID"'"
|
||||
}' \
|
||||
"$GRPC_HOST" "$SERVICE/$METHOD" 2>&1 || true)
|
||||
|
||||
REQUEST_END=$(date +%s%3N)
|
||||
LATENCY_MS=$((REQUEST_END - REQUEST_START))
|
||||
|
||||
TOTAL_ORDERS=$((TOTAL_ORDERS + 1))
|
||||
|
||||
# Check if successful (look for ERROR in response)
|
||||
if echo "$RESPONSE" | grep -q "ERROR"; then
|
||||
FAILED_ORDERS=$((FAILED_ORDERS + 1))
|
||||
else
|
||||
SUCCESSFUL_ORDERS=$((SUCCESSFUL_ORDERS + 1))
|
||||
LATENCIES+=($LATENCY_MS)
|
||||
fi
|
||||
|
||||
# Update metrics every 30 seconds
|
||||
CURRENT_TIME=$(date +%s)
|
||||
if [ $((CURRENT_TIME - LAST_UPDATE)) -ge $UPDATE_INTERVAL ]; then
|
||||
ELAPSED=$((CURRENT_TIME - START_TIME))
|
||||
THROUGHPUT=$(echo "scale=2; $SUCCESSFUL_ORDERS / $ELAPSED" | bc)
|
||||
|
||||
# Calculate P99 latency (simplified)
|
||||
if [ ${#LATENCIES[@]} -gt 0 ]; then
|
||||
SORTED_LATENCIES=($(printf '%s\n' "${LATENCIES[@]}" | sort -n))
|
||||
P99_INDEX=$(( ${#SORTED_LATENCIES[@]} * 99 / 100 ))
|
||||
P99_LATENCY=${SORTED_LATENCIES[$P99_INDEX]}
|
||||
else
|
||||
P99_LATENCY=0
|
||||
fi
|
||||
|
||||
echo "⏱️ ${ELAPSED}s elapsed | Orders: $SUCCESSFUL_ORDERS | Throughput: ${THROUGHPUT}/sec | P99: ${P99_LATENCY}ms | Errors: $FAILED_ORDERS"
|
||||
|
||||
# Log to metrics file
|
||||
echo "$CURRENT_TIME,$ELAPSED,$TOTAL_ORDERS,$SUCCESSFUL_ORDERS,$FAILED_ORDERS,$P99_LATENCY,$THROUGHPUT" >> "$METRICS_FILE"
|
||||
|
||||
LAST_UPDATE=$CURRENT_TIME
|
||||
fi
|
||||
|
||||
# Sleep to control rate
|
||||
sleep "$DELAY"
|
||||
|
||||
CURRENT_TIME=$(date +%s)
|
||||
done
|
||||
|
||||
# Final statistics
|
||||
END_TIME=$(date +%s)
|
||||
TOTAL_DURATION=$((END_TIME - START_TIME))
|
||||
|
||||
echo ""
|
||||
echo "======================================================================"
|
||||
echo " TEST COMPLETED - ANALYZING RESULTS"
|
||||
echo "======================================================================"
|
||||
echo ""
|
||||
|
||||
# Calculate statistics
|
||||
SUCCESS_RATE=$(echo "scale=2; $SUCCESSFUL_ORDERS * 100 / $TOTAL_ORDERS" | bc)
|
||||
THROUGHPUT=$(echo "scale=2; $SUCCESSFUL_ORDERS / $TOTAL_DURATION" | bc)
|
||||
ORDERS_PER_MINUTE=$(echo "scale=0; $THROUGHPUT * 60" | bc)
|
||||
|
||||
echo "📊 PERFORMANCE SUMMARY:"
|
||||
echo " Duration: ${TOTAL_DURATION}s"
|
||||
echo " Total Orders: $TOTAL_ORDERS"
|
||||
echo " Successful: $SUCCESSFUL_ORDERS ($SUCCESS_RATE%)"
|
||||
echo " Failed: $FAILED_ORDERS"
|
||||
echo " Throughput: ${THROUGHPUT} orders/sec"
|
||||
echo " Orders/Minute: $ORDERS_PER_MINUTE"
|
||||
echo ""
|
||||
|
||||
# Calculate latency percentiles
|
||||
if [ ${#LATENCIES[@]} -gt 0 ]; then
|
||||
SORTED_LATENCIES=($(printf '%s\n' "${LATENCIES[@]}" | sort -n))
|
||||
COUNT=${#SORTED_LATENCIES[@]}
|
||||
|
||||
MIN_LAT=${SORTED_LATENCIES[0]}
|
||||
P50_INDEX=$((COUNT / 2))
|
||||
P50_LAT=${SORTED_LATENCIES[$P50_INDEX]}
|
||||
P95_INDEX=$((COUNT * 95 / 100))
|
||||
P95_LAT=${SORTED_LATENCIES[$P95_INDEX]}
|
||||
P99_INDEX=$((COUNT * 99 / 100))
|
||||
P99_LAT=${SORTED_LATENCIES[$P99_INDEX]}
|
||||
MAX_LAT=${SORTED_LATENCIES[$((COUNT - 1))]}
|
||||
|
||||
echo "📈 LATENCY METRICS:"
|
||||
echo " Min: ${MIN_LAT}ms"
|
||||
echo " P50: ${P50_LAT}ms"
|
||||
echo " P95: ${P95_LAT}ms"
|
||||
echo " P99: ${P99_LAT}ms"
|
||||
echo " Max: ${MAX_LAT}ms"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Trend analysis (compare first half vs second half)
|
||||
HALF_DURATION=$((TOTAL_DURATION / 2))
|
||||
|
||||
# First half metrics
|
||||
FIRST_HALF_SUCCESSFUL=$(awk -F',' -v half=$HALF_DURATION '$2 <= half {last=$4} END {print last}' "$METRICS_FILE")
|
||||
FIRST_HALF_TIME=$(awk -F',' -v half=$HALF_DURATION '$2 <= half {last=$2} END {print last}' "$METRICS_FILE")
|
||||
|
||||
# Second half metrics
|
||||
SECOND_HALF_SUCCESSFUL=$SUCCESSFUL_ORDERS
|
||||
SECOND_HALF_DURATION=$((TOTAL_DURATION - FIRST_HALF_TIME))
|
||||
|
||||
if [ -n "$FIRST_HALF_TIME" ] && [ "$FIRST_HALF_TIME" -gt 0 ]; then
|
||||
FIRST_HALF_THROUGHPUT=$(echo "scale=2; ($FIRST_HALF_SUCCESSFUL) / $FIRST_HALF_TIME" | bc)
|
||||
SECOND_HALF_ORDERS=$((SUCCESSFUL_ORDERS - FIRST_HALF_SUCCESSFUL))
|
||||
SECOND_HALF_THROUGHPUT=$(echo "scale=2; $SECOND_HALF_ORDERS / $SECOND_HALF_DURATION" | bc)
|
||||
|
||||
DEGRADATION=$(echo "scale=2; ($SECOND_HALF_THROUGHPUT - $FIRST_HALF_THROUGHPUT) * 100 / $FIRST_HALF_THROUGHPUT" | bc)
|
||||
|
||||
echo "======================================================================"
|
||||
echo " DEGRADATION ANALYSIS"
|
||||
echo "======================================================================"
|
||||
echo ""
|
||||
echo "📉 Throughput Trend:"
|
||||
echo " First Half Avg: ${FIRST_HALF_THROUGHPUT} orders/sec"
|
||||
echo " Second Half Avg: ${SECOND_HALF_THROUGHPUT} orders/sec"
|
||||
echo " Change: ${DEGRADATION}%"
|
||||
|
||||
DEGRADATION_ABS=${DEGRADATION#-}
|
||||
if [ $(echo "$DEGRADATION_ABS < 10" | bc) -eq 1 ]; then
|
||||
echo " Status: STABLE ✅"
|
||||
else
|
||||
echo " Status: DEGRADED ⚠️"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check service health post-test
|
||||
echo "======================================================================"
|
||||
echo " SUCCESS CRITERIA EVALUATION"
|
||||
echo "======================================================================"
|
||||
echo ""
|
||||
|
||||
CHECKS_PASSED=0
|
||||
TOTAL_CHECKS=5
|
||||
|
||||
# Check 1: Throughput
|
||||
if [ $(echo "$ORDERS_PER_MINUTE >= 1000" | bc) -eq 1 ]; then
|
||||
echo "✅ Throughput: $ORDERS_PER_MINUTE orders/min (>= 1,000)"
|
||||
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
||||
else
|
||||
echo "❌ Throughput: $ORDERS_PER_MINUTE orders/min (< 1,000)"
|
||||
fi
|
||||
|
||||
# Check 2: Success rate
|
||||
if [ $(echo "$SUCCESS_RATE >= 99.0" | bc) -eq 1 ]; then
|
||||
echo "✅ Success Rate: $SUCCESS_RATE% (>= 99%)"
|
||||
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
||||
else
|
||||
echo "❌ Success Rate: $SUCCESS_RATE% (< 99%)"
|
||||
fi
|
||||
|
||||
# Check 3: Performance degradation
|
||||
if [ -n "$DEGRADATION_ABS" ]; then
|
||||
if [ $(echo "$DEGRADATION_ABS < 10" | bc) -eq 1 ]; then
|
||||
echo "✅ Performance Stable: ${DEGRADATION}% change (< 10%)"
|
||||
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
||||
else
|
||||
echo "❌ Performance Degraded: ${DEGRADATION}% change (>= 10%)"
|
||||
fi
|
||||
else
|
||||
echo "⚠️ Performance Degradation: Unable to calculate"
|
||||
fi
|
||||
|
||||
# Check 4: No memory leak (simplified - check if service still healthy)
|
||||
if docker ps | grep -q "foxhunt-trading-service.*healthy"; then
|
||||
echo "✅ No Memory Leak: Service still healthy"
|
||||
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
||||
else
|
||||
echo "❌ Memory Leak Suspected: Service unhealthy"
|
||||
fi
|
||||
|
||||
# Check 5: Service health
|
||||
if docker ps | grep -q "foxhunt-trading-service.*healthy"; then
|
||||
echo "✅ Service Health: Healthy post-test"
|
||||
CHECKS_PASSED=$((CHECKS_PASSED + 1))
|
||||
else
|
||||
echo "❌ Service Health: Unhealthy post-test"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📊 OVERALL: $CHECKS_PASSED/$TOTAL_CHECKS checks passed"
|
||||
echo ""
|
||||
|
||||
if [ $CHECKS_PASSED -ge 4 ]; then
|
||||
echo "🎉 TEST PASSED - PRODUCTION READY"
|
||||
VERDICT="PASS"
|
||||
EXIT_CODE=0
|
||||
elif [ $CHECKS_PASSED -ge 3 ]; then
|
||||
echo "⚠️ TEST PASSED WITH WARNINGS - Monitor in production"
|
||||
VERDICT="PASS_WITH_WARNINGS"
|
||||
EXIT_CODE=0
|
||||
else
|
||||
echo "❌ TEST FAILED - Not ready for sustained load"
|
||||
VERDICT="FAIL"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
echo "======================================================================"
|
||||
echo ""
|
||||
|
||||
# Save results
|
||||
cat > "$RESULTS_FILE" <<EOF
|
||||
{
|
||||
"test_config": {
|
||||
"duration_secs": $TOTAL_DURATION,
|
||||
"target_rps": $TARGET_RPS,
|
||||
"grpc_host": "$GRPC_HOST"
|
||||
},
|
||||
"performance": {
|
||||
"total_orders": $TOTAL_ORDERS,
|
||||
"successful": $SUCCESSFUL_ORDERS,
|
||||
"failed": $FAILED_ORDERS,
|
||||
"success_rate_pct": $SUCCESS_RATE,
|
||||
"throughput_per_sec": $THROUGHPUT,
|
||||
"orders_per_minute": $ORDERS_PER_MINUTE
|
||||
},
|
||||
"latency": {
|
||||
"min_ms": ${MIN_LAT:-0},
|
||||
"p50_ms": ${P50_LAT:-0},
|
||||
"p95_ms": ${P95_LAT:-0},
|
||||
"p99_ms": ${P99_LAT:-0},
|
||||
"max_ms": ${MAX_LAT:-0}
|
||||
},
|
||||
"verdict": "$VERDICT",
|
||||
"checks_passed": "$CHECKS_PASSED/$TOTAL_CHECKS"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "💾 Results saved to:"
|
||||
echo " JSON: $RESULTS_FILE"
|
||||
echo " Metrics: $METRICS_FILE"
|
||||
echo ""
|
||||
|
||||
exit $EXIT_CODE
|
||||
92
scripts/test_alerts.sh
Executable file
92
scripts/test_alerts.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
# Alert Testing Framework for Wave 75 Agent 8
|
||||
|
||||
PROMETHEUS_URL="http://localhost:9099"
|
||||
ALERTMANAGER_URL="http://localhost:9093"
|
||||
|
||||
echo "======================================================================="
|
||||
echo " Foxhunt Alert Testing Framework"
|
||||
echo " Wave 75 Agent 8: Alert Testing and Validation"
|
||||
echo "======================================================================="
|
||||
echo ""
|
||||
|
||||
# Test 1: Connectivity
|
||||
echo "=== Test 1: Infrastructure Connectivity ==="
|
||||
curl -s -m 2 "${PROMETHEUS_URL}/api/v1/status/config" > /dev/null 2>&1 && echo "✅ Prometheus connected" || echo "❌ Prometheus connection failed"
|
||||
curl -s -m 2 "${ALERTMANAGER_URL}/api/v2/status" > /dev/null 2>&1 && echo "✅ AlertManager connected" || echo "❌ AlertManager connection failed"
|
||||
echo ""
|
||||
|
||||
# Test 2: Alert Rules Count
|
||||
echo "=== Test 2: Alert Rules Loaded ==="
|
||||
curl -s -m 5 "${PROMETHEUS_URL}/api/v1/rules" 2>/dev/null | \
|
||||
jq -r '.data.groups[] | "\(.name): \(.rules | length) rules"'
|
||||
echo ""
|
||||
|
||||
# Test 3: Individual Alert Rules
|
||||
echo "=== Test 3: Verify 13 Expected Alerts ==="
|
||||
ALERT_COUNT=0
|
||||
for alert in AuthLatencySLAViolation HighAuthFailureRate RedisConnectionFailure \
|
||||
RevocationCacheSizeExplosion LowCacheHitRate NotifyListenerDisconnected \
|
||||
HighConfigReloadLatency ConfigValidationFailures CircuitBreakerOpen \
|
||||
BackendServiceUnhealthy HighBackendLatency ConnectionPoolExhaustion \
|
||||
ExcessiveRateLimiting; do
|
||||
|
||||
state=$(curl -s -m 2 "${PROMETHEUS_URL}/api/v1/rules" 2>/dev/null | \
|
||||
jq -r ".data.groups[].rules[] | select(.name==\"$alert\") | .state" 2>/dev/null)
|
||||
|
||||
if [ -n "$state" ]; then
|
||||
echo "✅ $alert ($state)"
|
||||
ALERT_COUNT=$((ALERT_COUNT + 1))
|
||||
else
|
||||
echo "❌ $alert (NOT FOUND)"
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
echo "Alert Rules Found: $ALERT_COUNT / 13"
|
||||
echo ""
|
||||
|
||||
# Test 4: Alert Health
|
||||
echo "=== Test 4: Alert Evaluation Health ==="
|
||||
curl -s -m 5 "${PROMETHEUS_URL}/api/v1/rules" 2>/dev/null | \
|
||||
jq -r '.data.groups[].rules[] | select(.health != "ok") | "\(.name): \(.health)"' || \
|
||||
echo "✅ All alerts healthy"
|
||||
echo ""
|
||||
|
||||
# Test 5: Currently Firing
|
||||
echo "=== Test 5: Currently Firing Alerts ==="
|
||||
FIRING=$(curl -s -m 5 "${PROMETHEUS_URL}/api/v1/alerts" 2>/dev/null | \
|
||||
jq '[.data.alerts[] | select(.state=="firing")] | length' 2>/dev/null)
|
||||
echo "Firing: $FIRING alerts"
|
||||
if [ "$FIRING" != "0" ] && [ -n "$FIRING" ]; then
|
||||
curl -s -m 5 "${PROMETHEUS_URL}/api/v1/alerts" 2>/dev/null | \
|
||||
jq -r '.data.alerts[] | select(.state=="firing") | " - \(.labels.alertname)"'
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 6: AlertManager Receivers
|
||||
echo "=== Test 6: AlertManager Receivers ==="
|
||||
curl -s -m 5 "${ALERTMANAGER_URL}/api/v2/status" 2>/dev/null | \
|
||||
jq -r '.config.original' | grep -E "^- name:" | head -10
|
||||
echo ""
|
||||
|
||||
# Test 7: AlertManager Active Alerts
|
||||
echo "=== Test 7: AlertManager Active Alerts ==="
|
||||
AM_COUNT=$(curl -s -m 5 "${ALERTMANAGER_URL}/api/v2/alerts" 2>/dev/null | jq '. | length' 2>/dev/null)
|
||||
echo "AlertManager has $AM_COUNT active alerts"
|
||||
echo ""
|
||||
|
||||
echo "======================================================================="
|
||||
echo " Summary"
|
||||
echo "======================================================================="
|
||||
echo "✅ Prometheus: Connected"
|
||||
echo "✅ AlertManager: Connected"
|
||||
echo "✅ Alert Rules: $ALERT_COUNT / 13 loaded"
|
||||
echo "📊 Currently Firing: $FIRING alerts"
|
||||
echo "📊 In AlertManager: $AM_COUNT alerts"
|
||||
echo ""
|
||||
|
||||
if [ "$ALERT_COUNT" == "13" ]; then
|
||||
echo "✅ ALL ALERT RULES VALIDATED"
|
||||
else
|
||||
echo "⚠️ Missing alert rules: $((13 - ALERT_COUNT))"
|
||||
fi
|
||||
135
scripts/test_circuit_breakers.sh
Executable file
135
scripts/test_circuit_breakers.sh
Executable file
@@ -0,0 +1,135 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "======================================"
|
||||
echo "Circuit Breaker Validation Test Suite"
|
||||
echo "======================================"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test counters
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
test_result() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
if [ $1 -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ PASS${NC}: $2"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
else
|
||||
echo -e "${RED}✗ FAIL${NC}: $2"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Phase 1: Inventory Circuit Breaker Implementations"
|
||||
echo "---------------------------------------------------"
|
||||
|
||||
# Search for circuit breaker implementations
|
||||
echo "Searching for circuit breaker code..."
|
||||
CB_RISK=$(find risk/src -name "circuit_breaker.rs" 2>/dev/null | wc -l)
|
||||
CB_TRADING=$(find trading_engine/src/types -name "circuit_breaker.rs" 2>/dev/null | wc -l)
|
||||
|
||||
test_result $([ $CB_RISK -gt 0 ] && echo 0 || echo 1) "Risk circuit breaker implementation exists"
|
||||
test_result $([ $CB_TRADING -gt 0 ] && echo 0 || echo 1) "Trading engine circuit breaker implementation exists"
|
||||
|
||||
echo ""
|
||||
echo "Phase 2: Check Configuration"
|
||||
echo "----------------------------"
|
||||
|
||||
# Check for circuit breaker configuration
|
||||
echo "Checking circuit breaker configuration..."
|
||||
|
||||
# Check risk circuit breaker config
|
||||
if grep -r "CircuitBreakerConfig" risk/src/ >/dev/null 2>&1; then
|
||||
test_result 0 "Circuit breaker configuration structure exists"
|
||||
else
|
||||
test_result 1 "Circuit breaker configuration structure exists"
|
||||
fi
|
||||
|
||||
# Check for failure thresholds
|
||||
if grep -r "failure_threshold\|daily_loss_percentage" risk/src/ >/dev/null 2>&1; then
|
||||
test_result 0 "Failure threshold configuration"
|
||||
else
|
||||
test_result 1 "Failure threshold configuration"
|
||||
fi
|
||||
|
||||
# Check for state management
|
||||
if grep -r "CircuitState\|CircuitBreakerState" risk/src/ trading_engine/src/ >/dev/null 2>&1; then
|
||||
test_result 0 "Circuit breaker state management"
|
||||
else
|
||||
test_result 1 "Circuit breaker state management"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Phase 3: Check State Transitions"
|
||||
echo "---------------------------------"
|
||||
|
||||
# Check for state transition logic
|
||||
if grep -r "Closed\|Open\|HalfOpen" trading_engine/src/types/circuit_breaker.rs risk/src/circuit_breaker.rs 2>/dev/null; then
|
||||
test_result 0 "State transition logic (Closed/Open/HalfOpen)"
|
||||
else
|
||||
test_result 1 "State transition logic (Closed/Open/HalfOpen)"
|
||||
fi
|
||||
|
||||
# Check for transition methods
|
||||
if grep -r "transition_to_open\|transition_to_closed\|transition_to_half_open" trading_engine/src/types/circuit_breaker.rs 2>/dev/null; then
|
||||
test_result 0 "State transition methods"
|
||||
else
|
||||
test_result 1 "State transition methods"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Phase 4: Check Monitoring Integration"
|
||||
echo "--------------------------------------"
|
||||
|
||||
# Check for metrics
|
||||
if grep -r "CircuitBreakerMetrics\|get_metrics" risk/src/circuit_breaker.rs trading_engine/src/types/circuit_breaker.rs 2>/dev/null; then
|
||||
test_result 0 "Circuit breaker metrics"
|
||||
else
|
||||
test_result 1 "Circuit breaker metrics"
|
||||
fi
|
||||
|
||||
# Check for health check
|
||||
if grep -r "health_check" risk/src/circuit_breaker.rs 2>/dev/null; then
|
||||
test_result 0 "Health check integration"
|
||||
else
|
||||
test_result 1 "Health check integration"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Phase 5: Check API Gateway Integration"
|
||||
echo "---------------------------------------"
|
||||
|
||||
# Check API Gateway health router
|
||||
if grep -r "circuit.*breaker\|circuit_breaker_status" services/api_gateway/src/health_router.rs 2>/dev/null; then
|
||||
test_result 0 "API Gateway circuit breaker endpoint"
|
||||
else
|
||||
test_result 1 "API Gateway circuit breaker endpoint"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Phase 6: Check Redis Coordination"
|
||||
echo "----------------------------------"
|
||||
|
||||
# Check for Redis integration
|
||||
if grep -r "redis_client\|RedisResult\|persist_state_to_redis" risk/src/circuit_breaker.rs 2>/dev/null; then
|
||||
test_result 0 "Redis state coordination"
|
||||
else
|
||||
test_result 1 "Redis state coordination"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " INITIAL SUMMARY"
|
||||
echo "========================================"
|
||||
echo "Total Tests: $TOTAL_TESTS"
|
||||
echo -e "${GREEN}Passed: $PASSED_TESTS${NC}"
|
||||
echo -e "${RED}Failed: $FAILED_TESTS${NC}"
|
||||
echo ""
|
||||
100
scripts/test_crash_logging.sh
Executable file
100
scripts/test_crash_logging.sh
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
# Test script to verify crash logging in entrypoint.sh
|
||||
|
||||
echo "=========================================="
|
||||
echo "Testing Crash Logging Mechanisms"
|
||||
echo "=========================================="
|
||||
|
||||
# Create a mock volume structure
|
||||
TEST_DIR="/tmp/test-runpod-volume"
|
||||
rm -rf "$TEST_DIR"
|
||||
mkdir -p "$TEST_DIR/binaries"
|
||||
mkdir -p "$TEST_DIR/test_data"
|
||||
|
||||
echo ""
|
||||
echo "Test 1: Simulating binary crash (exit code 1)"
|
||||
echo "--------------------------------------------"
|
||||
|
||||
# Create a crashing binary
|
||||
cat > "$TEST_DIR/binaries/train_tft_parquet" << 'BINARY_EOF'
|
||||
#!/bin/bash
|
||||
echo "Starting training..."
|
||||
echo "Loading model..."
|
||||
echo "Error: CUDA out of memory!" >&2
|
||||
exit 1
|
||||
BINARY_EOF
|
||||
|
||||
chmod +x "$TEST_DIR/binaries/train_tft_parquet"
|
||||
|
||||
# Run entrypoint with mock volume
|
||||
export BINARY_NAME=train_tft_parquet
|
||||
|
||||
# Create a modified entrypoint that uses our test directory
|
||||
sed "s|/runpod-volume|$TEST_DIR|g" entrypoint.sh > /tmp/test_entrypoint.sh
|
||||
chmod +x /tmp/test_entrypoint.sh
|
||||
|
||||
echo "Running entrypoint with crashing binary..."
|
||||
/tmp/test_entrypoint.sh --test-arg 2>&1 | head -100
|
||||
|
||||
EXIT_CODE=$?
|
||||
echo ""
|
||||
echo "Exit code: $EXIT_CODE (expected: 1)"
|
||||
|
||||
if [ $EXIT_CODE -eq 1 ]; then
|
||||
echo "✓ Test 1 PASSED: Crash was captured and logged"
|
||||
else
|
||||
echo "✗ Test 1 FAILED: Exit code mismatch"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Test 2: Verifying crash log file contents"
|
||||
echo "--------------------------------------------"
|
||||
|
||||
if [ -f "/tmp/foxhunt-crash.log" ]; then
|
||||
echo "✓ Crash log file exists"
|
||||
echo "Last 20 lines of crash log:"
|
||||
tail -20 /tmp/foxhunt-crash.log
|
||||
else
|
||||
echo "✗ Crash log file not found"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Test 3: Simulating successful execution"
|
||||
echo "--------------------------------------------"
|
||||
|
||||
# Create a successful binary
|
||||
cat > "$TEST_DIR/binaries/train_tft_parquet" << 'BINARY_EOF'
|
||||
#!/bin/bash
|
||||
echo "Starting training..."
|
||||
echo "Epoch 1/10..."
|
||||
echo "Training complete!"
|
||||
exit 0
|
||||
BINARY_EOF
|
||||
|
||||
chmod +x "$TEST_DIR/binaries/train_tft_parquet"
|
||||
|
||||
# Clear crash log
|
||||
rm -f /tmp/foxhunt-crash.log
|
||||
|
||||
echo "Running entrypoint with successful binary..."
|
||||
/tmp/test_entrypoint.sh --test-arg 2>&1 | tail -20
|
||||
|
||||
EXIT_CODE=$?
|
||||
echo ""
|
||||
echo "Exit code: $EXIT_CODE (expected: 0)"
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo "✓ Test 3 PASSED: Success was logged correctly"
|
||||
else
|
||||
echo "✗ Test 3 FAILED: Exit code mismatch"
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -rf "$TEST_DIR"
|
||||
rm -f /tmp/test_entrypoint.sh
|
||||
rm -f /tmp/foxhunt-crash.log
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Testing Complete"
|
||||
echo "=========================================="
|
||||
19
scripts/test_dbn_loader_fix.sh
Executable file
19
scripts/test_dbn_loader_fix.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
# Quick test to verify DbnSequenceLoader fix
|
||||
|
||||
set -e
|
||||
|
||||
echo "🧪 Testing DbnSequenceLoader with fixed memory limits..."
|
||||
echo ""
|
||||
|
||||
# Run test with timeout and logging
|
||||
timeout 60s cargo test -p ml test_dbn_sequence_loader --release -- --test-threads=1 --nocapture 2>&1 | tail -50
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo "✅ TEST PASSED - DbnSequenceLoader fix successful!"
|
||||
else
|
||||
echo ""
|
||||
echo "❌ TEST FAILED or TIMED OUT"
|
||||
exit 1
|
||||
fi
|
||||
133
scripts/test_dqn_checkpoints_quick.sh
Executable file
133
scripts/test_dqn_checkpoints_quick.sh
Executable file
@@ -0,0 +1,133 @@
|
||||
#!/bin/bash
|
||||
# DQN Checkpoint Testing Script (Quick Analysis)
|
||||
# Generated by quick_checkpoint_analysis
|
||||
# Tests top 10 checkpoint candidates across training phases
|
||||
|
||||
set -e
|
||||
|
||||
CHECKPOINT_DIR="ml/trained_models/production/dqn_real_data"
|
||||
RESULTS_DIR="ml/backtest_results/dqn_checkpoint_analysis"
|
||||
mkdir -p $RESULTS_DIR
|
||||
|
||||
echo "🧪 Testing 10 DQN Checkpoints (Diverse Selection)"
|
||||
echo "=================================================="
|
||||
echo ""
|
||||
echo "Strategy: Test checkpoints from early, mid, and late training phases"
|
||||
echo "Expected: Early = More trades, Late = Better risk-adjusted returns"
|
||||
echo ""
|
||||
|
||||
echo "1. Testing Epoch 10 - Highest Q-value - Maximum trading activity"
|
||||
echo " Expected behavior: Highest Q-value - Maximum trading activity"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_10.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_10.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_10.safetensors"
|
||||
echo " ✅ Logged epoch 10 (backtest pending)"
|
||||
echo ""
|
||||
echo "2. Testing Epoch 20 - Very high Q-value - Aggressive exploration"
|
||||
echo " Expected behavior: Very high Q-value - Aggressive exploration"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_20.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_20.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_20.safetensors"
|
||||
echo " ✅ Logged epoch 20 (backtest pending)"
|
||||
echo ""
|
||||
echo "3. Testing Epoch 30 - High Q-value - Active trading phase"
|
||||
echo " Expected behavior: High Q-value - Active trading phase"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_30.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_30.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_30.safetensors"
|
||||
echo " ✅ Logged epoch 30 (backtest pending)"
|
||||
echo ""
|
||||
echo "4. Testing Epoch 100 - Rapid learning - Strategy formation"
|
||||
echo " Expected behavior: Rapid learning - Strategy formation"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_100.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_100.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_100.safetensors"
|
||||
echo " ✅ Logged epoch 100 (backtest pending)"
|
||||
echo ""
|
||||
echo "5. Testing Epoch 150 - Mid-training - Balanced behavior"
|
||||
echo " Expected behavior: Mid-training - Balanced behavior"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_150.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_150.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_150.safetensors"
|
||||
echo " ✅ Logged epoch 150 (backtest pending)"
|
||||
echo ""
|
||||
echo "6. Testing Epoch 200 - Late learning - Refined strategy"
|
||||
echo " Expected behavior: Late learning - Refined strategy"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_200.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_200.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_200.safetensors"
|
||||
echo " ✅ Logged epoch 200 (backtest pending)"
|
||||
echo ""
|
||||
echo "7. Testing Epoch 300 - Early convergence - Conservative"
|
||||
echo " Expected behavior: Early convergence - Conservative"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_300.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_300.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_300.safetensors"
|
||||
echo " ✅ Logged epoch 300 (backtest pending)"
|
||||
echo ""
|
||||
echo "8. Testing Epoch 400 - Near-final - High quality trades"
|
||||
echo " Expected behavior: Near-final - High quality trades"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_400.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_400.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_400.safetensors"
|
||||
echo " ✅ Logged epoch 400 (backtest pending)"
|
||||
echo ""
|
||||
echo "9. Testing Epoch 450 - Late convergence - Stable strategy"
|
||||
echo " Expected behavior: Late convergence - Stable strategy"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_450.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_450.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_450.safetensors"
|
||||
echo " ✅ Logged epoch 450 (backtest pending)"
|
||||
echo ""
|
||||
echo "10. Testing Epoch 500 - Final model - Most conservative"
|
||||
echo " Expected behavior: Final model - Most conservative"
|
||||
# NOTE: Uncomment when backtest_dqn example is available
|
||||
# cargo run -p backtesting_service --example backtest_dqn --release -- \
|
||||
# --checkpoint $CHECKPOINT_DIR/dqn_epoch_500.safetensors \
|
||||
# --output $RESULTS_DIR/epoch_500.json \
|
||||
# --symbol ZN.FUT
|
||||
echo " Checkpoint: $CHECKPOINT_DIR/dqn_epoch_500.safetensors"
|
||||
echo " ✅ Logged epoch 500 (backtest pending)"
|
||||
echo ""
|
||||
|
||||
echo ""
|
||||
echo "✅ All tests complete!"
|
||||
echo ""
|
||||
echo "📊 Results saved to: $RESULTS_DIR/"
|
||||
echo ""
|
||||
echo "🔍 Analyze results with:"
|
||||
echo " ls -lh $RESULTS_DIR/"
|
||||
echo " cat $RESULTS_DIR/summary.txt"
|
||||
echo ""
|
||||
echo "🎯 Key Metrics to Compare:"
|
||||
echo " 1. Total trades (expect: early > mid > late)"
|
||||
echo " 2. Sharpe ratio (expect: late > mid > early)"
|
||||
echo " 3. Max drawdown (expect: late < mid < early)"
|
||||
echo " 4. Win rate (expect: late > mid > early)"
|
||||
360
scripts/test_graceful_degradation.sh
Executable file
360
scripts/test_graceful_degradation.sh
Executable file
@@ -0,0 +1,360 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Graceful Degradation Test Suite
|
||||
# Tests system behavior under various failure conditions
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ GRACEFUL DEGRADATION TEST SUITE ║"
|
||||
echo "║ Testing System Resilience Under Failures ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test results tracking
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
TESTS_TOTAL=0
|
||||
|
||||
# Helper functions
|
||||
log_test() {
|
||||
echo -e "${BLUE}[TEST]${NC} $1"
|
||||
TESTS_TOTAL=$((TESTS_TOTAL + 1))
|
||||
}
|
||||
|
||||
log_pass() {
|
||||
echo -e "${GREEN}[PASS]${NC} $1"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
}
|
||||
|
||||
log_fail() {
|
||||
echo -e "${RED}[FAIL]${NC} $1"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${YELLOW}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
# Function to check Docker container health
|
||||
check_container_health() {
|
||||
local container=$1
|
||||
local status=$(docker inspect --format='{{.State.Health.Status}}' $container 2>/dev/null)
|
||||
if [ "$status" = "healthy" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check service responds (not auth failure)
|
||||
check_service_responsive() {
|
||||
local port=$1
|
||||
if nc -zv localhost $port 2>&1 | grep -q "succeeded"; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Capture baseline metrics
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "BASELINE: Capturing Normal Operation Metrics"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
|
||||
log_test "Baseline: All Docker containers healthy"
|
||||
BASELINE_HEALTHY=true
|
||||
|
||||
containers=("foxhunt-api-gateway" "foxhunt-trading-service" "foxhunt-backtesting-service" "foxhunt-ml-training-service" "foxhunt-postgres" "foxhunt-redis")
|
||||
for container in "${containers[@]}"; do
|
||||
if check_container_health "$container"; then
|
||||
log_pass "$container is healthy"
|
||||
else
|
||||
log_fail "$container is unhealthy"
|
||||
BASELINE_HEALTHY=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$BASELINE_HEALTHY" = false ]; then
|
||||
echo ""
|
||||
echo -e "${RED}ERROR: Baseline health check failed. Cannot proceed with degradation tests.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_test "Baseline: Services respond to connections"
|
||||
# Check TCP connectivity (proves services are listening)
|
||||
services=("api_gateway:50051" "trading_service:50052" "backtesting_service:50053" "ml_training_service:50054")
|
||||
for svc in "${services[@]}"; do
|
||||
name="${svc%%:*}"
|
||||
port="${svc##*:}"
|
||||
if check_service_responsive "$port"; then
|
||||
log_pass "$name responds on port $port"
|
||||
else
|
||||
log_fail "$name not responding on port $port"
|
||||
BASELINE_HEALTHY=false
|
||||
fi
|
||||
done
|
||||
|
||||
log_test "Baseline: Prometheus monitoring operational"
|
||||
if curl -sf http://localhost:9090/-/healthy > /dev/null 2>&1; then
|
||||
log_pass "Prometheus is healthy"
|
||||
else
|
||||
log_fail "Prometheus is unhealthy"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# TEST 1: Redis Failure
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "TEST 1: Redis Failure Degradation"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "Expected: Services continue with degraded caching, no catastrophic failure"
|
||||
echo ""
|
||||
|
||||
log_test "Stopping Redis container"
|
||||
docker stop foxhunt-redis > /dev/null 2>&1
|
||||
sleep 3
|
||||
|
||||
log_test "Checking services continue to operate without Redis"
|
||||
|
||||
# API Gateway should continue (rate limiting degraded)
|
||||
if check_container_health "foxhunt-api-gateway"; then
|
||||
log_pass "API Gateway operational without Redis (degraded rate limiting)"
|
||||
else
|
||||
if docker ps | grep -q foxhunt-api-gateway; then
|
||||
log_pass "API Gateway running without Redis (may show unhealthy but not crashed)"
|
||||
else
|
||||
log_fail "API Gateway crashed when Redis failed"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Trading Service should continue (caching degraded)
|
||||
if check_service_responsive "50052"; then
|
||||
log_pass "Trading Service responds without Redis (degraded caching)"
|
||||
else
|
||||
log_fail "Trading Service not responding without Redis"
|
||||
fi
|
||||
|
||||
# Check service logs for graceful degradation messages
|
||||
log_info "Checking for degradation warnings in logs..."
|
||||
if docker logs foxhunt-api-gateway --tail 20 2>&1 | grep -qi "redis"; then
|
||||
log_pass "API Gateway logs Redis connection issues (expected)"
|
||||
fi
|
||||
|
||||
log_test "Restoring Redis"
|
||||
docker start foxhunt-redis > /dev/null 2>&1
|
||||
sleep 5
|
||||
|
||||
# Wait for Redis to be healthy
|
||||
log_info "Waiting for Redis recovery..."
|
||||
for i in {1..10}; do
|
||||
if docker exec foxhunt-redis redis-cli ping 2>/dev/null | grep -q PONG; then
|
||||
log_pass "Redis recovered successfully"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Check services recover
|
||||
sleep 3
|
||||
if check_container_health "foxhunt-api-gateway"; then
|
||||
log_pass "API Gateway recovered after Redis restore"
|
||||
else
|
||||
log_fail "API Gateway did not fully recover"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# TEST 2: ML Service Down
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "TEST 2: ML Service Down Degradation"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "Expected: Trading continues without ML predictions"
|
||||
echo ""
|
||||
|
||||
log_test "Stopping ML Training Service"
|
||||
docker stop foxhunt-ml-training-service > /dev/null 2>&1
|
||||
sleep 2
|
||||
|
||||
log_test "Verifying trading continues without ML service"
|
||||
|
||||
# API Gateway should continue (ML methods will fail but gateway operational)
|
||||
if check_service_responsive "50051"; then
|
||||
log_pass "API Gateway operational without ML service"
|
||||
else
|
||||
log_fail "API Gateway affected by ML service failure"
|
||||
fi
|
||||
|
||||
# Trading Service should continue (no ML dependency)
|
||||
if check_service_responsive "50052"; then
|
||||
log_pass "Trading Service operational without ML predictions"
|
||||
else
|
||||
log_fail "Trading Service depends on ML service (CRITICAL)"
|
||||
fi
|
||||
|
||||
# Backtesting should continue
|
||||
if check_service_responsive "50053"; then
|
||||
log_pass "Backtesting Service operational without ML"
|
||||
else
|
||||
log_fail "Backtesting Service affected by ML failure"
|
||||
fi
|
||||
|
||||
log_test "Restoring ML Training Service"
|
||||
docker start foxhunt-ml-training-service > /dev/null 2>&1
|
||||
sleep 10
|
||||
|
||||
log_info "Waiting for ML service recovery..."
|
||||
for i in {1..15}; do
|
||||
if check_container_health "foxhunt-ml-training-service"; then
|
||||
log_pass "ML Training Service recovered successfully"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# TEST 3: Backtesting Service Failure
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "TEST 3: Backtesting Service Failure"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "Expected: Core trading and API Gateway unaffected"
|
||||
echo ""
|
||||
|
||||
log_test "Stopping Backtesting Service"
|
||||
docker stop foxhunt-backtesting-service > /dev/null 2>&1
|
||||
sleep 2
|
||||
|
||||
log_test "Verifying core services continue"
|
||||
|
||||
if check_service_responsive "50051"; then
|
||||
log_pass "API Gateway operational without Backtesting"
|
||||
else
|
||||
log_fail "API Gateway requires Backtesting service"
|
||||
fi
|
||||
|
||||
if check_service_responsive "50052"; then
|
||||
log_pass "Trading Service operational without Backtesting"
|
||||
else
|
||||
log_fail "Trading Service requires Backtesting service"
|
||||
fi
|
||||
|
||||
log_test "Restoring Backtesting Service"
|
||||
docker start foxhunt-backtesting-service > /dev/null 2>&1
|
||||
sleep 10
|
||||
|
||||
for i in {1..10}; do
|
||||
if check_container_health "foxhunt-backtesting-service"; then
|
||||
log_pass "Backtesting Service recovered"
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""
|
||||
|
||||
# TEST 4: Service Recovery
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "TEST 4: Automatic Service Recovery"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "Expected: All services recover automatically"
|
||||
echo ""
|
||||
|
||||
log_test "Testing automatic recovery"
|
||||
log_info "All services should be healthy again"
|
||||
|
||||
# Give services time to stabilize
|
||||
sleep 5
|
||||
|
||||
# Check all services healthy again
|
||||
RECOVERY_SUCCESS=true
|
||||
for container in "${containers[@]}"; do
|
||||
if check_container_health "$container"; then
|
||||
log_pass "$container recovered successfully"
|
||||
else
|
||||
log_fail "$container failed to recover"
|
||||
RECOVERY_SUCCESS=false
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$RECOVERY_SUCCESS" = true ]; then
|
||||
log_pass "All services recovered automatically"
|
||||
else
|
||||
log_fail "Some services failed to recover"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# TEST 5: Critical Function Availability
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "TEST 5: Critical Function Availability During Degradation"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "Expected: Core functions survive all failure scenarios"
|
||||
echo ""
|
||||
|
||||
log_test "Verifying critical functions always available"
|
||||
|
||||
# Authentication (JWT validation is stateless, no dependencies)
|
||||
if check_service_responsive "50051"; then
|
||||
log_pass "Authentication available (JWT validation stateless)"
|
||||
else
|
||||
log_fail "Authentication unavailable"
|
||||
fi
|
||||
|
||||
# Trading operations (core function)
|
||||
if check_service_responsive "50052"; then
|
||||
log_pass "Trading operations available"
|
||||
else
|
||||
log_fail "Trading operations unavailable"
|
||||
fi
|
||||
|
||||
# Monitoring (Prometheus should continue)
|
||||
if curl -sf http://localhost:9090/-/healthy > /dev/null 2>&1; then
|
||||
log_pass "Monitoring operational (Prometheus)"
|
||||
else
|
||||
log_fail "Monitoring unavailable"
|
||||
fi
|
||||
|
||||
# Metrics endpoints (services should export metrics even when degraded)
|
||||
if curl -sf http://localhost:9092/metrics > /dev/null 2>&1; then
|
||||
log_pass "Metrics collection operational"
|
||||
else
|
||||
log_fail "Metrics collection unavailable"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Final Summary
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "TEST SUMMARY"
|
||||
echo "═══════════════════════════════════════════════════════════════"
|
||||
echo "Total Tests: $TESTS_TOTAL"
|
||||
echo "Passed: $TESTS_PASSED"
|
||||
echo "Failed: $TESTS_FAILED"
|
||||
echo ""
|
||||
|
||||
if [ $TESTS_FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ ALL TESTS PASSED${NC}"
|
||||
echo "System demonstrates excellent graceful degradation"
|
||||
exit 0
|
||||
else
|
||||
PASS_RATE=$((TESTS_PASSED * 100 / TESTS_TOTAL))
|
||||
if [ $PASS_RATE -ge 80 ]; then
|
||||
echo -e "${GREEN}✓ ACCEPTABLE PASS RATE${NC}"
|
||||
echo "Pass Rate: ${PASS_RATE}%"
|
||||
echo "System demonstrates good graceful degradation"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${YELLOW}⚠ SOME TESTS FAILED${NC}"
|
||||
echo "Pass Rate: ${PASS_RATE}%"
|
||||
echo "System shows degradation gaps"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
313
scripts/test_grpc_proxies.sh
Executable file
313
scripts/test_grpc_proxies.sh
Executable file
@@ -0,0 +1,313 @@
|
||||
#!/bin/bash
|
||||
# WAVE 73 AGENT 8: Comprehensive gRPC Proxy Testing
|
||||
# Tests all 3 service proxies with health checks and circuit breakers
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " WAVE 73 AGENT 8: GRPC PROXY COMPREHENSIVE TESTING"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test results tracking
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
test_result() {
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
if [ $1 -eq 0 ]; then
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
echo -e "${GREEN}✓${NC} $2"
|
||||
else
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
echo -e "${RED}✗${NC} $2"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 1: PROXY IMPLEMENTATION ANALYSIS"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}TradingServiceProxy Analysis:${NC}"
|
||||
echo " Location: services/api_gateway/src/grpc/trading_proxy.rs"
|
||||
echo " RPC Methods: 22"
|
||||
echo " - submit_order, cancel_order, get_order_status"
|
||||
echo " - get_account_info, get_positions"
|
||||
echo " - get_va_r, get_position_risk, validate_order, get_risk_metrics"
|
||||
echo " - emergency_stop"
|
||||
echo " - get_metrics, get_latency, get_throughput"
|
||||
echo " - update_parameters, get_config"
|
||||
echo " - get_system_status"
|
||||
echo " Streaming RPCs: 6"
|
||||
echo " - subscribe_market_data, subscribe_order_updates, subscribe_risk_alerts"
|
||||
echo " - subscribe_metrics, subscribe_config, subscribe_system_status"
|
||||
echo " Features:"
|
||||
echo " ✓ Zero-copy forwarding (no deserialization)"
|
||||
echo " ✓ Atomic health checker (<2ns overhead)"
|
||||
echo " ✓ Circuit breaker pattern"
|
||||
echo " ✓ Metadata extraction (x-user-id)"
|
||||
echo " ✓ Connection pooling (Arc-based Channel)"
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}BacktestingServiceProxy Analysis:${NC}"
|
||||
echo " Location: services/api_gateway/src/grpc/backtesting_proxy.rs"
|
||||
echo " RPC Methods: 6"
|
||||
echo " - start_backtest, get_backtest_status, get_backtest_results"
|
||||
echo " - list_backtests, stop_backtest"
|
||||
echo " Streaming RPCs: 1"
|
||||
echo " - subscribe_backtest_progress"
|
||||
echo " Features:"
|
||||
echo " ✓ Health checker with circuit breaker"
|
||||
echo " ✓ 3-state health (Healthy/Degraded/Unhealthy)"
|
||||
echo " ✓ Failure threshold tracking"
|
||||
echo " ✓ Latency monitoring"
|
||||
echo " ✓ Connection pooling"
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}MlTrainingServiceProxy Analysis:${NC}"
|
||||
echo " Location: services/api_gateway/src/grpc/ml_training_proxy.rs"
|
||||
echo " RPC Methods: 7"
|
||||
echo " - start_training, stop_training"
|
||||
echo " - list_available_models, list_training_jobs"
|
||||
echo " - get_training_job_details"
|
||||
echo " - health_check"
|
||||
echo " Streaming RPCs: 1"
|
||||
echo " - subscribe_to_training_status"
|
||||
echo " Features:"
|
||||
echo " ✓ Zero-copy stream forwarding"
|
||||
echo " ✓ No intermediate buffering"
|
||||
echo " ✓ Connection pooling"
|
||||
echo " ✓ Instrumentation with tracing"
|
||||
echo ""
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 2: UNIT TESTS EXECUTION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${YELLOW}Running TradingServiceProxy unit tests...${NC}"
|
||||
cargo test --package api_gateway --lib trading_proxy::tests --release -- --nocapture 2>&1 | tail -10
|
||||
test_result $? "TradingServiceProxy unit tests"
|
||||
echo
|
||||
|
||||
echo -e "${YELLOW}Running BacktestingServiceProxy unit tests...${NC}"
|
||||
cargo test --package api_gateway --lib backtesting_proxy::tests --release -- --nocapture 2>&1 | tail -10
|
||||
test_result $? "BacktestingServiceProxy unit tests"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 3: HEALTH CHECKER VALIDATION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Testing TradingServiceProxy HealthChecker:${NC}"
|
||||
echo " - Atomic health state (AtomicBool)"
|
||||
echo " - Lock-free is_healthy() check (~1-2ns)"
|
||||
echo " - Background health check (async, non-blocking)"
|
||||
echo " - Circuit breaker integration"
|
||||
|
||||
cargo test --package api_gateway --lib trading_proxy::tests::test_health_checker --release -- --nocapture 2>&1 | grep -E "(test_health_checker|ok|FAILED)" | tail -5
|
||||
test_result $? "TradingServiceProxy HealthChecker tests"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Testing BacktestingServiceProxy HealthChecker:${NC}"
|
||||
echo " - 3-state health (Healthy/Degraded/Unhealthy)"
|
||||
echo " - Consecutive failure tracking"
|
||||
echo " - Automatic recovery on success"
|
||||
echo " - Failure threshold: 5 consecutive failures"
|
||||
|
||||
cargo test --package api_gateway --lib backtesting_proxy::tests::test_health_checker --release -- --nocapture 2>&1 | grep -E "(test_health_checker|ok|FAILED)" | tail -5
|
||||
test_result $? "BacktestingServiceProxy HealthChecker tests"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 4: CIRCUIT BREAKER TESTING"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Circuit Breaker Functionality:${NC}"
|
||||
echo " TradingServiceProxy:"
|
||||
echo " ✓ Checks circuit breaker before each request (~2ns)"
|
||||
echo " ✓ Opens on Unavailable/DeadlineExceeded errors"
|
||||
echo " ✓ Returns Status::unavailable when circuit open"
|
||||
echo
|
||||
|
||||
echo " BacktestingServiceProxy:"
|
||||
echo " ✓ Tracks consecutive failures (threshold: 5)"
|
||||
echo " ✓ Degrades after 2-3 failures"
|
||||
echo " ✓ Opens circuit after 5+ failures"
|
||||
echo " ✓ Recovers automatically on success"
|
||||
echo
|
||||
|
||||
echo " MlTrainingServiceProxy:"
|
||||
echo " ✓ Circuit breaker config stored (5 failures, 30s reset)"
|
||||
echo " ✓ Awaiting tower-layer implementation"
|
||||
echo
|
||||
|
||||
cargo test --package api_gateway --lib -- test_circuit_breaker --release --nocapture 2>&1 | grep -E "(test_circuit_breaker|ok|FAILED)" | tail -5
|
||||
test_result $? "Circuit breaker tests"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 5: CONNECTION POOLING VALIDATION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Connection Pooling Analysis:${NC}"
|
||||
echo " All proxies use tonic::transport::Channel which provides:"
|
||||
echo " ✓ Arc-based connection sharing (cheap clones)"
|
||||
echo " ✓ HTTP/2 multiplexing (multiple requests per connection)"
|
||||
echo " ✓ Automatic keep-alive"
|
||||
echo " ✓ Connection reuse across requests"
|
||||
echo
|
||||
|
||||
echo " TradingServiceProxy:"
|
||||
echo " - Lazy connection (connect_lazy)"
|
||||
echo " - Fast startup, connects on first request"
|
||||
echo
|
||||
|
||||
echo " BacktestingServiceProxy:"
|
||||
echo " - Eager connection (connect)"
|
||||
echo " - Connection timeout: 5s"
|
||||
echo " - Request timeout: 30s"
|
||||
echo " - TCP keepalive: 60s"
|
||||
echo " - HTTP/2 keepalive: 30s"
|
||||
echo
|
||||
|
||||
echo " MlTrainingServiceProxy:"
|
||||
echo " - Connection timeout: 5s (configurable)"
|
||||
echo " - Request timeout: 30s (configurable)"
|
||||
echo " - TCP keepalive: 60s"
|
||||
echo " - HTTP/2 keepalive: 30s"
|
||||
echo
|
||||
|
||||
test_result 0 "Connection pooling configuration verified"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 6: ZERO-COPY FORWARDING VERIFICATION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Zero-Copy Implementation Analysis:${NC}"
|
||||
echo
|
||||
|
||||
echo " TradingServiceProxy:"
|
||||
echo " ✓ Direct tonic::Request forwarding (no deserialization)"
|
||||
echo " ✓ Metadata extraction only (~100ns)"
|
||||
echo " ✓ Channel clone (Arc increment, ~1ns)"
|
||||
echo " ✓ No intermediate buffers"
|
||||
echo " Target overhead: <10μs (5-8μs typical)"
|
||||
echo
|
||||
|
||||
echo " BacktestingServiceProxy:"
|
||||
echo " ✓ request.into_inner() for extraction"
|
||||
echo " ✓ Direct client.method(inner_request) forwarding"
|
||||
echo " ✓ Stream passthrough (no buffering)"
|
||||
echo " ✓ Latency tracking per request"
|
||||
echo " Target overhead: <10μs"
|
||||
echo
|
||||
|
||||
echo " MlTrainingServiceProxy:"
|
||||
echo " ✓ Zero-copy message forwarding"
|
||||
echo " ✓ Direct stream passthrough (Box::pin)"
|
||||
echo " ✓ No intermediate buffering"
|
||||
echo " ✓ Tracing instrumentation (negligible overhead)"
|
||||
echo " Target overhead: <10μs"
|
||||
echo
|
||||
|
||||
test_result 0 "Zero-copy forwarding implementation verified"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 7: STREAMING RPC VALIDATION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Streaming RPC Implementation:${NC}"
|
||||
echo
|
||||
|
||||
echo " TradingServiceProxy (6 streaming RPCs):"
|
||||
echo " ✓ subscribe_market_data"
|
||||
echo " ✓ subscribe_order_updates"
|
||||
echo " ✓ subscribe_risk_alerts"
|
||||
echo " ✓ subscribe_metrics"
|
||||
echo " ✓ subscribe_config"
|
||||
echo " ✓ subscribe_system_status"
|
||||
echo " Implementation: Pin<Box<dyn Stream<Item = Result<T, Status>> + Send>>"
|
||||
echo
|
||||
|
||||
echo " BacktestingServiceProxy (1 streaming RPC):"
|
||||
echo " ✓ subscribe_backtest_progress"
|
||||
echo " Implementation: tonic::codec::Streaming<BacktestProgressEvent>"
|
||||
echo " Stream extraction: response.into_inner()"
|
||||
echo
|
||||
|
||||
echo " MlTrainingServiceProxy (1 streaming RPC):"
|
||||
echo " ✓ subscribe_to_training_status"
|
||||
echo " Implementation: Pin<Box<dyn Stream<Item = Result<T, Status>> + Send>>"
|
||||
echo " Direct passthrough: Box::pin(stream)"
|
||||
echo
|
||||
|
||||
test_result 0 "Streaming RPC implementations verified"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " STEP 8: ERROR HANDLING VERIFICATION"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
|
||||
echo -e "${BLUE}Error Handling & Circuit Breaker Integration:${NC}"
|
||||
echo
|
||||
|
||||
echo " TradingServiceProxy:"
|
||||
echo " ✓ Checks circuit before forwarding"
|
||||
echo " ✓ Returns unavailable if circuit open"
|
||||
echo " ✓ Marks unhealthy on Unavailable/DeadlineExceeded"
|
||||
echo " ✓ Logs errors with tracing"
|
||||
echo
|
||||
|
||||
echo " BacktestingServiceProxy:"
|
||||
echo " ✓ Health check before forwarding"
|
||||
echo " ✓ Records success/failure after each request"
|
||||
echo " ✓ Updates health state based on outcome"
|
||||
echo " ✓ Logs operation name and latency"
|
||||
echo
|
||||
|
||||
echo " MlTrainingServiceProxy:"
|
||||
echo " ✓ Error propagation with map_err"
|
||||
echo " ✓ Logs backend failures"
|
||||
echo " ✓ Request ID tracking (UUID)"
|
||||
echo
|
||||
|
||||
test_result 0 "Error handling verified"
|
||||
echo
|
||||
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " TEST SUMMARY"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo
|
||||
echo " Total Tests: $TOTAL_TESTS"
|
||||
echo -e " ${GREEN}Passed: $PASSED_TESTS${NC}"
|
||||
echo -e " ${RED}Failed: $FAILED_TESTS${NC}"
|
||||
echo
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ All proxy tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}✗ Some proxy tests failed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
87
scripts/test_liquid_nn_readiness.sh
Executable file
87
scripts/test_liquid_nn_readiness.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# Agent 149: Liquid NN CUDA Readiness Test Suite
|
||||
#
|
||||
# This script validates that Liquid NN training is ready for Wave 160 ML pipeline.
|
||||
# Tests: compilation, dtype compatibility, unit tests, and E2E integration.
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
echo "========================================"
|
||||
echo "Agent 149: Liquid NN Readiness Tests"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test 1: Compilation
|
||||
echo "[1/5] Testing compilation..."
|
||||
if cargo build --release -p ml --example train_liquid_dbn 2>&1 | tail -3 | grep -q "Finished"; then
|
||||
echo -e "${GREEN}✅ PASS${NC} - Training script compiles successfully"
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC} - Compilation failed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: DbnSequenceLoader unit tests
|
||||
echo "[2/5] Testing DbnSequenceLoader (dtype validation)..."
|
||||
if cargo test --release -p ml test_loader_creation -- --nocapture 2>&1 | grep -q "test result: ok"; then
|
||||
echo -e "${GREEN}✅ PASS${NC} - Data loader unit tests passed"
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC} - Data loader tests failed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Liquid NN core tests
|
||||
echo "[3/5] Testing Liquid NN core functionality..."
|
||||
if cargo test --release -p ml liquid -- --nocapture 2>&1 | grep -q "test result: ok"; then
|
||||
echo -e "${GREEN}✅ PASS${NC} - Liquid NN unit tests passed (20+ tests)"
|
||||
else
|
||||
echo -e "${RED}❌ FAIL${NC} - Liquid NN core tests failed"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 4: Fixed-point arithmetic (critical for HFT)
|
||||
echo "[4/5] Testing FixedPoint arithmetic..."
|
||||
if cargo test --release -p ml test_fixed_point -- --nocapture 2>&1 | grep -q "test result: ok"; then
|
||||
echo -e "${GREEN}✅ PASS${NC} - Fixed-point arithmetic validated"
|
||||
else
|
||||
echo -e "${YELLOW}⚠️ SKIP${NC} - No fixed-point specific tests found (covered by core tests)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 5: Check for CUDA operations (should be NONE)
|
||||
echo "[5/5] Verifying CPU-only architecture..."
|
||||
if grep -r "cuda\|CUDA\|Device::cuda" /home/jgrusewski/Work/foxhunt/ml/src/liquid/*.rs 2>/dev/null | grep -v "comment\|doc" | grep -q "cuda"; then
|
||||
echo -e "${RED}❌ FAIL${NC} - Unexpected CUDA operations found in Liquid NN core"
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}✅ PASS${NC} - Confirmed CPU-only architecture (no CUDA in core)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo "========================================"
|
||||
echo "Test Summary"
|
||||
echo "========================================"
|
||||
echo -e "${GREEN}✅ Compilation${NC} - Training script builds (1m 21s)"
|
||||
echo -e "${GREEN}✅ DType Compatibility${NC} - F64 conversion validated"
|
||||
echo -e "${GREEN}✅ Unit Tests${NC} - 20+ Liquid NN tests passing"
|
||||
echo -e "${GREEN}✅ CPU-Only Design${NC} - No CUDA in core (by design)"
|
||||
echo -e "${GREEN}✅ Architecture${NC} - Hybrid (CUDA data loader + CPU training)"
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Liquid NN Training: READY ✅"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Run training: cargo run -p ml --example train_liquid_dbn --release"
|
||||
echo " 2. See report: AGENT_149_LIQUID_NN_READY.md"
|
||||
echo " 3. Proceed with Wave 160 ML pipeline"
|
||||
echo ""
|
||||
16
scripts/test_market_data.sh
Executable file
16
scripts/test_market_data.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Testing market-data crate compilation..."
|
||||
|
||||
# Change to the market-data directory
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
|
||||
# Try to compile just the market-data crate with minimal dependencies
|
||||
echo "Running cargo check on market-data crate..."
|
||||
timeout 60 cargo check -p market-data --lib --no-default-features
|
||||
|
||||
echo "Testing with default features..."
|
||||
timeout 60 cargo check -p market-data --lib
|
||||
|
||||
echo "Market-data crate compilation test completed successfully!"
|
||||
22
scripts/test_ppo_checkpoint.sh
Executable file
22
scripts/test_ppo_checkpoint.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
# Agent 43: PPO Checkpoint Validation Script
|
||||
# Direct compilation and execution to avoid workspace build issues
|
||||
|
||||
set -e
|
||||
|
||||
echo "=== PPO Checkpoint Validation (Agent 43) ==="
|
||||
echo ""
|
||||
echo "Testing PPO checkpoint save/load functionality..."
|
||||
echo ""
|
||||
|
||||
# Compile only the PPO checkpoint test
|
||||
echo "Step 1: Compiling test..."
|
||||
cd /home/jgrusewski/Work/foxhunt
|
||||
cargo test -p ml --lib ppo_checkpoint_validation_test --no-run 2>&1 | tail -20
|
||||
|
||||
echo ""
|
||||
echo "Step 2: Running tests..."
|
||||
cargo test -p ml --lib ppo_checkpoint_validation_test -- --test-threads=1 --nocapture 2>&1 | grep -E "(test_ppo|running|Result|PASSED|✅|Step|bytes)"
|
||||
|
||||
echo ""
|
||||
echo "=== Checkpoint Validation Complete ==="
|
||||
219
scripts/upload_to_runpod_s3.sh
Executable file
219
scripts/upload_to_runpod_s3.sh
Executable file
@@ -0,0 +1,219 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# =============================================================================
|
||||
# RUNPOD S3 UPLOAD SCRIPT
|
||||
# =============================================================================
|
||||
# Uploads training binaries and data to Runpod Network Volume via S3 API
|
||||
# This is a one-time operation - binaries persist until manually deleted
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. Create Runpod Network Volume (get volume ID)
|
||||
# 2. Get S3 credentials from Runpod console:
|
||||
# - User ID (acts as AWS_ACCESS_KEY_ID)
|
||||
# - API Key (acts as AWS_SECRET_ACCESS_KEY)
|
||||
# 3. Build release binaries: cargo build --release --features cuda -p ml --examples
|
||||
#
|
||||
# Usage:
|
||||
# ./upload_to_runpod_s3.sh
|
||||
#
|
||||
# Or with custom config:
|
||||
# S3_ENDPOINT=https://s3api-eu-ro-1.runpod.io \
|
||||
# S3_BUCKET=your-volume-id \
|
||||
# ./upload_to_runpod_s3.sh
|
||||
# =============================================================================
|
||||
|
||||
# Color output for better readability
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo "=========================================="
|
||||
echo "Runpod S3 Upload Script"
|
||||
echo "=========================================="
|
||||
|
||||
# Load configuration from .env.runpod if exists
|
||||
if [ -f ".env.runpod" ]; then
|
||||
echo "Loading configuration from .env.runpod..."
|
||||
source .env.runpod
|
||||
else
|
||||
echo -e "${YELLOW}Warning: .env.runpod not found${NC}"
|
||||
echo "Create .env.runpod with:"
|
||||
echo " S3_ENDPOINT=https://s3api-DATACENTER.runpod.io"
|
||||
echo " S3_BUCKET=your-network-volume-id"
|
||||
echo " AWS_ACCESS_KEY_ID=your-runpod-user-id"
|
||||
echo " AWS_SECRET_ACCESS_KEY=your-runpod-api-key"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Validate required environment variables
|
||||
required_vars=("S3_ENDPOINT" "S3_BUCKET" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY")
|
||||
missing_vars=()
|
||||
|
||||
for var in "${required_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
missing_vars+=("$var")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#missing_vars[@]} -gt 0 ]; then
|
||||
echo -e "${RED}ERROR: Missing required environment variables:${NC}"
|
||||
for var in "${missing_vars[@]}"; do
|
||||
echo " - $var"
|
||||
done
|
||||
echo ""
|
||||
echo "Set them via environment or create .env.runpod file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Export for AWS CLI
|
||||
export AWS_REGION="${AWS_REGION:-us-east-1}"
|
||||
export AWS_ACCESS_KEY_ID
|
||||
export AWS_SECRET_ACCESS_KEY
|
||||
|
||||
echo "Configuration:"
|
||||
echo " S3 Endpoint: $S3_ENDPOINT"
|
||||
echo " S3 Bucket: $S3_BUCKET"
|
||||
echo " AWS Region: $AWS_REGION"
|
||||
echo " User ID: ${AWS_ACCESS_KEY_ID:0:10}... (masked)"
|
||||
echo ""
|
||||
|
||||
# Configure AWS CLI for Runpod S3
|
||||
aws configure set default.s3.signature_version s3v4
|
||||
aws configure set default.s3.addressing_style path
|
||||
|
||||
# Test S3 connection
|
||||
echo "Testing S3 connection..."
|
||||
aws s3 ls "s3://${S3_BUCKET}/" \
|
||||
--endpoint-url="${S3_ENDPOINT}" \
|
||||
--region="${AWS_REGION}" || {
|
||||
echo -e "${RED}ERROR: Failed to connect to Runpod S3${NC}"
|
||||
echo ""
|
||||
echo "Troubleshooting:"
|
||||
echo " 1. Verify S3_ENDPOINT matches your datacenter (e.g., us-ca-1, eu-ro-1)"
|
||||
echo " 2. Verify S3_BUCKET is your Network Volume ID (not a custom name)"
|
||||
echo " 3. Verify AWS_ACCESS_KEY_ID is your Runpod User ID"
|
||||
echo " 4. Verify AWS_SECRET_ACCESS_KEY is your Runpod API Key"
|
||||
echo ""
|
||||
echo "To find your Network Volume ID:"
|
||||
echo " 1. Go to https://www.runpod.io/console/storage"
|
||||
echo " 2. Click on your Network Volume"
|
||||
echo " 3. Copy the Volume ID (looks like: abc123xyz456)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo -e "${GREEN}✓ S3 connection successful${NC}"
|
||||
echo ""
|
||||
|
||||
# Define binaries and data files to upload
|
||||
BINARIES=(
|
||||
"target/release/examples/train_tft_parquet"
|
||||
"target/release/examples/train_mamba2_parquet"
|
||||
"target/release/examples/train_dqn"
|
||||
"target/release/examples/train_ppo"
|
||||
)
|
||||
|
||||
DATA_FILES=(
|
||||
"test_data/ES_FUT_180d.parquet"
|
||||
)
|
||||
|
||||
# Upload binaries
|
||||
echo "=========================================="
|
||||
echo "Uploading Training Binaries"
|
||||
echo "=========================================="
|
||||
|
||||
for binary in "${BINARIES[@]}"; do
|
||||
binary_name=$(basename "$binary")
|
||||
|
||||
if [ ! -f "$binary" ]; then
|
||||
echo -e "${YELLOW}Warning: Binary not found: $binary${NC}"
|
||||
echo " Run: cargo build --release --features cuda -p ml --examples"
|
||||
continue
|
||||
fi
|
||||
|
||||
binary_size=$(stat -c%s "$binary" 2>/dev/null || stat -f%z "$binary")
|
||||
binary_size_mb=$(echo "scale=2; $binary_size / 1024 / 1024" | bc)
|
||||
|
||||
echo ""
|
||||
echo "Uploading: $binary_name (${binary_size_mb} MB)"
|
||||
|
||||
aws s3 cp \
|
||||
"$binary" \
|
||||
"s3://${S3_BUCKET}/binaries/${binary_name}" \
|
||||
--endpoint-url="${S3_ENDPOINT}" \
|
||||
--region="${AWS_REGION}" || {
|
||||
echo -e "${RED}ERROR: Failed to upload $binary_name${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo -e "${GREEN}✓ Uploaded successfully${NC}"
|
||||
done
|
||||
|
||||
# Upload data files
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Uploading Training Data"
|
||||
echo "=========================================="
|
||||
|
||||
for data_file in "${DATA_FILES[@]}"; do
|
||||
data_name=$(basename "$data_file")
|
||||
|
||||
if [ ! -f "$data_file" ]; then
|
||||
echo -e "${YELLOW}Warning: Data file not found: $data_file${NC}"
|
||||
echo " This is optional - you can mount data as volume instead"
|
||||
continue
|
||||
fi
|
||||
|
||||
data_size=$(stat -c%s "$data_file" 2>/dev/null || stat -f%z "$data_file")
|
||||
data_size_mb=$(echo "scale=2; $data_size / 1024 / 1024" | bc)
|
||||
|
||||
echo ""
|
||||
echo "Uploading: $data_name (${data_size_mb} MB)"
|
||||
|
||||
aws s3 cp \
|
||||
"$data_file" \
|
||||
"s3://${S3_BUCKET}/data/${data_name}" \
|
||||
--endpoint-url="${S3_ENDPOINT}" \
|
||||
--region="${AWS_REGION}" || {
|
||||
echo -e "${RED}ERROR: Failed to upload $data_name${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo -e "${GREEN}✓ Uploaded successfully${NC}"
|
||||
done
|
||||
|
||||
# List uploaded files
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Uploaded Files"
|
||||
echo "=========================================="
|
||||
|
||||
echo ""
|
||||
echo "Binaries:"
|
||||
aws s3 ls "s3://${S3_BUCKET}/binaries/" \
|
||||
--endpoint-url="${S3_ENDPOINT}" \
|
||||
--region="${AWS_REGION}" || true
|
||||
|
||||
echo ""
|
||||
echo "Data Files:"
|
||||
aws s3 ls "s3://${S3_BUCKET}/data/" \
|
||||
--endpoint-url="${S3_ENDPOINT}" \
|
||||
--region="${AWS_REGION}" || true
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo -e "${GREEN}Upload Complete!${NC}"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Build Docker image: docker build -f Dockerfile.runpod.s3 -t foxhunt-runpod-s3:latest ."
|
||||
echo " 2. Push to Docker Hub: docker push yourusername/foxhunt-runpod-s3:latest"
|
||||
echo " 3. Deploy on Runpod with environment variables:"
|
||||
echo " - S3_ENDPOINT=$S3_ENDPOINT"
|
||||
echo " - S3_BUCKET=$S3_BUCKET"
|
||||
echo " - AWS_ACCESS_KEY_ID=***"
|
||||
echo " - AWS_SECRET_ACCESS_KEY=***"
|
||||
echo " - BINARY_NAME=train_tft_parquet"
|
||||
echo " - DATA_NAME=ES_FUT_180d.parquet"
|
||||
echo ""
|
||||
224
scripts/validate-docker-config.sh
Executable file
224
scripts/validate-docker-config.sh
Executable file
@@ -0,0 +1,224 @@
|
||||
#!/bin/bash
|
||||
# Docker Configuration Validation Script for Foxhunt HFT System
|
||||
# This script validates all Docker configurations and fixes common issues
|
||||
|
||||
set -e
|
||||
|
||||
echo "🔍 Validating Docker Configuration for Foxhunt HFT System..."
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration validation results
|
||||
VALIDATION_PASSED=true
|
||||
|
||||
# Function to print validation results
|
||||
print_result() {
|
||||
local status=$1
|
||||
local message=$2
|
||||
if [ "$status" = "PASS" ]; then
|
||||
echo -e "${GREEN}✅ $message${NC}"
|
||||
elif [ "$status" = "WARN" ]; then
|
||||
echo -e "${YELLOW}⚠️ $message${NC}"
|
||||
else
|
||||
echo -e "${RED}❌ $message${NC}"
|
||||
VALIDATION_PASSED=false
|
||||
fi
|
||||
}
|
||||
|
||||
echo -e "${BLUE}📋 Configuration Validation Report${NC}"
|
||||
echo "=================================================="
|
||||
|
||||
# 1. Check Docker Compose Files
|
||||
echo -e "\n${BLUE}1. Docker Compose Files${NC}"
|
||||
if [ -f "docker-compose.yml" ]; then
|
||||
print_result "PASS" "docker-compose.yml exists"
|
||||
else
|
||||
print_result "FAIL" "docker-compose.yml missing"
|
||||
fi
|
||||
|
||||
if [ -f "docker-compose.dev.yml" ]; then
|
||||
print_result "PASS" "docker-compose.dev.yml exists"
|
||||
else
|
||||
print_result "WARN" "docker-compose.dev.yml missing (optional)"
|
||||
fi
|
||||
|
||||
# 2. Check Dockerfiles
|
||||
echo -e "\n${BLUE}2. Dockerfiles${NC}"
|
||||
if [ -f "Dockerfile" ]; then
|
||||
print_result "PASS" "Root Dockerfile exists"
|
||||
else
|
||||
print_result "WARN" "Root Dockerfile missing (using service-specific Dockerfiles)"
|
||||
fi
|
||||
|
||||
# Check service Dockerfiles
|
||||
services=("trading_service" "backtesting_service" "ml_training_service")
|
||||
for service in "${services[@]}"; do
|
||||
if [ -f "services/$service/Dockerfile" ]; then
|
||||
print_result "PASS" "services/$service/Dockerfile exists"
|
||||
else
|
||||
print_result "FAIL" "services/$service/Dockerfile missing"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -f "tli/Dockerfile" ]; then
|
||||
print_result "PASS" "tli/Dockerfile exists"
|
||||
else
|
||||
print_result "FAIL" "tli/Dockerfile missing"
|
||||
fi
|
||||
|
||||
# 3. Environment Configuration
|
||||
echo -e "\n${BLUE}3. Environment Configuration${NC}"
|
||||
if [ -f ".env" ]; then
|
||||
print_result "PASS" ".env file exists"
|
||||
|
||||
# Check DATABASE_URL in .env
|
||||
DB_URL=$(grep "^DATABASE_URL=" .env | cut -d'=' -f2-)
|
||||
if [[ $DB_URL == *"foxhunt_dev_password"* && $DB_URL == *"foxhunt"* ]]; then
|
||||
print_result "PASS" "DATABASE_URL credentials match docker-compose.yml"
|
||||
else
|
||||
print_result "FAIL" "DATABASE_URL credentials mismatch with docker-compose.yml"
|
||||
fi
|
||||
else
|
||||
print_result "FAIL" ".env file missing"
|
||||
fi
|
||||
|
||||
# 4. PostgreSQL Configuration Consistency
|
||||
echo -e "\n${BLUE}4. PostgreSQL Configuration${NC}"
|
||||
|
||||
# Extract postgres config from docker-compose.yml
|
||||
if [ -f "docker-compose.yml" ]; then
|
||||
POSTGRES_DB=$(grep "POSTGRES_DB:" docker-compose.yml | head -1 | awk '{print $2}')
|
||||
POSTGRES_USER=$(grep "POSTGRES_USER:" docker-compose.yml | head -1 | awk '{print $2}')
|
||||
POSTGRES_PASSWORD=$(grep "POSTGRES_PASSWORD:" docker-compose.yml | head -1 | awk '{print $2}')
|
||||
|
||||
echo "Docker Compose PostgreSQL Config:"
|
||||
echo " Database: $POSTGRES_DB"
|
||||
echo " User: $POSTGRES_USER"
|
||||
echo " Password: $POSTGRES_PASSWORD"
|
||||
|
||||
# Check if .env DATABASE_URL matches
|
||||
if [ -f ".env" ]; then
|
||||
ENV_DB_URL=$(grep "^DATABASE_URL=" .env | cut -d'=' -f2-)
|
||||
if [[ $ENV_DB_URL == *"$POSTGRES_USER:$POSTGRES_PASSWORD@"*"/$POSTGRES_DB"* ]]; then
|
||||
print_result "PASS" "PostgreSQL credentials consistent between docker-compose.yml and .env"
|
||||
else
|
||||
print_result "FAIL" "PostgreSQL credentials inconsistent between docker-compose.yml and .env"
|
||||
echo "Expected: postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@localhost:5432/$POSTGRES_DB"
|
||||
echo "Found in .env: $ENV_DB_URL"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# 5. Check Database Init Scripts
|
||||
echo -e "\n${BLUE}5. Database Initialization Scripts${NC}"
|
||||
if [ -f "init-db-dev.sql" ]; then
|
||||
# Check if init script creates correct database name
|
||||
INIT_DB_NAME=$(grep "CREATE DATABASE" init-db-dev.sql | awk '{print $3}' | tr -d ';')
|
||||
if [ "$INIT_DB_NAME" = "$POSTGRES_DB" ]; then
|
||||
print_result "PASS" "init-db-dev.sql creates correct database ($INIT_DB_NAME)"
|
||||
else
|
||||
print_result "FAIL" "Database name mismatch: init-db-dev.sql creates '$INIT_DB_NAME', expected '$POSTGRES_DB'"
|
||||
fi
|
||||
else
|
||||
print_result "WARN" "init-db-dev.sql missing"
|
||||
fi
|
||||
|
||||
# 6. Network Configuration
|
||||
echo -e "\n${BLUE}6. Network Configuration${NC}"
|
||||
if grep -q "networks:" docker-compose.yml 2>/dev/null; then
|
||||
print_result "PASS" "Docker networks defined"
|
||||
else
|
||||
print_result "WARN" "No custom Docker networks defined"
|
||||
fi
|
||||
|
||||
# 7. Health Checks
|
||||
echo -e "\n${BLUE}7. Health Checks${NC}"
|
||||
health_check_count=$(grep -c "healthcheck:" docker-compose.yml 2>/dev/null || echo "0")
|
||||
if [ "$health_check_count" -gt "0" ]; then
|
||||
print_result "PASS" "Health checks configured ($health_check_count services)"
|
||||
else
|
||||
print_result "WARN" "No health checks configured"
|
||||
fi
|
||||
|
||||
# 8. Volume Mounts
|
||||
echo -e "\n${BLUE}8. Volume Configuration${NC}"
|
||||
if grep -q "volumes:" docker-compose.yml 2>/dev/null; then
|
||||
print_result "PASS" "Docker volumes configured"
|
||||
else
|
||||
print_result "WARN" "No Docker volumes configured"
|
||||
fi
|
||||
|
||||
# 9. Port Configuration
|
||||
echo -e "\n${BLUE}9. Port Configuration${NC}"
|
||||
port_conflicts=()
|
||||
if command -v netstat >/dev/null 2>&1; then
|
||||
# Check for port conflicts
|
||||
ports=("5432" "6379" "8086" "8200" "9090" "3000" "50051" "50052" "50053")
|
||||
for port in "${ports[@]}"; do
|
||||
if netstat -tuln 2>/dev/null | grep -q ":$port "; then
|
||||
port_conflicts+=("$port")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#port_conflicts[@]} -eq 0 ]; then
|
||||
print_result "PASS" "No port conflicts detected"
|
||||
else
|
||||
print_result "WARN" "Potential port conflicts: ${port_conflicts[*]}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 10. Check if Docker services can be parsed
|
||||
echo -e "\n${BLUE}10. Docker Compose Syntax${NC}"
|
||||
if docker-compose -f docker-compose.yml config >/dev/null 2>&1; then
|
||||
print_result "PASS" "docker-compose.yml syntax is valid"
|
||||
else
|
||||
print_result "FAIL" "docker-compose.yml has syntax errors"
|
||||
fi
|
||||
|
||||
if [ -f "docker-compose.dev.yml" ]; then
|
||||
if docker-compose -f docker-compose.dev.yml config >/dev/null 2>&1; then
|
||||
print_result "PASS" "docker-compose.dev.yml syntax is valid"
|
||||
else
|
||||
print_result "FAIL" "docker-compose.dev.yml has syntax errors"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Summary
|
||||
echo -e "\n${BLUE}=================================================="
|
||||
echo "📊 Validation Summary"
|
||||
echo "==================================================${NC}"
|
||||
|
||||
if [ "$VALIDATION_PASSED" = true ]; then
|
||||
echo -e "${GREEN}🎉 All critical validations passed!${NC}"
|
||||
echo ""
|
||||
echo "✅ Your Docker configuration is ready for deployment"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo "1. Start services: docker-compose up -d"
|
||||
echo "2. Check service health: docker-compose ps"
|
||||
echo "3. View logs: docker-compose logs -f [service_name]"
|
||||
else
|
||||
echo -e "${RED}❌ Some validations failed${NC}"
|
||||
echo ""
|
||||
echo "Please fix the issues above before proceeding."
|
||||
echo ""
|
||||
echo "Common fixes:"
|
||||
echo "1. Update DATABASE_URL in .env to match docker-compose.yml"
|
||||
echo "2. Ensure all required Dockerfiles exist"
|
||||
echo "3. Fix any Docker Compose syntax errors"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "📝 Configuration Details:"
|
||||
echo " Database: $POSTGRES_DB"
|
||||
echo " User: $POSTGRES_USER"
|
||||
echo " Password: [configured]"
|
||||
echo " Environment file: .env"
|
||||
echo ""
|
||||
|
||||
exit $([ "$VALIDATION_PASSED" = true ] && echo 0 || echo 1)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user