# Market Data Service

A high-performance financial data API that provides comprehensive market data for various financial instruments including cryptocurrencies, stocks, forex, and commodities through both RESTful endpoints and real-time WebSocket connections.

## MT5 Integration

The service includes an MT5 Expert Advisor (EA) that automatically sends historical candle data and live tick prices to the API.

### EA Features
- Automatic symbol registration in the database
- Bulk upload of last 1000 candles per symbol
- Real-time price streaming on tick events
- Error handling with retry logic
- Configurable API endpoint and authentication

### Setup Instructions
1. Copy `MT5/Experts/MarketDataSender.mq5` to your MT5 Experts directory
2. Configure EA settings:
   - `ApiBaseUrl`: Your API endpoint (e.g., `http://your-server:3000`)
   - `ApiKey`: Optional authentication key
   - `HistoricalCandleCount`: Number of historical candles to send (default: 1000)
   - `HistoricalTimeframe`: Timeframe for historical data (default: H1)

3. Attach the EA to any MT5 chart
4. The EA will:
   - Register all available symbols with the API
   - Send historical candles on initialization
   - Stream live prices every second

### Notes
- Symbols are automatically created if they don't exist
- Exchange is derived from symbol name (format: `EXCHANGE_SYMBOL`)
- Default instrument type is forex (customize in EA code if needed)

## Features  
✅ **MT5 Integration**  
- Automatic retry logic (3 attempts with exponential backoff)  
- Precision-preserving data transmission  
- Symbol auto-registration  

✅ **Data Integrity**  
- Unique constraint on candle timestamps per symbol  
- 15-decimal precision enforcement  
- Strict schema validation  

✅ **Reliability**  
- Database transaction safety  
- Error recovery mechanisms  
- Comprehensive test coverage  

- **Multi-Asset Support**: Handles cryptocurrencies, stocks, forex, and commodities
- **Real-time Data**: Live price feeds with bid/ask spreads
- **Historical Data**: OHLCV candle data with flexible timeframes
- **RESTful API**: Well-structured endpoints for all operations
- **Data Validation**: Comprehensive input validation using Joi
- **Error Handling**: Robust error handling with detailed responses
- **Security**: Helmet.js for security headers, CORS support
- **Logging**: Winston-based logging with multiple transports
- **Database**: PostgreSQL with Sequelize ORM
- **Scalable Architecture**: Modular design with controllers, routes, and middleware

## Tech Stack

- **Backend**: Node.js, Express.js
- **Database**: PostgreSQL
- **ORM**: Sequelize
- **Validation**: Joi
- **Security**: Helmet, CORS
- **Logging**: Winston, Morgan
- **Testing**: Jest, Supertest
- **Development**: Nodemon, ESLint, Sequelize CLI

## Project Structure

```
market-data-service/
├── src/
│   ├── config/
│   │   └── database.js          # Database configuration
│   ├── controllers/
│   │   ├── symbolController.js      # Symbol CRUD operations
│   │   ├── candleController.js      # Candle data operations
│   │   └── livePriceController.js   # Live price operations
│   ├── middleware/
│   │   ├── errorHandler.js          # Global error handling
│   │   └── validation.js            # Request validation
│   ├── models/
│   │   ├── Symbol.js                # Symbol model
│   │   ├── Candle1h.js              # 1-hour candle model
│   │   ├── LivePrice.js             # Live price model
│   │   └── index.js                 # Model associations
│   ├── routes/
│   │   ├── symbols.js               # Symbol routes
│   │   ├── candles.js               # Candle routes
│   │   └── livePrices.js            # Live price routes
│   ├── utils/
│   │   └── logger.js                # Logging utility
│   ├── app.js                       # Express app configuration
│   └── server.js                    # Server startup
├── tests/                           # Test files
├── schema.sql                       # Database schema
├── .env                             # Environment variables
├── .gitignore                       # Git ignore rules
├── package.json                     # Dependencies and scripts
└── README.md                        # This file
```

## Technical Specifications  

**Database Constraints**  
```sql
ALTER TABLE candles_1h 
ADD CONSTRAINT unique_symbol_open_time 
UNIQUE (symbol_id, open_time);
```

**Precision Requirements**  
```javascript
// All numeric fields require 15 decimal precision
Joi.number().precision(15)
```

## 🚀 Complete Deployment Guide

## 📋 Prerequisites (All Versions)

### Required Software (Latest Stable Versions)
- **Node.js**: v18.x LTS or higher
- **PostgreSQL**: v13.x or higher
- **Nginx**: v1.24.x or higher
- **Git**: Latest version
- **Certbot** (for SSL certificates)

---

## 🆕 FRESH SERVER DEPLOYMENT (Step-by-Step)

### Step 1: Server Preparation
```bash
# Update system packages
sudo apt update && sudo apt upgrade -y

# Install required tools
sudo apt install -y curl wget git htop nano ufw
```

### Step 2: Install Node.js (v18.x LTS)
```bash
# Using NodeSource repository
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs

# Verify installation
node --version  # Should show v18.x.x
npm --version   # Should show latest version
```

### Step 3: Install PostgreSQL
```bash
# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib

# Start and enable PostgreSQL
sudo systemctl enable postgresql
sudo systemctl start postgresql

# Verify installation
psql --version  # Should show 15.x or higher
```

### Step 4: Deploy Application
```bash
# Create application directory
sudo mkdir -p /root/market-data-service
sudo chown $USER:$USER /root/market-data-service
cd /root/market-data-service

# Clone repository (replace with your actual repo URL)
git clone https://git.mqldevelopment.com/muhammad.uzair/market-data-service.git .
# OR copy your project files to this directory

# Install dependencies
npm install --production

# Set up environment variables
cat > .env << EOF
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=financial_data
DB_USER=postgres
DB_PASSWORD=your_secure_password_here

PORT=3001
NODE_ENV=production

JWT_SECRET=your_secure_jwt_secret_key_here
CORS_ORIGIN=*

LOG_LEVEL=info
EOF
```

### Step 5: Database Setup
```bash
# Switch to postgres user
sudo -u postgres psql

# In PostgreSQL shell, run:
CREATE DATABASE financial_data;
ALTER USER postgres PASSWORD 'your_secure_password_here';
GRANT ALL PRIVILEGES ON DATABASE financial_data TO postgres;
\q

# Exit and run migrations
npx sequelize-cli db:migrate

# Verify tables were created
sudo -u postgres psql -d financial_data -c "\dt"
```

### Step 6: Install PM2 (Process Manager)
```bash
# Install PM2 globally
sudo npm install -g pm2

# Create PM2 ecosystem file
cat > ecosystem.config.js << EOF
module.exports = {
  apps: [{
    name: 'market-data-api',
    script: 'src/server.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3001
    },
    error_file: './logs/err.log',
    out_file: './logs/out.log',
    log_file: './logs/combined.log',
    time: true,
    merge_logs: true,
    watch: false,
    max_memory_restart: '1G'
  }]
};
EOF

# Start with PM2
pm2 start ecosystem.config.js
pm2 save
pm2 startup

# Verify PM2 status
pm2 status
pm2 logs
```

### Step 7: Configure Nginx (WITHOUT SSL first)
```bash
# Copy nginx configuration
sudo cp nginx-1.24.0/conf/nginx.conf /etc/nginx/nginx.conf

# Test configuration
sudo nginx -t

# Start Nginx
sudo systemctl enable nginx
sudo systemctl start nginx
```

### Step 8: Install SSL Certificate (Certbot)
```bash
# Install certbot
sudo apt install -y certbot python3-certbot-nginx

# Generate SSL certificate
sudo certbot --nginx -d your-domain.com

# Test certificate
sudo certbot certificates
```

### Step 9: Update Nginx for SSL (After Certificate Generation)
```bash
# Update nginx configuration with SSL
sudo tee /etc/nginx/sites-available/market-data-api << EOF
server {
    listen 80;
    server_name your-domain.com;

    # Redirect HTTP to HTTPS
    return 301 https://\$server_name\$request_uri;
}

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # Security Headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;

    # API Proxy Settings
    location /api/ {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;

        # Timeout and buffer settings
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 4k;
        client_max_body_size 50M;
    }

    location /health {
        proxy_pass http://localhost:3001/health;
        proxy_http_version 1.1;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }

    location / {
        return 200 "Market Data API is running. Use /api/ for endpoints\\n";
        add_header Content-Type text/plain;
    }
}
EOF

# Enable site and restart nginx
sudo ln -s /etc/nginx/sites-available/market-data-api /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl restart nginx
```

### Step 10: Final Testing
```bash
# Test HTTP to HTTPS redirect
curl -I http://your-domain.com

# Test HTTPS API endpoints
curl https://your-domain.com/health
curl https://your-domain.com/api/health
curl https://your-domain.com/api/symbols

# Test bulk endpoint
curl -X POST https://your-domain.com/api/live-prices/bulk \
  -H "Content-Type: application/json" \
  -d '{"prices": [{"symbolId": 1, "price": 123.45}]}'
```

---

## 💻 LOCAL DEVELOPMENT SETUP

This section explains how to run the project locally on your development machine.

### Prerequisites for Local Development
- **Node.js**: v18.x LTS or higher
- **PostgreSQL**: v13.x or higher
- **Git**: Latest version

### Step-by-Step Local Setup

#### 1. Clone the Repository
```bash
# Clone from GitHub
git clone https://git.mqldevelopment.com/muhammad.uzair/market-data-service.git
cd market-data-service

# Install dependencies
npm install
```

#### 2. Set Up PostgreSQL Database
```bash
# Create local database
createdb financial_data

# Set up environment variables
cat > .env << EOF
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_NAME=financial_data
DB_USER=your_local_username
DB_PASSWORD=your_local_password

PORT=3001
NODE_ENV=development

JWT_SECRET=your_development_jwt_secret_key
CORS_ORIGIN=http://localhost:3000

LOG_LEVEL=debug
EOF
```

#### 3. Run Database Migrations
```bash
# Install Sequelize CLI (if not already installed)
npm install -g sequelize-cli

# Run migrations to create tables
npx sequelize-cli db:migrate

# Verify tables were created
psql -d financial_data -c "\dt"
```

#### 4. Start Development Server
```bash
# Option 1: Using npm script (recommended)
npm run dev

# Option 2: Using nodemon directly
npx nodemon src/server.js

# Option 3: Using node directly
node src/server.js
```

#### 5. Verify Local Setup
```bash
# Test health endpoint
curl http://localhost:3001/health

# Test API endpoints
curl http://localhost:3001/api/symbols
curl http://localhost:3001/api/live-prices

# Test bulk endpoint
curl -X POST http://localhost:3001/api/live-prices/bulk \
  -H "Content-Type: application/json" \
  -d '{"prices": [{"symbolId": 1, "price": 123.45}]}'
```


## 🔄 UPDATING EXISTING DEPLOYMENT

### Quick Update (When Code Changes on GitHub)

```bash
# Navigate to project directory
cd /root/market-data-service

# Stop the application
pm2 stop market-data-api

# Backup current deployment (optional but recommended)
cp .env .env.backup
cp ecosystem.config.js ecosystem.config.js.backup

# Pull latest changes
git pull origin master

# Install any new dependencies
npm install --production

# Run database migrations (if schema changed)
npx sequelize-cli db:migrate

# Restart application
pm2 start ecosystem.config.js
pm2 save

# Verify everything is working
pm2 status
curl https://your-domain.com/health
```

### Full Update (Major Changes)

```bash
# Navigate to project directory
cd /root/market-data-service

# Create backup
sudo cp -r /root/market-data-service /root/market-data-service-backup-$(date +%Y%m%d_%H%M%S)

# Stop services
pm2 stop market-data-api
sudo systemctl stop nginx

# Update code
git fetch origin
git reset --hard origin/master

# Install dependencies
npm install --production

# Update environment if needed
nano .env

# Run migrations
npx sequelize-cli db:migrate

# Update PM2 configuration if needed
pm2 start ecosystem.config.js
pm2 save

# Restart nginx
sudo systemctl start nginx

# Verify deployment
pm2 status
sudo systemctl status nginx
curl https://your-domain.com/health
```

---

## 🛠️ Troubleshooting Guide

### Common Issues & Solutions

#### 1. PostgreSQL User Already Exists
**Error:** `ERROR: role "postgres" already exists`

**Solution:**
```bash
# Just set password for existing user
sudo -u postgres psql
ALTER USER postgres PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE financial_data TO postgres;
\q
```

#### 2. SSL Certificate Not Found
**Error:** `cannot load certificate "/etc/letsencrypt/live/.../fullchain.pem"`

**Solution:**
```bash
# Generate certificate first
sudo certbot --nginx -d your-domain.com

# Check if certificates exist
ls -la /etc/letsencrypt/live/your-domain.com/

# Test nginx configuration
sudo nginx -t
```

#### 3. PM2 Multiple Instances
**Question:** Why are there 8 instances running?

**Answer:** This is **GOOD for production**:
- Uses all CPU cores efficiently
- Better load distribution
- Fault tolerance
- Zero-downtime restarts

**To adjust instances:**
```bash
pm2 scale market-data-api 4  # Use 4 instances
pm2 scale market-data-api 1  # Use 1 instance for testing
```

## 📊 Monitoring & Maintenance

```bash
# Check service status
pm2 status
sudo systemctl status nginx
sudo systemctl status postgresql

# View logs
pm2 logs --lines 50
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log

```

### SSL Certificate Auto-Renewal
```bash
# Check renewal status
sudo certbot certificates
sudo systemctl status certbot.timer

# Manual renewal test
sudo certbot renew --dry-run
```

---

## 🔧 Management Commands

### PM2 Management
```bash
pm2 status              # Check status
pm2 logs                # View logs
pm2 monit               # Real-time monitoring
pm2 restart all         # Restart all apps
pm2 stop all            # Stop all apps
pm2 delete all          # Delete all apps
```

### Nginx Management
```bash
sudo systemctl status nginx      # Check status
sudo systemctl restart nginx     # Restart nginx
sudo nginx -t                    # Test configuration
sudo tail -f /var/log/nginx/error.log  # View error logs
```

### Database Management
```bash
# Connect to database
sudo -u postgres psql -d financial_data

# View tables
\dt

# View table structure
\d table_name

# Backup database
pg_dump financial_data > backup.sql

# Restore database
psql financial_data < backup.sql
```

---

## Environment Variables

Create a `.env` file in the root directory with the following variables:

```env
# Database Configuration
DB_HOST=localhost
DB_PORT=5432
DB_NAME=market_data
DB_USER=your_username
DB_PASSWORD=your_password

# Server Configuration
PORT=3000
NODE_ENV=development

# JWT Configuration (if needed for authentication)
JWT_SECRET=your_jwt_secret_key

# API Keys (if needed for external services)
# BINANCE_API_KEY=your_api_key
# BINANCE_API_SECRET=your_api_secret

# CORS Configuration
CORS_ORIGIN=*
```

## API Endpoints

### Health Check
- `GET /health` - Check service health

### Symbols
- `GET /api/symbols` - Get all symbols (with filtering)
- `GET /api/symbols/search?q=EURUSD` - Search symbols by name
- `GET /api/symbols/:id` - Get symbol by ID
- `POST /api/symbols` - Create new symbol
- `PUT /api/symbols/:id` - Update symbol
- `DELETE /api/symbols/:id` - Delete symbol (soft delete)

### Candles
- `GET /api/candles?symbolId=1&startTime=2025-01-01T00:00:00Z&endTime=2025-01-02T00:00:00Z&limit=100&offset=0` - Get candles with filtering
- `GET /api/candles/ohlc?symbolId=1&period=1h&limit=100` - Get OHLC data
- `GET /api/candles/:symbolId/latest` - Get latest candle for symbol
- `POST /api/candles` - Create new candle
- `POST /api/candles/bulk` - Bulk create candles
- `DELETE /api/candles/cleanup/:symbolId?keep=1000` - Clean up old candles, keep latest N (default 1000)

### Live Prices
- `GET /api/live-prices` - Get all live prices
- `GET /api/live-prices/exchange/:exchange` - Get live prices by exchange
- `GET /api/live-prices/type/:type` - Get live prices by instrument type
- `GET /api/live-prices/:symbolId` - Get live price for symbol
- `POST /api/live-prices` - Create/update live price
- `POST /api/live-prices/bulk` - Bulk update live prices
- `DELETE /api/live-prices/:symbolId` - Delete live price

## Development

### Available Scripts

- `npm start` - Start production server
- `npm run dev` - Start development server with auto-reload
- `npm test` - Run Jest test suite
- `npm run test:watch` - Run tests in watch mode
- `npm run migrate` - Run database migrations
- `npm run migrate:undo` - Revert last migration
- `npm run lint` - Run ESLint
- `npm run lint:fix` - Fix ESLint issues


### Database Migrations

We use Sequelize CLI for database migrations:

```bash
# Create new migration
npx sequelize-cli migration:generate --name your-migration-name

# Run pending migrations
npx sequelize-cli db:migrate

# Revert last migration
npx sequelize-cli db:migrate:undo
```

## Deployment

1. Set `NODE_ENV=production` in your environment
2. Run `npm start` to start the production server
3. Consider using a process manager like PM2 for production deployments
