initial setup
This commit is contained in:
commit
232a63dfe8
61 changed files with 4985 additions and 0 deletions
60
.dockerignore
Normal file
60
.dockerignore
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Node.js
|
||||
node_modules/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
.turbo/
|
||||
.next/
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# Documentation
|
||||
README.md
|
||||
DOCKER.md
|
||||
docs/
|
||||
|
||||
# Docker
|
||||
Dockerfile*
|
||||
docker-compose*
|
||||
.dockerignore
|
||||
|
||||
# Cache
|
||||
.cache/
|
||||
.temp/
|
||||
.tmp/
|
||||
|
||||
# Test coverage
|
||||
coverage/
|
||||
.nyc_output/
|
||||
|
||||
# Misc
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
35
.env.example
Normal file
35
.env.example
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Environment Configuration
|
||||
NODE_ENV=development
|
||||
PORT=3000
|
||||
|
||||
# Database Configuration
|
||||
QUESTDB_HOST=localhost
|
||||
QUESTDB_PORT=9000
|
||||
QUESTDB_DATABASE=qdb
|
||||
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DATABASE=stockbot
|
||||
POSTGRES_USERNAME=postgres
|
||||
POSTGRES_PASSWORD=password
|
||||
|
||||
DRAGONFLY_HOST=localhost
|
||||
DRAGONFLY_PORT=6379
|
||||
DRAGONFLY_PASSWORD=
|
||||
|
||||
# API Keys (Add your actual keys here)
|
||||
ALPHA_VANTAGE_API_KEY=demo
|
||||
ALPACA_API_KEY=your_alpaca_key_here
|
||||
ALPACA_SECRET_KEY=your_alpaca_secret_here
|
||||
|
||||
# Trading Configuration
|
||||
PAPER_TRADING=true
|
||||
MAX_POSITION_SIZE=0.1
|
||||
MAX_DAILY_LOSS=1000
|
||||
|
||||
# Logging
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# Feature Flags
|
||||
ENABLE_ML_SIGNALS=false
|
||||
ENABLE_SENTIMENT_ANALYSIS=false
|
||||
106
.gitignore
vendored
Normal file
106
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# Dependencies
|
||||
node_modules/
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Production builds
|
||||
dist/
|
||||
build/
|
||||
.next/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
bun-debug.log*
|
||||
bun-error.log*
|
||||
*.log
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage/
|
||||
*.lcov
|
||||
|
||||
# Dependency directories
|
||||
.pnpm-store/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
|
||||
# Storybook build outputs
|
||||
.out
|
||||
.storybook-out
|
||||
|
||||
# Temporary folders
|
||||
tmp/
|
||||
temp/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
|
||||
# Trading bot specific
|
||||
data/
|
||||
backtest-results/
|
||||
logs/
|
||||
*.db
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
# Turbo
|
||||
.turbo
|
||||
272
DOCKER.md
Normal file
272
DOCKER.md
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
# 🐳 Trading Bot Docker Infrastructure
|
||||
|
||||
This directory contains the Docker Compose configuration for the Trading Bot infrastructure, including databases, caching, monitoring, and admin tools.
|
||||
|
||||
## 🏗️ Architecture Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Trading Bot Infrastructure │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 🐉 Dragonfly (Redis) │ 🐘 PostgreSQL │ 📊 QuestDB │
|
||||
│ Events & Caching │ Operational DB │ Time-series Data │
|
||||
│ Port: 6379 │ Port: 5432 │ Port: 9000/8812 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 🔧 Redis Insight │ 🛠️ PgAdmin │ 📈 Prometheus │
|
||||
│ Dragonfly GUI │ PostgreSQL GUI │ Metrics Collection │
|
||||
│ Port: 8001 │ Port: 8080 │ Port: 9090 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ 📊 Grafana Dashboard │
|
||||
│ Visualization & Alerting │
|
||||
│ Port: 3000 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Start Core Infrastructure
|
||||
```powershell
|
||||
# Start essential services (Dragonfly, PostgreSQL, QuestDB)
|
||||
npm run infra:up
|
||||
|
||||
# Or use the management script
|
||||
npm run docker:start
|
||||
```
|
||||
|
||||
### 2. Start Admin Interfaces
|
||||
```powershell
|
||||
# Start Redis Insight and PgAdmin
|
||||
npm run docker:admin
|
||||
```
|
||||
|
||||
### 3. Start Full Development Environment
|
||||
```powershell
|
||||
# Start infrastructure + admin + your trading services
|
||||
npm run dev:full
|
||||
```
|
||||
|
||||
## 📊 Service Access Points
|
||||
|
||||
| Service | URL | Credentials | Purpose |
|
||||
|---------|-----|-------------|---------|
|
||||
| **Dragonfly** | `localhost:6379` | None | Redis-compatible cache & events |
|
||||
| **PostgreSQL** | `localhost:5432` | `trading_user` / `trading_pass_dev` | Operational database |
|
||||
| **QuestDB Console** | http://localhost:9000 | None | Time-series database GUI |
|
||||
| **QuestDB Wire** | `localhost:8812` | `admin` / `quest` | PostgreSQL protocol access |
|
||||
| **Redis Insight** | http://localhost:8001 | None | Dragonfly/Redis management |
|
||||
| **PgAdmin** | http://localhost:8080 | `admin@tradingbot.local` / `admin123` | PostgreSQL management |
|
||||
| **Prometheus** | http://localhost:9090 | None | Metrics collection |
|
||||
| **Grafana** | http://localhost:3000 | `admin` / `admin123` | Dashboards & alerts |
|
||||
|
||||
## 🛠️ Management Commands
|
||||
|
||||
### Using NPM Scripts (Recommended)
|
||||
```powershell
|
||||
# Infrastructure management
|
||||
npm run docker:start # Start core services
|
||||
npm run docker:stop # Stop all services
|
||||
npm run docker:restart # Restart services
|
||||
npm run docker:status # Show service status
|
||||
npm run docker:logs # Show logs
|
||||
npm run docker:reset # Reset all data (destructive)
|
||||
|
||||
# Additional services
|
||||
npm run docker:admin # Start admin interfaces
|
||||
npm run docker:monitoring # Start monitoring stack
|
||||
|
||||
# Quick development
|
||||
npm run dev:full # Infrastructure + admin + services
|
||||
npm run dev:clean # Reset + full restart
|
||||
```
|
||||
|
||||
### Using PowerShell Script Directly
|
||||
```powershell
|
||||
# Basic operations
|
||||
./scripts/docker.ps1 start
|
||||
./scripts/docker.ps1 stop
|
||||
./scripts/docker.ps1 status
|
||||
|
||||
# View logs for specific service
|
||||
./scripts/docker.ps1 logs -Service dragonfly
|
||||
|
||||
# Development mode (lighter configuration)
|
||||
./scripts/docker.ps1 start -Dev
|
||||
|
||||
# Get help
|
||||
./scripts/docker.ps1 help
|
||||
```
|
||||
|
||||
### Using Docker Compose Directly
|
||||
```powershell
|
||||
# Start core services
|
||||
docker-compose up -d dragonfly postgres questdb
|
||||
|
||||
# Start with development overrides
|
||||
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
|
||||
|
||||
# Start monitoring (optional)
|
||||
docker-compose --profile monitoring up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop everything
|
||||
docker-compose down
|
||||
|
||||
# Reset all data
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
## 🗃️ Database Schemas
|
||||
|
||||
### PostgreSQL Structure
|
||||
```sql
|
||||
trading.* -- Orders, positions, executions, accounts
|
||||
strategy.* -- Strategies, signals, performance
|
||||
risk.* -- Risk limits, events, monitoring
|
||||
audit.* -- System events, health, configuration
|
||||
```
|
||||
|
||||
### Initial Data
|
||||
The PostgreSQL database is automatically initialized with:
|
||||
- Common stock symbols (AAPL, GOOGL, etc.)
|
||||
- Demo trading account with $100k
|
||||
- Sample mean reversion strategy
|
||||
- Basic risk limits
|
||||
- Audit triggers for data tracking
|
||||
|
||||
## 📈 Monitoring & Observability
|
||||
|
||||
### Prometheus Metrics
|
||||
The setup includes Prometheus configuration to monitor:
|
||||
- Trading bot services (ports 3001, 3002, 4001)
|
||||
- Dragonfly performance
|
||||
- PostgreSQL health
|
||||
- QuestDB metrics
|
||||
|
||||
### Grafana Dashboards
|
||||
Access Grafana at http://localhost:3000 to view:
|
||||
- Trading system performance
|
||||
- Database metrics
|
||||
- Service health
|
||||
- Risk monitoring
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
### Environment Variables
|
||||
Services use these environment variables (configured in docker-compose.yml):
|
||||
|
||||
```yaml
|
||||
# Dragonfly
|
||||
DRAGONFLY_HOST=localhost
|
||||
DRAGONFLY_PORT=6379
|
||||
|
||||
# PostgreSQL
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=trading_bot
|
||||
POSTGRES_USER=trading_user
|
||||
POSTGRES_PASSWORD=trading_pass_dev
|
||||
|
||||
# QuestDB
|
||||
QUESTDB_HOST=localhost
|
||||
QUESTDB_PORT=8812
|
||||
QUESTDB_DB=qdb
|
||||
```
|
||||
|
||||
### Development vs Production
|
||||
- **Development**: Use `docker-compose.dev.yml` for lighter resource usage
|
||||
- **Production**: Use base `docker-compose.yml` with proper secrets management
|
||||
|
||||
## 🚨 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Port Conflicts**
|
||||
```powershell
|
||||
# Check what's using a port
|
||||
netstat -ano | findstr :6379
|
||||
|
||||
# Kill process if needed
|
||||
taskkill /PID <process_id> /F
|
||||
```
|
||||
|
||||
**Permission Issues**
|
||||
```powershell
|
||||
# Ensure Docker is running as administrator
|
||||
# Check Docker Desktop settings
|
||||
```
|
||||
|
||||
**Data Corruption**
|
||||
```powershell
|
||||
# Reset all data and restart fresh
|
||||
npm run docker:reset
|
||||
```
|
||||
|
||||
**Service Won't Start**
|
||||
```powershell
|
||||
# Check logs for specific service
|
||||
npm run docker:logs
|
||||
./scripts/docker.ps1 logs -Service dragonfly
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
All services include health checks. Monitor with:
|
||||
```powershell
|
||||
npm run docker:status
|
||||
docker ps --filter "name=trading-bot"
|
||||
```
|
||||
|
||||
## 🔒 Security Notes
|
||||
|
||||
### Development Security
|
||||
- Default passwords are for development only
|
||||
- No SSL/TLS in development mode
|
||||
- Services exposed on localhost only
|
||||
|
||||
### Production Considerations
|
||||
- Change all default passwords
|
||||
- Enable SSL/TLS encryption
|
||||
- Use Docker secrets for sensitive data
|
||||
- Implement proper backup strategies
|
||||
- Set up log rotation
|
||||
- Configure firewall rules
|
||||
|
||||
## 📋 Maintenance
|
||||
|
||||
### Backup Data
|
||||
```powershell
|
||||
# Backup PostgreSQL
|
||||
docker exec trading-bot-postgres pg_dump -U trading_user trading_bot > backup.sql
|
||||
|
||||
# Backup Dragonfly (if persistence enabled)
|
||||
docker exec trading-bot-dragonfly redis-cli BGSAVE
|
||||
```
|
||||
|
||||
### Update Services
|
||||
```powershell
|
||||
# Pull latest images
|
||||
docker-compose pull
|
||||
|
||||
# Restart with new images
|
||||
npm run docker:restart
|
||||
```
|
||||
|
||||
### Clean Up
|
||||
```powershell
|
||||
# Remove unused containers and images
|
||||
docker system prune
|
||||
|
||||
# Remove all trading-bot related containers and volumes
|
||||
docker-compose down -v --remove-orphans
|
||||
```
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
1. **Start the infrastructure**: `npm run docker:start`
|
||||
2. **Open admin interfaces**: `npm run docker:admin`
|
||||
3. **Configure your services**: Update .env files to use Docker services
|
||||
4. **Start development**: `npm run dev:full`
|
||||
5. **Monitor health**: Check http://localhost:8001 and http://localhost:8080
|
||||
|
||||
For more advanced configuration, see the individual service documentation in the `database/` and `monitoring/` directories.
|
||||
180
README.md
Normal file
180
README.md
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
# 🤖 Stock Bot Trading System
|
||||
|
||||
A comprehensive trading bot built with Bun and Turborepo, featuring a service-oriented architecture for real-time market data processing and strategy execution.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
- [Bun](https://bun.sh/) runtime
|
||||
- Node.js 18+ (for compatibility)
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
# Clone and install dependencies
|
||||
git clone <your-repo-url>
|
||||
cd stock-bot
|
||||
bun install
|
||||
```
|
||||
|
||||
### Running the System
|
||||
|
||||
#### Option 1: VS Code Tasks (Recommended)
|
||||
1. Open the project in VS Code
|
||||
2. Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
|
||||
3. Type "Tasks: Run Task" and select it
|
||||
4. Choose "Start All Services"
|
||||
|
||||
#### Option 2: Manual Startup
|
||||
```bash
|
||||
# Terminal 1: Start Market Data Gateway
|
||||
cd apps/core-services/market-data-gateway
|
||||
bun run dev
|
||||
|
||||
# Terminal 2: Start Trading Dashboard
|
||||
cd apps/interface-services/trading-dashboard
|
||||
bun run dev
|
||||
```
|
||||
|
||||
### Access Points
|
||||
- **Trading Dashboard**: http://localhost:5173
|
||||
- **Market Data API**: http://localhost:3001
|
||||
- **Health Check**: http://localhost:3001/health
|
||||
|
||||
## 📊 Dashboard Features
|
||||
|
||||
### Real-time Market Data
|
||||
- Live price feeds for AAPL, GOOGL, MSFT, TSLA, AMZN
|
||||
- WebSocket connections for real-time updates
|
||||
- Service health monitoring
|
||||
|
||||
### Professional UI Components
|
||||
- Built with Tremor UI for financial visualizations
|
||||
- Interactive charts and metrics
|
||||
- Responsive design for all devices
|
||||
|
||||
### Dashboard Tabs
|
||||
1. **Market Data**: Live prices, volume, bid/ask spreads
|
||||
2. **Portfolio**: Holdings allocation and performance
|
||||
3. **Charts**: Price and volume analysis
|
||||
4. **Performance**: Trading metrics and statistics
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
### Service-Oriented Design
|
||||
```
|
||||
apps/
|
||||
├── core-services/
|
||||
│ └── market-data-gateway/ # Market data ingestion
|
||||
├── interface-services/
|
||||
│ └── trading-dashboard/ # React dashboard
|
||||
├── data-services/ # (Future) Data processing
|
||||
├── execution-services/ # (Future) Order management
|
||||
├── intelligence-services/ # (Future) Strategy engine
|
||||
├── platform-services/ # (Future) Infrastructure
|
||||
└── integration-services/ # (Future) External APIs
|
||||
```
|
||||
|
||||
### Shared Packages
|
||||
```
|
||||
packages/
|
||||
├── shared-types/ # TypeScript definitions
|
||||
├── config/ # Configuration management
|
||||
├── database/ # (Future) Database utilities
|
||||
└── trading-core/ # (Future) Core trading logic
|
||||
```
|
||||
|
||||
## 🔧 Development
|
||||
|
||||
### Project Structure
|
||||
- **Turborepo**: Monorepo management
|
||||
- **Bun**: Package manager and runtime
|
||||
- **TypeScript**: Type safety across all services
|
||||
- **React + Vite**: Modern frontend development
|
||||
- **Tremor UI**: Financial dashboard components
|
||||
|
||||
### Key Technologies
|
||||
- **Backend**: Hono framework, WebSockets, Redis
|
||||
- **Frontend**: React, TypeScript, Tremor UI
|
||||
- **Data**: QuestDB (planned), PostgreSQL (planned)
|
||||
- **Deployment**: Docker, Kubernetes (planned)
|
||||
|
||||
## 📈 Current Status
|
||||
|
||||
### ✅ Completed
|
||||
- [x] Monorepo setup with Turborepo
|
||||
- [x] Market Data Gateway service
|
||||
- [x] Real-time WebSocket connections
|
||||
- [x] Professional React dashboard
|
||||
- [x] Tremor UI integration
|
||||
- [x] TypeScript type system
|
||||
- [x] Service health monitoring
|
||||
|
||||
### 🚧 In Progress
|
||||
- [ ] Strategy execution engine
|
||||
- [ ] Risk management system
|
||||
- [ ] Portfolio tracking
|
||||
- [ ] Real broker integration
|
||||
|
||||
### 🔮 Planned
|
||||
- [ ] Advanced charting
|
||||
- [ ] Backtesting framework
|
||||
- [ ] Machine learning signals
|
||||
- [ ] Multi-broker support
|
||||
- [ ] Mobile application
|
||||
|
||||
## 🛠️ API Endpoints
|
||||
|
||||
### Market Data Gateway (Port 3001)
|
||||
```
|
||||
GET /health # Service health check
|
||||
GET /api/market-data/:symbol # Current market data
|
||||
GET /api/ohlcv/:symbol # Historical OHLCV data
|
||||
WS ws://localhost:3001 # Real-time data stream
|
||||
```
|
||||
|
||||
### Data Format
|
||||
```typescript
|
||||
interface MarketData {
|
||||
symbol: string;
|
||||
price: number;
|
||||
bid: number;
|
||||
ask: number;
|
||||
volume: number;
|
||||
timestamp: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
Environment variables are managed in `.env`:
|
||||
```bash
|
||||
# Database Configuration
|
||||
DATABASE_URL=postgresql://...
|
||||
QUESTDB_URL=http://localhost:9000
|
||||
|
||||
# External APIs
|
||||
ALPHA_VANTAGE_API_KEY=your_key_here
|
||||
ALPACA_API_KEY=your_key_here
|
||||
|
||||
# Service Configuration
|
||||
NODE_ENV=development
|
||||
LOG_LEVEL=info
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch: `git checkout -b feature/amazing-feature`
|
||||
3. Commit changes: `git commit -m 'Add amazing feature'`
|
||||
4. Push to branch: `git push origin feature/amazing-feature`
|
||||
5. Open a Pull Request
|
||||
|
||||
## 📝 License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## 🙏 Acknowledgments
|
||||
|
||||
- [Tremor UI](https://tremor.so/) for beautiful financial components
|
||||
- [Bun](https://bun.sh/) for fast runtime and package management
|
||||
- [Turborepo](https://turbo.build/) for monorepo tooling
|
||||
149
SETUP-COMPLETE.md
Normal file
149
SETUP-COMPLETE.md
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# 🚀 Trading Bot Docker Infrastructure Setup Complete!
|
||||
|
||||
Your Docker infrastructure has been successfully configured. Here's what you have:
|
||||
|
||||
## 📦 What's Included
|
||||
|
||||
### Core Services
|
||||
- **🐉 Dragonfly**: Redis-compatible cache and event streaming (Port 6379)
|
||||
- **🐘 PostgreSQL**: Operational database with complete trading schema (Port 5432)
|
||||
- **📊 QuestDB**: Time-series database for market data (Ports 9000, 8812, 9009)
|
||||
|
||||
### Admin Tools
|
||||
- **🔧 Redis Insight**: Dragonfly management GUI (Port 8001)
|
||||
- **🛠️ PgAdmin**: PostgreSQL administration (Port 8080)
|
||||
|
||||
### Monitoring (Optional)
|
||||
- **📈 Prometheus**: Metrics collection (Port 9090)
|
||||
- **📊 Grafana**: Dashboards and alerting (Port 3000)
|
||||
|
||||
## 🏁 Getting Started
|
||||
|
||||
### Step 1: Start Docker Desktop
|
||||
Make sure Docker Desktop is running on your Windows machine.
|
||||
|
||||
### Step 2: Start Infrastructure
|
||||
```powershell
|
||||
# Quick start - core services only
|
||||
npm run infra:up
|
||||
|
||||
# Or with management script
|
||||
npm run docker:start
|
||||
|
||||
# Full development environment
|
||||
npm run dev:full
|
||||
```
|
||||
|
||||
### Step 3: Access Admin Interfaces
|
||||
```powershell
|
||||
# Start admin tools
|
||||
npm run docker:admin
|
||||
```
|
||||
|
||||
## 🔗 Access URLs
|
||||
|
||||
Once running, access these services:
|
||||
|
||||
| Service | URL | Login |
|
||||
|---------|-----|-------|
|
||||
| **QuestDB Console** | http://localhost:9000 | No login required |
|
||||
| **Redis Insight** | http://localhost:8001 | No login required |
|
||||
| **PgAdmin** | http://localhost:8080 | `admin@tradingbot.local` / `admin123` |
|
||||
| **Prometheus** | http://localhost:9090 | No login required |
|
||||
| **Grafana** | http://localhost:3000 | `admin` / `admin123` |
|
||||
|
||||
## 📊 Database Connections
|
||||
|
||||
### From Your Trading Services
|
||||
Update your `.env` file:
|
||||
```env
|
||||
# Dragonfly (Redis replacement)
|
||||
DRAGONFLY_HOST=localhost
|
||||
DRAGONFLY_PORT=6379
|
||||
|
||||
# PostgreSQL
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_DB=trading_bot
|
||||
POSTGRES_USER=trading_user
|
||||
POSTGRES_PASSWORD=trading_pass_dev
|
||||
|
||||
# QuestDB
|
||||
QUESTDB_HOST=localhost
|
||||
QUESTDB_PORT=8812
|
||||
QUESTDB_DB=qdb
|
||||
```
|
||||
|
||||
### Database Schema
|
||||
PostgreSQL includes these pre-configured schemas:
|
||||
- `trading.*` - Orders, positions, executions, accounts
|
||||
- `strategy.*` - Strategies, signals, performance metrics
|
||||
- `risk.*` - Risk limits, events, monitoring
|
||||
- `audit.*` - System events, health checks, configuration
|
||||
|
||||
## 🛠️ Management Commands
|
||||
|
||||
```powershell
|
||||
# Basic operations
|
||||
npm run docker:start # Start core services
|
||||
npm run docker:stop # Stop all services
|
||||
npm run docker:status # Check service status
|
||||
npm run docker:logs # View all logs
|
||||
npm run docker:reset # Reset all data (destructive!)
|
||||
|
||||
# Additional services
|
||||
npm run docker:admin # Start admin interfaces
|
||||
npm run docker:monitoring # Start Prometheus & Grafana
|
||||
|
||||
# Development workflows
|
||||
npm run dev:full # Infrastructure + admin + your services
|
||||
npm run dev:clean # Reset + restart everything
|
||||
|
||||
# Direct PowerShell script access
|
||||
./scripts/docker.ps1 start
|
||||
./scripts/docker.ps1 logs -Service dragonfly
|
||||
./scripts/docker.ps1 help
|
||||
```
|
||||
|
||||
## ✅ Next Steps
|
||||
|
||||
1. **Start Docker Desktop** if not already running
|
||||
2. **Run**: `npm run docker:start` to start core infrastructure
|
||||
3. **Run**: `npm run docker:admin` to start admin tools
|
||||
4. **Update** your environment variables to use the Docker services
|
||||
5. **Test** Dragonfly connection in your EventPublisher service
|
||||
6. **Verify** database schema in PgAdmin
|
||||
7. **Start** your trading services with the new infrastructure
|
||||
|
||||
## 🎯 Ready for Integration
|
||||
|
||||
Your EventPublisher service is already configured to use Dragonfly. The infrastructure supports:
|
||||
|
||||
- ✅ **Event Streaming**: Dragonfly handles Redis Streams for real-time events
|
||||
- ✅ **Caching**: High-performance caching with better memory efficiency
|
||||
- ✅ **Operational Data**: PostgreSQL with complete trading schemas
|
||||
- ✅ **Time-Series Data**: QuestDB for market data and analytics
|
||||
- ✅ **Monitoring**: Full observability stack ready
|
||||
- ✅ **Admin Tools**: Web-based management interfaces
|
||||
|
||||
The system is designed to scale from development to production with the same Docker configuration.
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
If you encounter issues:
|
||||
```powershell
|
||||
# Check Docker status
|
||||
docker --version
|
||||
docker-compose --version
|
||||
|
||||
# Verify services
|
||||
npm run docker:status
|
||||
|
||||
# View specific service logs
|
||||
./scripts/docker.ps1 logs -Service dragonfly
|
||||
|
||||
# Reset if needed
|
||||
npm run docker:reset
|
||||
```
|
||||
|
||||
**Happy Trading! 🚀📈**
|
||||
20
apps/core-services/market-data-gateway/package.json
Normal file
20
apps/core-services/market-data-gateway/package.json
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"name": "market-data-gateway",
|
||||
"version": "1.0.0",
|
||||
"description": "Market data ingestion service",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"dev": "bun run --watch src/index.ts",
|
||||
"start": "bun run src/index.ts",
|
||||
"test": "echo 'No tests yet'"
|
||||
}, "dependencies": {
|
||||
"hono": "^4.6.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"ws": "^8.18.0"
|
||||
}, "devDependencies": {
|
||||
"bun-types": "^1.2.15",
|
||||
"@types/ws": "^8.5.12"
|
||||
}
|
||||
}
|
||||
83
apps/core-services/market-data-gateway/src/index.ts
Normal file
83
apps/core-services/market-data-gateway/src/index.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { Hono } from 'hono';
|
||||
import { serve } from 'bun';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Health check endpoint
|
||||
app.get('/health', (c) => {
|
||||
return c.json({
|
||||
service: 'market-data-gateway',
|
||||
status: 'healthy',
|
||||
timestamp: new Date(),
|
||||
version: '1.0.0'
|
||||
});
|
||||
});
|
||||
|
||||
// Demo market data endpoint
|
||||
app.get('/api/market-data/:symbol', (c) => {
|
||||
const symbol = c.req.param('symbol');
|
||||
|
||||
// Generate demo data
|
||||
const demoData = {
|
||||
symbol: symbol.toUpperCase(),
|
||||
price: 150 + Math.random() * 50, // Random price between 150-200
|
||||
bid: 149.99,
|
||||
ask: 150.01,
|
||||
volume: Math.floor(Math.random() * 1000000),
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data: demoData,
|
||||
timestamp: new Date()
|
||||
});
|
||||
});
|
||||
|
||||
// Demo OHLCV endpoint
|
||||
app.get('/api/ohlcv/:symbol', (c) => {
|
||||
const symbol = c.req.param('symbol');
|
||||
const limit = parseInt(c.req.query('limit') || '10');
|
||||
|
||||
// Generate demo OHLCV data
|
||||
const data = [];
|
||||
let basePrice = 150;
|
||||
|
||||
for (let i = limit - 1; i >= 0; i--) {
|
||||
const open = basePrice + (Math.random() - 0.5) * 10;
|
||||
const close = open + (Math.random() - 0.5) * 5;
|
||||
const high = Math.max(open, close) + Math.random() * 3;
|
||||
const low = Math.min(open, close) - Math.random() * 3;
|
||||
|
||||
data.push({
|
||||
symbol: symbol.toUpperCase(),
|
||||
timestamp: new Date(Date.now() - i * 60000), // 1 minute intervals
|
||||
open: Math.round(open * 100) / 100,
|
||||
high: Math.round(high * 100) / 100,
|
||||
low: Math.round(low * 100) / 100,
|
||||
close: Math.round(close * 100) / 100,
|
||||
volume: Math.floor(Math.random() * 50000) + 10000
|
||||
});
|
||||
|
||||
basePrice = close;
|
||||
}
|
||||
|
||||
return c.json({
|
||||
success: true,
|
||||
data,
|
||||
timestamp: new Date()
|
||||
});
|
||||
});
|
||||
|
||||
const PORT = 3001;
|
||||
|
||||
console.log(`🚀 Market Data Gateway starting on port ${PORT}`);
|
||||
|
||||
serve({
|
||||
port: PORT,
|
||||
fetch: app.fetch,
|
||||
});
|
||||
|
||||
console.log(`📊 Market Data Gateway running on http://localhost:${PORT}`);
|
||||
console.log(`🔍 Health check: http://localhost:${PORT}/health`);
|
||||
console.log(`📈 Demo data: http://localhost:${PORT}/api/market-data/AAPL`);
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
import type { MarketData, OHLCV } from '@stock-bot/shared-types';
|
||||
import { dataProviderConfigs } from '@stock-bot/config';
|
||||
|
||||
export class DataNormalizer {
|
||||
/**
|
||||
* Normalize market data from different providers to our standard format
|
||||
*/
|
||||
normalizeMarketData(rawData: any, source: string): MarketData {
|
||||
switch (source) {
|
||||
case 'alpha-vantage':
|
||||
return this.normalizeAlphaVantage(rawData);
|
||||
case 'yahoo-finance':
|
||||
return this.normalizeYahooFinance(rawData);
|
||||
default:
|
||||
throw new Error(`Unsupported data source: ${source}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize OHLCV data from different providers
|
||||
*/
|
||||
normalizeOHLCV(rawData: any, source: string): OHLCV[] {
|
||||
switch (source) {
|
||||
case 'alpha-vantage':
|
||||
return this.normalizeAlphaVantageOHLCV(rawData);
|
||||
case 'yahoo-finance':
|
||||
return this.normalizeYahooFinanceOHLCV(rawData);
|
||||
default:
|
||||
throw new Error(`Unsupported data source: ${source}`);
|
||||
}
|
||||
}
|
||||
|
||||
private normalizeAlphaVantage(data: any): MarketData {
|
||||
const quote = data['Global Quote'];
|
||||
return {
|
||||
symbol: quote['01. symbol'],
|
||||
price: parseFloat(quote['05. price']),
|
||||
bid: parseFloat(quote['05. price']) - 0.01, // Approximate bid/ask
|
||||
ask: parseFloat(quote['05. price']) + 0.01,
|
||||
volume: parseInt(quote['06. volume']),
|
||||
timestamp: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeYahooFinance(data: any): MarketData {
|
||||
return {
|
||||
symbol: data.symbol,
|
||||
price: data.regularMarketPrice,
|
||||
bid: data.bid || data.regularMarketPrice - 0.01,
|
||||
ask: data.ask || data.regularMarketPrice + 0.01,
|
||||
volume: data.regularMarketVolume,
|
||||
timestamp: new Date(data.regularMarketTime * 1000),
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeAlphaVantageOHLCV(data: any): OHLCV[] {
|
||||
const timeSeries = data['Time Series (1min)'] || data['Time Series (5min)'] || data['Time Series (Daily)'];
|
||||
const symbol = data['Meta Data']['2. Symbol'];
|
||||
|
||||
return Object.entries(timeSeries).map(([timestamp, values]: [string, any]) => ({
|
||||
symbol,
|
||||
timestamp: new Date(timestamp),
|
||||
open: parseFloat(values['1. open']),
|
||||
high: parseFloat(values['2. high']),
|
||||
low: parseFloat(values['3. low']),
|
||||
close: parseFloat(values['4. close']),
|
||||
volume: parseInt(values['5. volume']),
|
||||
})).sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
||||
}
|
||||
|
||||
private normalizeYahooFinanceOHLCV(data: any): OHLCV[] {
|
||||
const result = data.chart.result[0];
|
||||
const timestamps = result.timestamp;
|
||||
const quotes = result.indicators.quote[0];
|
||||
|
||||
return timestamps.map((timestamp: number, index: number) => ({
|
||||
symbol: result.meta.symbol,
|
||||
timestamp: new Date(timestamp * 1000),
|
||||
open: quotes.open[index],
|
||||
high: quotes.high[index],
|
||||
low: quotes.low[index],
|
||||
close: quotes.close[index],
|
||||
volume: quotes.volume[index],
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate market data quality
|
||||
*/
|
||||
validateMarketData(data: MarketData): boolean {
|
||||
return (
|
||||
data.symbol &&
|
||||
typeof data.price === 'number' &&
|
||||
data.price > 0 &&
|
||||
typeof data.volume === 'number' &&
|
||||
data.volume >= 0 &&
|
||||
data.timestamp instanceof Date
|
||||
) as boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate OHLCV data quality
|
||||
*/
|
||||
validateOHLCV(data: OHLCV): boolean {
|
||||
return (
|
||||
data.symbol &&
|
||||
typeof data.open === 'number' && data.open > 0 &&
|
||||
typeof data.high === 'number' && data.high > 0 &&
|
||||
typeof data.low === 'number' && data.low > 0 &&
|
||||
typeof data.close === 'number' && data.close > 0 &&
|
||||
data.high >= Math.max(data.open, data.close) &&
|
||||
data.low <= Math.min(data.open, data.close) &&
|
||||
typeof data.volume === 'number' && data.volume >= 0 &&
|
||||
data.timestamp instanceof Date
|
||||
) as boolean;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
import Redis from 'ioredis';
|
||||
import { databaseConfig } from '@stock-bot/config';
|
||||
import type { MarketDataEvent, SignalEvent, TradingEvent } from '@stock-bot/shared-types';
|
||||
|
||||
export class EventPublisher {
|
||||
private dragonfly: Redis;
|
||||
private readonly STREAM_NAME = 'trading-events'; constructor() {
|
||||
this.dragonfly = new Redis({
|
||||
host: databaseConfig.dragonfly.host,
|
||||
port: databaseConfig.dragonfly.port,
|
||||
password: databaseConfig.dragonfly.password,
|
||||
maxRetriesPerRequest: 3,
|
||||
});
|
||||
|
||||
this.dragonfly.on('connect', () => {
|
||||
console.log('🐉 Connected to Dragonfly for event publishing');
|
||||
});
|
||||
|
||||
this.dragonfly.on('error', (error) => {
|
||||
console.error('❌ Dragonfly connection error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish a market data event to the event stream
|
||||
*/ async publishMarketData(event: MarketDataEvent): Promise<void> {
|
||||
try {
|
||||
await this.dragonfly.xadd(
|
||||
this.STREAM_NAME,
|
||||
'*',
|
||||
'type', event.type,
|
||||
'data', JSON.stringify(event.data),
|
||||
'timestamp', event.timestamp.toISOString()
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error publishing market data event:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Publish a trading signal event
|
||||
*/
|
||||
async publishSignal(event: SignalEvent): Promise<void> {
|
||||
try {
|
||||
await this.dragonfly.xadd(
|
||||
this.STREAM_NAME,
|
||||
'*',
|
||||
'type', event.type,
|
||||
'signal', JSON.stringify(event.signal),
|
||||
'timestamp', event.timestamp.toISOString()
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Error publishing signal event:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish any trading event
|
||||
*/
|
||||
async publishEvent(event: TradingEvent): Promise<void> {
|
||||
try {
|
||||
const fields: string[] = ['type', event.type, 'timestamp', event.timestamp.toISOString()];
|
||||
|
||||
if ('data' in event) {
|
||||
fields.push('data', JSON.stringify(event.data));
|
||||
}
|
||||
if ('order' in event) {
|
||||
fields.push('order', JSON.stringify(event.order));
|
||||
}
|
||||
if ('signal' in event) {
|
||||
fields.push('signal', JSON.stringify(event.signal));
|
||||
}
|
||||
|
||||
await this.dragonfly.xadd(this.STREAM_NAME, '*', ...fields);
|
||||
} catch (error) {
|
||||
console.error('Error publishing event:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cache market data in Dragonfly for quick access
|
||||
*/
|
||||
async cacheMarketData(symbol: string, data: any, ttl: number = 60): Promise<void> {
|
||||
try {
|
||||
const key = `market-data:${symbol}`;
|
||||
await this.dragonfly.setex(key, ttl, JSON.stringify(data));
|
||||
} catch (error) {
|
||||
console.error('Error caching market data:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get cached market data from Dragonfly
|
||||
*/
|
||||
async getCachedMarketData(symbol: string): Promise<any | null> {
|
||||
try {
|
||||
const key = `market-data:${symbol}`;
|
||||
const cached = await this.dragonfly.get(key);
|
||||
return cached ? JSON.parse(cached) : null;
|
||||
} catch (error) {
|
||||
console.error('Error getting cached market data:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Publish to a specific channel for real-time subscriptions
|
||||
*/
|
||||
async publishToChannel(channel: string, data: any): Promise<void> {
|
||||
try {
|
||||
await this.dragonfly.publish(channel, JSON.stringify(data));
|
||||
} catch (error) {
|
||||
console.error(`Error publishing to channel ${channel}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Set up health monitoring
|
||||
*/
|
||||
async setServiceHealth(serviceName: string, status: 'healthy' | 'unhealthy'): Promise<void> {
|
||||
try {
|
||||
const key = `health:${serviceName}`;
|
||||
const healthData = {
|
||||
status,
|
||||
timestamp: new Date().toISOString(),
|
||||
lastSeen: Date.now(),
|
||||
};
|
||||
await this.dragonfly.setex(key, 300, JSON.stringify(healthData)); // 5 minutes TTL
|
||||
} catch (error) {
|
||||
console.error('Error setting service health:', error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Close Dragonfly connection
|
||||
*/
|
||||
async disconnect(): Promise<void> {
|
||||
await this.dragonfly.quit();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
import type { MarketData, OHLCV, MarketDataEvent } from '@stock-bot/shared-types';
|
||||
import { dataProviderConfigs } from '@stock-bot/config';
|
||||
import { EventPublisher } from './EventPublisher';
|
||||
import { DataNormalizer } from './DataNormalizer';
|
||||
|
||||
export class MarketDataService {
|
||||
private wsClients: Set<any> = new Set();
|
||||
private subscriptions: Map<string, Set<any>> = new Map();
|
||||
private dataUpdateInterval: Timer | null = null;
|
||||
private readonly UPDATE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
constructor(
|
||||
private eventPublisher: EventPublisher,
|
||||
private dataNormalizer: DataNormalizer
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Initialize the market data service
|
||||
*/
|
||||
async initialize(): Promise<void> {
|
||||
console.log('🔄 Initializing Market Data Service...');
|
||||
|
||||
// Set up periodic data updates for demo purposes
|
||||
this.startDataUpdates();
|
||||
|
||||
// Set service health
|
||||
await this.eventPublisher.setServiceHealth('market-data-gateway', 'healthy');
|
||||
|
||||
console.log('✅ Market Data Service initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get latest market data for a symbol
|
||||
*/
|
||||
async getLatestData(symbol: string): Promise<MarketData> {
|
||||
// First check cache
|
||||
const cached = await this.eventPublisher.getCachedMarketData(symbol);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Fetch fresh data (using demo data for now)
|
||||
const marketData = this.generateDemoData(symbol);
|
||||
|
||||
// Cache the data
|
||||
await this.eventPublisher.cacheMarketData(symbol, marketData, 60);
|
||||
|
||||
// Publish market data event
|
||||
const event: MarketDataEvent = {
|
||||
type: 'MARKET_DATA',
|
||||
data: marketData,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
await this.eventPublisher.publishMarketData(event);
|
||||
|
||||
return marketData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OHLCV data for a symbol
|
||||
*/
|
||||
async getOHLCV(symbol: string, interval: string, limit: number): Promise<OHLCV[]> {
|
||||
// Generate demo OHLCV data
|
||||
const ohlcvData = this.generateDemoOHLCV(symbol, limit);
|
||||
|
||||
// Cache the data
|
||||
await this.eventPublisher.cacheMarketData(`ohlcv:${symbol}:${interval}`, ohlcvData, 300);
|
||||
|
||||
return ohlcvData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add WebSocket client for real-time updates
|
||||
*/
|
||||
addWebSocketClient(ws: any): void {
|
||||
this.wsClients.add(ws);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove WebSocket client
|
||||
*/
|
||||
removeWebSocketClient(ws: any): void {
|
||||
this.wsClients.delete(ws);
|
||||
|
||||
// Remove from all subscriptions
|
||||
for (const [symbol, clients] of this.subscriptions) {
|
||||
clients.delete(ws);
|
||||
if (clients.size === 0) {
|
||||
this.subscriptions.delete(symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle WebSocket messages
|
||||
*/
|
||||
handleWebSocketMessage(ws: any, data: any): void {
|
||||
try {
|
||||
const message = typeof data === 'string' ? JSON.parse(data) : data;
|
||||
|
||||
switch (message.type) {
|
||||
case 'subscribe':
|
||||
this.subscribeToSymbol(ws, message.symbol);
|
||||
break;
|
||||
case 'unsubscribe':
|
||||
this.unsubscribeFromSymbol(ws, message.symbol);
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown WebSocket message type:', message.type);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error handling WebSocket message:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe WebSocket client to symbol updates
|
||||
*/
|
||||
private subscribeToSymbol(ws: any, symbol: string): void {
|
||||
if (!this.subscriptions.has(symbol)) {
|
||||
this.subscriptions.set(symbol, new Set());
|
||||
}
|
||||
this.subscriptions.get(symbol)!.add(ws);
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'subscribed',
|
||||
symbol,
|
||||
timestamp: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe WebSocket client from symbol updates
|
||||
*/
|
||||
private unsubscribeFromSymbol(ws: any, symbol: string): void {
|
||||
const clients = this.subscriptions.get(symbol);
|
||||
if (clients) {
|
||||
clients.delete(ws);
|
||||
if (clients.size === 0) {
|
||||
this.subscriptions.delete(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
ws.send(JSON.stringify({
|
||||
type: 'unsubscribed',
|
||||
symbol,
|
||||
timestamp: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start periodic data updates for demo
|
||||
*/
|
||||
private startDataUpdates(): void {
|
||||
this.dataUpdateInterval = setInterval(async () => {
|
||||
const symbols = ['AAPL', 'GOOGL', 'MSFT', 'TSLA', 'AMZN'];
|
||||
|
||||
for (const symbol of symbols) {
|
||||
if (this.subscriptions.has(symbol)) {
|
||||
const marketData = this.generateDemoData(symbol);
|
||||
|
||||
// Send to subscribed WebSocket clients
|
||||
const clients = this.subscriptions.get(symbol)!;
|
||||
const message = JSON.stringify({
|
||||
type: 'market_data',
|
||||
data: marketData,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
for (const client of clients) {
|
||||
try {
|
||||
client.send(message);
|
||||
} catch (error) {
|
||||
console.error('Error sending WebSocket message:', error);
|
||||
clients.delete(client);
|
||||
}
|
||||
}
|
||||
|
||||
// Publish event
|
||||
const event: MarketDataEvent = {
|
||||
type: 'MARKET_DATA',
|
||||
data: marketData,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
await this.eventPublisher.publishMarketData(event);
|
||||
}
|
||||
}
|
||||
}, this.UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate demo market data
|
||||
*/
|
||||
private generateDemoData(symbol: string): MarketData {
|
||||
const basePrice = this.getBasePrice(symbol);
|
||||
const variation = (Math.random() - 0.5) * 0.02; // ±1% variation
|
||||
const price = basePrice * (1 + variation);
|
||||
|
||||
return {
|
||||
symbol,
|
||||
price: Math.round(price * 100) / 100,
|
||||
bid: Math.round((price - 0.01) * 100) / 100,
|
||||
ask: Math.round((price + 0.01) * 100) / 100,
|
||||
volume: Math.floor(Math.random() * 1000000) + 100000,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate demo OHLCV data
|
||||
*/
|
||||
private generateDemoOHLCV(symbol: string, limit: number): OHLCV[] {
|
||||
const basePrice = this.getBasePrice(symbol);
|
||||
const data: OHLCV[] = [];
|
||||
let currentPrice = basePrice;
|
||||
|
||||
for (let i = limit - 1; i >= 0; i--) {
|
||||
const variation = (Math.random() - 0.5) * 0.05; // ±2.5% variation
|
||||
const open = currentPrice;
|
||||
const close = open * (1 + variation);
|
||||
const high = Math.max(open, close) * (1 + Math.random() * 0.02);
|
||||
const low = Math.min(open, close) * (1 - Math.random() * 0.02);
|
||||
|
||||
data.push({
|
||||
symbol,
|
||||
timestamp: new Date(Date.now() - i * 60000), // 1 minute intervals
|
||||
open: Math.round(open * 100) / 100,
|
||||
high: Math.round(high * 100) / 100,
|
||||
low: Math.round(low * 100) / 100,
|
||||
close: Math.round(close * 100) / 100,
|
||||
volume: Math.floor(Math.random() * 50000) + 10000,
|
||||
});
|
||||
|
||||
currentPrice = close;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get base price for demo data
|
||||
*/
|
||||
private getBasePrice(symbol: string): number {
|
||||
const prices: Record<string, number> = {
|
||||
'AAPL': 175.50,
|
||||
'GOOGL': 142.30,
|
||||
'MSFT': 378.85,
|
||||
'TSLA': 208.75,
|
||||
'AMZN': 151.20,
|
||||
'NVDA': 465.80,
|
||||
'META': 298.45,
|
||||
'NFLX': 425.60,
|
||||
};
|
||||
|
||||
return prices[symbol] || 100.00;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown the service
|
||||
*/
|
||||
async shutdown(): Promise<void> {
|
||||
console.log('🔄 Shutting down Market Data Service...');
|
||||
|
||||
if (this.dataUpdateInterval) {
|
||||
clearInterval(this.dataUpdateInterval);
|
||||
}
|
||||
|
||||
// Close all WebSocket connections
|
||||
for (const client of this.wsClients) {
|
||||
client.close();
|
||||
}
|
||||
|
||||
await this.eventPublisher.setServiceHealth('market-data-gateway', 'unhealthy');
|
||||
await this.eventPublisher.disconnect();
|
||||
|
||||
console.log('✅ Market Data Service shutdown complete');
|
||||
}
|
||||
}
|
||||
18
apps/core-services/market-data-gateway/tsconfig.json
Normal file
18
apps/core-services/market-data-gateway/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"types": ["bun-types"]
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
22
apps/core-services/risk-guardian/package.json
Normal file
22
apps/core-services/risk-guardian/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "risk-guardian",
|
||||
"version": "1.0.0",
|
||||
"description": "Real-time risk monitoring and controls service",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"dev": "bun run --watch src/index.ts",
|
||||
"start": "bun run src/index.ts",
|
||||
"test": "echo 'No tests yet'"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.6.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "^1.2.15",
|
||||
"@types/ws": "^8.5.12"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "strategy-orchestrator",
|
||||
"version": "1.0.0",
|
||||
"description": "Trading strategy lifecycle management service",
|
||||
"main": "src/index.ts",
|
||||
"scripts": {
|
||||
"dev": "bun run --watch src/index.ts",
|
||||
"start": "bun run src/index.ts",
|
||||
"test": "echo 'No tests yet'"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.6.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"ws": "^8.18.0",
|
||||
"node-cron": "^3.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": "^1.2.15",
|
||||
"@types/ws": "^8.5.12",
|
||||
"@types/node-cron": "^3.0.11"
|
||||
}
|
||||
}
|
||||
24
apps/interface-services/trading-dashboard/.gitignore
vendored
Normal file
24
apps/interface-services/trading-dashboard/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
54
apps/interface-services/trading-dashboard/README.md
Normal file
54
apps/interface-services/trading-dashboard/README.md
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# React + TypeScript + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||
|
||||
## Expanding the ESLint configuration
|
||||
|
||||
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
||||
|
||||
```js
|
||||
export default tseslint.config({
|
||||
extends: [
|
||||
// Remove ...tseslint.configs.recommended and replace with this
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
// Alternatively, use this for stricter rules
|
||||
...tseslint.configs.strictTypeChecked,
|
||||
// Optionally, add this for stylistic rules
|
||||
...tseslint.configs.stylisticTypeChecked,
|
||||
],
|
||||
languageOptions: {
|
||||
// other options...
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
||||
|
||||
```js
|
||||
// eslint.config.js
|
||||
import reactX from 'eslint-plugin-react-x'
|
||||
import reactDom from 'eslint-plugin-react-dom'
|
||||
|
||||
export default tseslint.config({
|
||||
plugins: {
|
||||
// Add the react-x and react-dom plugins
|
||||
'react-x': reactX,
|
||||
'react-dom': reactDom,
|
||||
},
|
||||
rules: {
|
||||
// other rules...
|
||||
// Enable its recommended typescript rules
|
||||
...reactX.configs['recommended-typescript'].rules,
|
||||
...reactDom.configs.recommended.rules,
|
||||
},
|
||||
})
|
||||
```
|
||||
28
apps/interface-services/trading-dashboard/eslint.config.js
Normal file
28
apps/interface-services/trading-dashboard/eslint.config.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
13
apps/interface-services/trading-dashboard/index.html
Normal file
13
apps/interface-services/trading-dashboard/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
32
apps/interface-services/trading-dashboard/package.json
Normal file
32
apps/interface-services/trading-dashboard/package.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "trading-dashboard",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"@tremor/react": "^3.18.7",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
42
apps/interface-services/trading-dashboard/src/App.css
Normal file
42
apps/interface-services/trading-dashboard/src/App.css
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
8
apps/interface-services/trading-dashboard/src/App.tsx
Normal file
8
apps/interface-services/trading-dashboard/src/App.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { TradingDashboard } from './components/TradingDashboard'
|
||||
import './App.css'
|
||||
|
||||
function App() {
|
||||
return <TradingDashboard />
|
||||
}
|
||||
|
||||
export default App
|
||||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4 KiB |
|
|
@ -0,0 +1,455 @@
|
|||
import { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Card,
|
||||
Title,
|
||||
Text,
|
||||
Metric,
|
||||
Flex,
|
||||
Badge,
|
||||
Grid,
|
||||
AreaChart,
|
||||
DonutChart,
|
||||
BarChart,
|
||||
LineChart,
|
||||
Tab,
|
||||
TabGroup,
|
||||
TabList,
|
||||
TabPanel,
|
||||
TabPanels,
|
||||
Button,
|
||||
} from '@tremor/react';
|
||||
import type { MarketData, OHLCV, HealthStatus } from '@stock-bot/shared-types';
|
||||
|
||||
const API_BASE = 'http://localhost:3001';
|
||||
|
||||
interface DashboardData {
|
||||
marketData: MarketData[];
|
||||
ohlcvData: OHLCV[];
|
||||
serviceHealth: HealthStatus | null;
|
||||
lastUpdate: Date | null;
|
||||
}
|
||||
|
||||
export function TradingDashboard() {
|
||||
const [data, setData] = useState<DashboardData>({
|
||||
marketData: [],
|
||||
ohlcvData: [],
|
||||
serviceHealth: null,
|
||||
lastUpdate: null,
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [wsConnected, setWsConnected] = useState(false);
|
||||
|
||||
const symbols = useMemo(() => ['AAPL', 'GOOGL', 'MSFT', 'TSLA', 'AMZN'], []);
|
||||
|
||||
// Memoized fetch functions
|
||||
const fetchServiceHealth = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/health`);
|
||||
const health = await response.json();
|
||||
return health;
|
||||
} catch (error) {
|
||||
console.error('Error fetching service health:', error);
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const fetchMarketData = useCallback(async () => {
|
||||
try {
|
||||
const promises = symbols.map(async (symbol) => {
|
||||
const response = await fetch(`${API_BASE}/api/market-data/${symbol}`);
|
||||
const result = await response.json();
|
||||
return result.success ? result.data : null;
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
return results.filter(Boolean);
|
||||
} catch (error) {
|
||||
console.error('Error fetching market data:', error);
|
||||
return [];
|
||||
}
|
||||
}, [symbols]);
|
||||
|
||||
const fetchOHLCVData = useCallback(async (symbol: string = 'AAPL') => {
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/api/ohlcv/${symbol}?limit=50`);
|
||||
const result = await response.json();
|
||||
return result.success ? result.data : [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching OHLCV data:', error);
|
||||
return [];
|
||||
}
|
||||
}, []);
|
||||
|
||||
// Load all data function
|
||||
const loadData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [health, marketData, ohlcvData] = await Promise.all([
|
||||
fetchServiceHealth(),
|
||||
fetchMarketData(),
|
||||
fetchOHLCVData(),
|
||||
]);
|
||||
|
||||
setData({
|
||||
serviceHealth: health,
|
||||
marketData,
|
||||
ohlcvData,
|
||||
lastUpdate: new Date(),
|
||||
});
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Unknown error');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [fetchServiceHealth, fetchMarketData, fetchOHLCVData]);
|
||||
|
||||
// WebSocket connection and data loading
|
||||
useEffect(() => {
|
||||
let ws: WebSocket | null = null;
|
||||
|
||||
const connectWebSocket = () => {
|
||||
try {
|
||||
ws = new WebSocket('ws://localhost:3001');
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket connected');
|
||||
setWsConnected(true);
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.type === 'market-data' && message.data) {
|
||||
// Update specific symbol data
|
||||
setData(prev => ({
|
||||
...prev,
|
||||
marketData: prev.marketData.map(item =>
|
||||
item.symbol === message.data.symbol ? message.data : item
|
||||
),
|
||||
lastUpdate: new Date(),
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing WebSocket message:', error);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket disconnected');
|
||||
setWsConnected(false);
|
||||
// Reconnect after 5 seconds
|
||||
setTimeout(connectWebSocket, 5000);
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket error:', error);
|
||||
setWsConnected(false);
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Failed to connect WebSocket:', error);
|
||||
setWsConnected(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Initial data load
|
||||
loadData();
|
||||
|
||||
// Set up periodic refresh
|
||||
const interval = setInterval(loadData, 30000); // Refresh every 30 seconds
|
||||
|
||||
// Attempt WebSocket connection
|
||||
connectWebSocket();
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
};
|
||||
}, [loadData]);
|
||||
|
||||
// Prepare chart data
|
||||
const chartData = data.ohlcvData.map((item) => ({
|
||||
time: new Date(item.timestamp).toLocaleTimeString(),
|
||||
price: item.close,
|
||||
volume: item.volume,
|
||||
}));
|
||||
|
||||
// Prepare portfolio allocation data (demo)
|
||||
const portfolioData = data.marketData.map((item, index) => ({
|
||||
name: item.symbol,
|
||||
value: (index + 1) * 20000, // Demo allocation
|
||||
}));
|
||||
|
||||
// Calculate total portfolio value
|
||||
const totalValue = portfolioData.reduce((sum, item) => sum + item.value, 0);
|
||||
|
||||
// Performance metrics (demo)
|
||||
const performanceData = [
|
||||
{ date: 'Mon', value: 98000 },
|
||||
{ date: 'Tue', value: 102000 },
|
||||
{ date: 'Wed', value: 105000 },
|
||||
{ date: 'Thu', value: 103000 },
|
||||
{ date: 'Fri', value: 108000 },
|
||||
];
|
||||
|
||||
if (loading && data.marketData.length === 0) {
|
||||
return (
|
||||
<div className="p-8 bg-slate-50 min-h-screen">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="text-center">
|
||||
<Title>Loading Trading Dashboard...</Title>
|
||||
<Text className="mt-4">Connecting to market data services</Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-8 bg-slate-50 min-h-screen">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<Flex justifyContent="between" alignItems="center">
|
||||
<div>
|
||||
<Title className="text-3xl font-bold">🤖 Stock Bot Dashboard</Title>
|
||||
<Text className="mt-2">Real-time market data monitoring</Text>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<Button onClick={loadData} disabled={loading}>
|
||||
{loading ? 'Refreshing...' : 'Refresh Data'}
|
||||
</Button>
|
||||
{data.lastUpdate && (
|
||||
<Text className="mt-2">
|
||||
Last update: {data.lastUpdate.toLocaleTimeString()}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Card className="mb-6">
|
||||
<Text color="red">Error: {error}</Text>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Top Metrics */}
|
||||
<Grid numItems={1} numItemsSm={2} numItemsLg={4} className="gap-6 mb-8">
|
||||
<Card>
|
||||
<Text>Portfolio Value</Text>
|
||||
<Metric>${totalValue.toLocaleString()}</Metric>
|
||||
<Flex className="mt-4">
|
||||
<Badge color="emerald">+8.2%</Badge>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Text>Service Status</Text>
|
||||
<Flex alignItems="center" className="mt-2">
|
||||
<Badge
|
||||
color={data.serviceHealth?.status === 'healthy' ? 'emerald' : 'red'}
|
||||
>
|
||||
{data.serviceHealth?.status || 'Unknown'}
|
||||
</Badge>
|
||||
</Flex>
|
||||
<Flex alignItems="center" className="mt-2">
|
||||
<Text className="text-sm mr-2">WebSocket:</Text>
|
||||
<Badge
|
||||
color={wsConnected ? 'emerald' : 'red'}
|
||||
size="sm"
|
||||
>
|
||||
{wsConnected ? 'Connected' : 'Disconnected'}
|
||||
</Badge>
|
||||
</Flex>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Text>Active Symbols</Text>
|
||||
<Metric>{data.marketData.length}</Metric>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Text>Daily P&L</Text>
|
||||
<Metric color="emerald">+$2,450</Metric>
|
||||
<Flex className="mt-4">
|
||||
<Badge color="emerald">+2.3%</Badge>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
{/* Main Content Tabs */}
|
||||
<TabGroup>
|
||||
<TabList className="mb-8">
|
||||
<Tab>Market Data</Tab>
|
||||
<Tab>Portfolio</Tab>
|
||||
<Tab>Charts</Tab>
|
||||
<Tab>Performance</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanels>
|
||||
{/* Market Data Tab */}
|
||||
<TabPanel>
|
||||
<Grid numItems={1} numItemsLg={2} className="gap-6">
|
||||
<Card>
|
||||
<Title>Live Prices</Title>
|
||||
<div className="mt-6">
|
||||
{data.marketData.map((item) => (
|
||||
<div
|
||||
key={item.symbol}
|
||||
className="flex justify-between items-center py-3 border-b border-gray-200 last:border-b-0"
|
||||
>
|
||||
<div>
|
||||
<Text className="font-semibold">{item.symbol}</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
Vol: {item.volume.toLocaleString()}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<Text className="font-bold text-lg">
|
||||
${item.price.toFixed(2)}
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
Bid: ${item.bid.toFixed(2)} | Ask: ${item.ask.toFixed(2)}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Title>Market Overview</Title>
|
||||
<BarChart
|
||||
className="mt-6"
|
||||
data={data.marketData}
|
||||
index="symbol"
|
||||
categories={["price"]}
|
||||
colors={["blue"]}
|
||||
valueFormatter={(value) => `$${value.toFixed(2)}`}
|
||||
/>
|
||||
</Card>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
|
||||
{/* Portfolio Tab */}
|
||||
<TabPanel>
|
||||
<Grid numItems={1} numItemsLg={2} className="gap-6">
|
||||
<Card>
|
||||
<Title>Portfolio Allocation</Title>
|
||||
<DonutChart
|
||||
className="mt-6"
|
||||
data={portfolioData}
|
||||
category="value"
|
||||
index="name"
|
||||
valueFormatter={(value) => `$${value.toLocaleString()}`}
|
||||
colors={["slate", "violet", "indigo", "rose", "cyan"]}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Title>Holdings</Title>
|
||||
<div className="mt-6">
|
||||
{portfolioData.map((item) => (
|
||||
<div
|
||||
key={item.name}
|
||||
className="flex justify-between items-center py-3 border-b border-gray-200 last:border-b-0"
|
||||
>
|
||||
<Text className="font-semibold">{item.name}</Text>
|
||||
<div className="text-right">
|
||||
<Text className="font-bold">
|
||||
${item.value.toLocaleString()}
|
||||
</Text>
|
||||
<Text className="text-sm text-gray-500">
|
||||
{((item.value / totalValue) * 100).toFixed(1)}%
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
|
||||
{/* Charts Tab */}
|
||||
<TabPanel>
|
||||
<Grid numItems={1} className="gap-6">
|
||||
<Card>
|
||||
<Title>AAPL Price Chart</Title>
|
||||
<LineChart
|
||||
className="mt-6"
|
||||
data={chartData}
|
||||
index="time"
|
||||
categories={["price"]}
|
||||
colors={["indigo"]}
|
||||
valueFormatter={(value) => `$${value.toFixed(2)}`}
|
||||
yAxisWidth={60}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Title>Volume Analysis</Title>
|
||||
<AreaChart
|
||||
className="mt-6"
|
||||
data={chartData}
|
||||
index="time"
|
||||
categories={["volume"]}
|
||||
colors={["emerald"]}
|
||||
valueFormatter={(value) => value.toLocaleString()}
|
||||
yAxisWidth={80}
|
||||
/>
|
||||
</Card>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
|
||||
{/* Performance Tab */}
|
||||
<TabPanel>
|
||||
<Grid numItems={1} numItemsLg={2} className="gap-6">
|
||||
<Card>
|
||||
<Title>Weekly Performance</Title>
|
||||
<AreaChart
|
||||
className="mt-6"
|
||||
data={performanceData}
|
||||
index="date"
|
||||
categories={["value"]}
|
||||
colors={["emerald"]}
|
||||
valueFormatter={(value) => `$${value.toLocaleString()}`}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<Title>Performance Metrics</Title>
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="flex justify-between">
|
||||
<Text>Total Return</Text>
|
||||
<Badge color="emerald">+12.5%</Badge>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Text>Sharpe Ratio</Text>
|
||||
<Badge color="blue">1.8</Badge>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Text>Max Drawdown</Text>
|
||||
<Badge color="red">-5.2%</Badge>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Text>Win Rate</Text>
|
||||
<Badge color="emerald">68%</Badge>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<Text>Avg Trade</Text>
|
||||
<Badge color="indigo">+$245</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</TabGroup>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
apps/interface-services/trading-dashboard/src/index.css
Normal file
43
apps/interface-services/trading-dashboard/src/index.css
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* Reset and base styles for Tremor UI */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light;
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background-color: #f8fafc;
|
||||
}
|
||||
|
||||
#root {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Tremor component overrides */
|
||||
.tremor-Card-root {
|
||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
.tremor-Button-root {
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tremor-Button-root:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
0
apps/interface-services/trading-dashboard/src/index.ts
Normal file
0
apps/interface-services/trading-dashboard/src/index.ts
Normal file
10
apps/interface-services/trading-dashboard/src/main.tsx
Normal file
10
apps/interface-services/trading-dashboard/src/main.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
0
apps/interface-services/trading-dashboard/src/simple.ts
Normal file
0
apps/interface-services/trading-dashboard/src/simple.ts
Normal file
1
apps/interface-services/trading-dashboard/src/vite-env.d.ts
vendored
Normal file
1
apps/interface-services/trading-dashboard/src/vite-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/// <reference types="vite/client" />
|
||||
27
apps/interface-services/trading-dashboard/tsconfig.app.json
Normal file
27
apps/interface-services/trading-dashboard/tsconfig.app.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
apps/interface-services/trading-dashboard/tsconfig.json
Normal file
7
apps/interface-services/trading-dashboard/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
25
apps/interface-services/trading-dashboard/tsconfig.node.json
Normal file
25
apps/interface-services/trading-dashboard/tsconfig.node.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
7
apps/interface-services/trading-dashboard/vite.config.ts
Normal file
7
apps/interface-services/trading-dashboard/vite.config.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
718
bun.lock
Normal file
718
bun.lock
Normal file
|
|
@ -0,0 +1,718 @@
|
|||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "stock-bot",
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.12",
|
||||
"turbo": "^2.0.5",
|
||||
"typescript": "^5.4.5",
|
||||
},
|
||||
},
|
||||
"apps/core-services/market-data-gateway": {
|
||||
"name": "market-data-gateway",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"hono": "^4.6.3",
|
||||
"ioredis": "^5.4.1",
|
||||
"ws": "^8.18.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^8.5.12",
|
||||
"bun-types": "^1.2.15",
|
||||
},
|
||||
},
|
||||
"apps/interface-services/trading-dashboard": {
|
||||
"name": "trading-dashboard",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@stock-bot/config": "workspace:*",
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"@tremor/react": "^3.18.7",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.0",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.19",
|
||||
"globals": "^16.0.0",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.30.1",
|
||||
"vite": "^6.3.5",
|
||||
},
|
||||
},
|
||||
"packages/config": {
|
||||
"name": "@stock-bot/config",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"dotenv": "^16.4.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.12",
|
||||
"typescript": "^5.4.5",
|
||||
},
|
||||
},
|
||||
"packages/shared-types": {
|
||||
"name": "@stock-bot/shared-types",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
|
||||
|
||||
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||
|
||||
"@babel/compat-data": ["@babel/compat-data@7.27.3", "", {}, "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw=="],
|
||||
|
||||
"@babel/core": ["@babel/core@7.27.4", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.4", "@babel/parser": "^7.27.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.27.4", "@babel/types": "^7.27.3", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g=="],
|
||||
|
||||
"@babel/generator": ["@babel/generator@7.27.3", "", { "dependencies": { "@babel/parser": "^7.27.3", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q=="],
|
||||
|
||||
"@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.27.2", "", { "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ=="],
|
||||
|
||||
"@babel/helper-module-imports": ["@babel/helper-module-imports@7.27.1", "", { "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" } }, "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w=="],
|
||||
|
||||
"@babel/helper-module-transforms": ["@babel/helper-module-transforms@7.27.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.27.3" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg=="],
|
||||
|
||||
"@babel/helper-plugin-utils": ["@babel/helper-plugin-utils@7.27.1", "", {}, "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw=="],
|
||||
|
||||
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
|
||||
|
||||
"@babel/helper-validator-option": ["@babel/helper-validator-option@7.27.1", "", {}, "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg=="],
|
||||
|
||||
"@babel/helpers": ["@babel/helpers@7.27.4", "", { "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.3" } }, "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ=="],
|
||||
|
||||
"@babel/parser": ["@babel/parser@7.27.4", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="],
|
||||
|
||||
"@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="],
|
||||
|
||||
"@babel/runtime": ["@babel/runtime@7.27.4", "", {}, "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA=="],
|
||||
|
||||
"@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
|
||||
|
||||
"@babel/traverse": ["@babel/traverse@7.27.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.3", "@babel/parser": "^7.27.4", "@babel/template": "^7.27.2", "@babel/types": "^7.27.3", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA=="],
|
||||
|
||||
"@babel/types": ["@babel/types@7.27.3", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="],
|
||||
|
||||
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.7.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw=="],
|
||||
|
||||
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
|
||||
|
||||
"@eslint/config-array": ["@eslint/config-array@0.20.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ=="],
|
||||
|
||||
"@eslint/config-helpers": ["@eslint/config-helpers@0.2.2", "", {}, "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg=="],
|
||||
|
||||
"@eslint/core": ["@eslint/core@0.14.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg=="],
|
||||
|
||||
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
|
||||
|
||||
"@eslint/js": ["@eslint/js@9.28.0", "", {}, "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg=="],
|
||||
|
||||
"@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
|
||||
|
||||
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.1", "", { "dependencies": { "@eslint/core": "^0.14.0", "levn": "^0.4.1" } }, "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w=="],
|
||||
|
||||
"@floating-ui/core": ["@floating-ui/core@1.7.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-FRdBLykrPPA6P76GGGqlex/e7fbe0F1ykgxHYNXQsH/iTEtjMj/f9bpY5oQqbjt5VgZvgz/uKXbGuROijh3VLA=="],
|
||||
|
||||
"@floating-ui/dom": ["@floating-ui/dom@1.7.0", "", { "dependencies": { "@floating-ui/core": "^1.7.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-lGTor4VlXcesUMh1cupTUTDoCxMb0V6bm3CnxHzQcw8Eaf1jQbgQX4i02fYgT0vJ82tb5MZ4CZk1LRGkktJCzg=="],
|
||||
|
||||
"@floating-ui/react": ["@floating-ui/react@0.19.2", "", { "dependencies": { "@floating-ui/react-dom": "^1.3.0", "aria-hidden": "^1.1.3", "tabbable": "^6.0.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JyNk4A0Ezirq8FlXECvRtQOX/iBe5Ize0W/pLkrZjfHW9GUV7Xnq6zm6fyZuQzaHHqEnVizmvlA96e1/CkZv+w=="],
|
||||
|
||||
"@floating-ui/react-dom": ["@floating-ui/react-dom@1.3.0", "", { "dependencies": { "@floating-ui/dom": "^1.2.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g=="],
|
||||
|
||||
"@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="],
|
||||
|
||||
"@headlessui/react": ["@headlessui/react@2.2.0", "", { "dependencies": { "@floating-ui/react": "^0.26.16", "@react-aria/focus": "^3.17.1", "@react-aria/interactions": "^3.21.3", "@tanstack/react-virtual": "^3.8.1" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ=="],
|
||||
|
||||
"@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
|
||||
|
||||
"@humanfs/node": ["@humanfs/node@0.16.6", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.3.0" } }, "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw=="],
|
||||
|
||||
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
|
||||
|
||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
|
||||
|
||||
"@ioredis/commands": ["@ioredis/commands@1.2.0", "", {}, "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@react-aria/focus": ["@react-aria/focus@3.20.3", "", { "dependencies": { "@react-aria/interactions": "^3.25.1", "@react-aria/utils": "^3.29.0", "@react-types/shared": "^3.29.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-rR5uZUMSY4xLHmpK/I8bP1V6vUNHFo33gTvrvNUsAKKqvMfa7R2nu5A6v97dr5g6tVH6xzpdkPsOJCWh90H2cw=="],
|
||||
|
||||
"@react-aria/interactions": ["@react-aria/interactions@3.25.1", "", { "dependencies": { "@react-aria/ssr": "^3.9.8", "@react-aria/utils": "^3.29.0", "@react-stately/flags": "^3.1.1", "@react-types/shared": "^3.29.1", "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-ntLrlgqkmZupbbjekz3fE/n3eQH2vhncx8gUp0+N+GttKWevx7jos11JUBjnJwb1RSOPgRUFcrluOqBp0VgcfQ=="],
|
||||
|
||||
"@react-aria/ssr": ["@react-aria/ssr@3.9.8", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw=="],
|
||||
|
||||
"@react-aria/utils": ["@react-aria/utils@3.29.0", "", { "dependencies": { "@react-aria/ssr": "^3.9.8", "@react-stately/flags": "^3.1.1", "@react-stately/utils": "^3.10.6", "@react-types/shared": "^3.29.1", "@swc/helpers": "^0.5.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-jSOrZimCuT1iKNVlhjIxDkAhgF7HSp3pqyT6qjg/ZoA0wfqCi/okmrMPiWSAKBnkgX93N8GYTLT3CIEO6WZe9Q=="],
|
||||
|
||||
"@react-stately/flags": ["@react-stately/flags@3.1.1", "", { "dependencies": { "@swc/helpers": "^0.5.0" } }, "sha512-XPR5gi5LfrPdhxZzdIlJDz/B5cBf63l4q6/AzNqVWFKgd0QqY5LvWJftXkklaIUpKSJkIKQb8dphuZXDtkWNqg=="],
|
||||
|
||||
"@react-stately/utils": ["@react-stately/utils@3.10.6", "", { "dependencies": { "@swc/helpers": "^0.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-O76ip4InfTTzAJrg8OaZxKU4vvjMDOpfA/PGNOytiXwBbkct2ZeZwaimJ8Bt9W1bj5VsZ81/o/tW4BacbdDOMA=="],
|
||||
|
||||
"@react-types/shared": ["@react-types/shared@3.29.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, "sha512-KtM+cDf2CXoUX439rfEhbnEdAgFZX20UP2A35ypNIawR7/PFFPjQDWyA2EnClCcW/dLWJDEPX2U8+EJff8xqmQ=="],
|
||||
|
||||
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9", "", {}, "sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.41.1", "", { "os": "android", "cpu": "arm" }, "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.41.1", "", { "os": "android", "cpu": "arm64" }, "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.41.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.41.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.41.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.41.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.41.1", "", { "os": "linux", "cpu": "arm" }, "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.41.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg=="],
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw=="],
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.41.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.41.1", "", { "os": "linux", "cpu": "none" }, "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.41.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.41.1", "", { "os": "linux", "cpu": "x64" }, "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.41.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.41.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.41.1", "", { "os": "win32", "cpu": "x64" }, "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw=="],
|
||||
|
||||
"@stock-bot/config": ["@stock-bot/config@workspace:packages/config"],
|
||||
|
||||
"@stock-bot/shared-types": ["@stock-bot/shared-types@workspace:packages/shared-types"],
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.17", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A=="],
|
||||
|
||||
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.13.9", "", { "dependencies": { "@tanstack/virtual-core": "3.13.9" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-SPWC8kwG/dWBf7Py7cfheAPOxuvIv4fFQ54PdmYbg7CpXfsKxkucak43Q0qKsxVthhUJQ1A7CIMAIplq4BjVwA=="],
|
||||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.13.9", "", {}, "sha512-3jztt0jpaoJO5TARe2WIHC1UQC3VMLAFUW5mmMo0yrkwtDB2AQP0+sh10BVUpWrnvHjSLvzFizydtEGLCJKFoQ=="],
|
||||
|
||||
"@tremor/react": ["@tremor/react@3.18.7", "", { "dependencies": { "@floating-ui/react": "^0.19.2", "@headlessui/react": "2.2.0", "date-fns": "^3.6.0", "react-day-picker": "^8.10.1", "react-transition-state": "^2.1.2", "recharts": "^2.13.3", "tailwind-merge": "^2.5.2" }, "peerDependencies": { "react": "^18.0.0", "react-dom": ">=16.6.0" } }, "sha512-nmqvf/1m0GB4LXc7v2ftdfSLoZhy5WLrhV6HNf0SOriE6/l8WkYeWuhQq8QsBjRi94mUIKLJ/VC3/Y/pj6VubQ=="],
|
||||
|
||||
"@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="],
|
||||
|
||||
"@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="],
|
||||
|
||||
"@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="],
|
||||
|
||||
"@types/babel__traverse": ["@types/babel__traverse@7.20.7", "", { "dependencies": { "@babel/types": "^7.20.7" } }, "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng=="],
|
||||
|
||||
"@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="],
|
||||
|
||||
"@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
|
||||
|
||||
"@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
|
||||
|
||||
"@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
|
||||
|
||||
"@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
|
||||
|
||||
"@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
|
||||
|
||||
"@types/d3-shape": ["@types/d3-shape@3.1.7", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg=="],
|
||||
|
||||
"@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
|
||||
|
||||
"@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||
|
||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||
|
||||
"@types/node": ["@types/node@20.17.57", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-f3T4y6VU4fVQDKVqJV4Uppy8c1p/sVvS3peyqxyWnzkqXFJLRU7Y1Bl7rMS1Qe9z0v4M6McY0Fp9yBsgHJUsWQ=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.6", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.1.5", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.33.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/type-utils": "8.33.0", "@typescript-eslint/utils": "8.33.0", "@typescript-eslint/visitor-keys": "8.33.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.33.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-CACyQuqSHt7ma3Ns601xykeBK/rDeZa3w6IS6UtMQbixO5DWy+8TilKkviGDH6jtWCo8FGRKEK5cLLkPvEammQ=="],
|
||||
|
||||
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.33.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/types": "8.33.0", "@typescript-eslint/typescript-estree": "8.33.0", "@typescript-eslint/visitor-keys": "8.33.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-JaehZvf6m0yqYp34+RVnihBAChkqeH+tqqhS0GuX1qgPpwLvmTPheKEs6OeCK6hVJgXZHJ2vbjnC9j119auStQ=="],
|
||||
|
||||
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.33.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.33.0", "@typescript-eslint/types": "^8.33.0", "debug": "^4.3.4" } }, "sha512-d1hz0u9l6N+u/gcrk6s6gYdl7/+pp8yHheRTqP6X5hVDKALEaTn8WfGiit7G511yueBEL3OpOEpD+3/MBdoN+A=="],
|
||||
|
||||
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.33.0", "", { "dependencies": { "@typescript-eslint/types": "8.33.0", "@typescript-eslint/visitor-keys": "8.33.0" } }, "sha512-LMi/oqrzpqxyO72ltP+dBSP6V0xiUb4saY7WLtxSfiNEBI8m321LLVFU9/QDJxjDQG9/tjSqKz/E3380TEqSTw=="],
|
||||
|
||||
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.33.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-sTkETlbqhEoiFmGr1gsdq5HyVbSOF0145SYDJ/EQmXHtKViCaGvnyLqWFFHtEXoS0J1yU8Wyou2UGmgW88fEug=="],
|
||||
|
||||
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.33.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "8.33.0", "@typescript-eslint/utils": "8.33.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-lScnHNCBqL1QayuSrWeqAL5GmqNdVUQAAMTaCwdYEdWfIrSrOGzyLGRCHXcCixa5NK6i5l0AfSO2oBSjCjf4XQ=="],
|
||||
|
||||
"@typescript-eslint/types": ["@typescript-eslint/types@8.33.0", "", {}, "sha512-DKuXOKpM5IDT1FA2g9x9x1Ug81YuKrzf4mYX8FAVSNu5Wo/LELHWQyM1pQaDkI42bX15PWl0vNPt1uGiIFUOpg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.33.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.33.0", "@typescript-eslint/tsconfig-utils": "8.33.0", "@typescript-eslint/types": "8.33.0", "@typescript-eslint/visitor-keys": "8.33.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <5.9.0" } }, "sha512-vegY4FQoB6jL97Tu/lWRsAiUUp8qJTqzAmENH2k59SJhw0Th1oszb9Idq/FyyONLuNqT1OADJPXfyUNOR8SzAQ=="],
|
||||
|
||||
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.33.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.33.0", "@typescript-eslint/types": "8.33.0", "@typescript-eslint/typescript-estree": "8.33.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-lPFuQaLA9aSNa7D5u2EpRiqdAUhzShwGg/nhpBlc4GR6kcTABttCuyjFs8BcEZ8VWrjCBof/bePhP3Q3fS+Yrw=="],
|
||||
|
||||
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.33.0", "", { "dependencies": { "@typescript-eslint/types": "8.33.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-7RW7CMYoskiz5OOGAWjJFxgb7c5UNjTG292gYhWeOAcFmYCtVCSqjqSBj5zMhxbXo2JOW95YYrUWJfU0zrpaGQ=="],
|
||||
|
||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.5.0", "", { "dependencies": { "@babel/core": "^7.26.10", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@rolldown/pluginutils": "1.0.0-beta.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg=="],
|
||||
|
||||
"acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
|
||||
|
||||
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
|
||||
|
||||
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||
|
||||
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"browserslist": ["browserslist@4.25.0", "", { "dependencies": { "caniuse-lite": "^1.0.30001718", "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001720", "", {}, "sha512-Ec/2yV2nNPwb4DnTANEV99ZWwm3ZWfdlfkQbWSDDt+PsXEVYwlhPH8tdMaPunYTKKmz7AnHi2oNEi1GcmKCD8g=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
|
||||
|
||||
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
|
||||
|
||||
"d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
|
||||
|
||||
"d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
|
||||
|
||||
"d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="],
|
||||
|
||||
"d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
|
||||
|
||||
"d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
|
||||
|
||||
"d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
|
||||
|
||||
"d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
|
||||
|
||||
"d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
|
||||
|
||||
"d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
|
||||
|
||||
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
|
||||
|
||||
"date-fns": ["date-fns@3.6.0", "", {}, "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww=="],
|
||||
|
||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
|
||||
|
||||
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
||||
|
||||
"denque": ["denque@2.1.0", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="],
|
||||
|
||||
"dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="],
|
||||
|
||||
"dotenv": ["dotenv@16.5.0", "", {}, "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg=="],
|
||||
|
||||
"electron-to-chromium": ["electron-to-chromium@1.5.161", "", {}, "sha512-hwtetwfKNZo/UlwHIVBlKZVdy7o8bIZxxKs0Mv/ROPiQQQmDgdm5a+KvKtBsxM8ZjFzTaCeLoodZ8jiBE3o9rA=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="],
|
||||
|
||||
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
|
||||
|
||||
"eslint": ["eslint@9.28.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.28.0", "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ=="],
|
||||
|
||||
"eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
|
||||
|
||||
"eslint-plugin-react-refresh": ["eslint-plugin-react-refresh@0.4.20", "", { "peerDependencies": { "eslint": ">=8.40" } }, "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA=="],
|
||||
|
||||
"eslint-scope": ["eslint-scope@8.3.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ=="],
|
||||
|
||||
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.0", "", {}, "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw=="],
|
||||
|
||||
"espree": ["espree@10.3.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.0" } }, "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg=="],
|
||||
|
||||
"esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
|
||||
|
||||
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
|
||||
|
||||
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
|
||||
|
||||
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
"fast-equals": ["fast-equals@5.2.2", "", {}, "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
|
||||
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
|
||||
|
||||
"fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
|
||||
|
||||
"fdir": ["fdir@6.4.5", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
|
||||
|
||||
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
|
||||
|
||||
"flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"globals": ["globals@16.2.0", "", {}, "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg=="],
|
||||
|
||||
"graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="],
|
||||
|
||||
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
||||
|
||||
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
|
||||
|
||||
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
||||
|
||||
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
|
||||
|
||||
"ioredis": ["ioredis@5.6.1", "", { "dependencies": { "@ioredis/commands": "^1.1.1", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
|
||||
|
||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||
|
||||
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
|
||||
|
||||
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
||||
|
||||
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
|
||||
|
||||
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
|
||||
|
||||
"lodash.defaults": ["lodash.defaults@4.2.0", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="],
|
||||
|
||||
"lodash.isarguments": ["lodash.isarguments@3.1.0", "", {}, "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg=="],
|
||||
|
||||
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||
|
||||
"market-data-gateway": ["market-data-gateway@workspace:apps/core-services/market-data-gateway"],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
|
||||
|
||||
"node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
|
||||
|
||||
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
|
||||
|
||||
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
|
||||
|
||||
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
|
||||
|
||||
"path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
|
||||
|
||||
"postcss": ["postcss@8.5.4", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w=="],
|
||||
|
||||
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
|
||||
|
||||
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
|
||||
|
||||
"react-day-picker": ["react-day-picker@8.10.1", "", { "peerDependencies": { "date-fns": "^2.28.0 || ^3.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA=="],
|
||||
|
||||
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||
|
||||
"react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="],
|
||||
|
||||
"react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="],
|
||||
|
||||
"react-transition-state": ["react-transition-state@2.3.1", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-Z48el73x+7HUEM131dof9YpcQ5IlM4xB+pKWH/lX3FhxGfQaNTZa16zb7pWkC/y5btTZzXfCtglIJEGc57giOw=="],
|
||||
|
||||
"recharts": ["recharts@2.15.3", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.4", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ=="],
|
||||
|
||||
"recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="],
|
||||
|
||||
"redis-errors": ["redis-errors@1.2.0", "", {}, "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w=="],
|
||||
|
||||
"redis-parser": ["redis-parser@3.0.0", "", { "dependencies": { "redis-errors": "^1.0.0" } }, "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A=="],
|
||||
|
||||
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"rollup": ["rollup@4.41.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.41.1", "@rollup/rollup-android-arm64": "4.41.1", "@rollup/rollup-darwin-arm64": "4.41.1", "@rollup/rollup-darwin-x64": "4.41.1", "@rollup/rollup-freebsd-arm64": "4.41.1", "@rollup/rollup-freebsd-x64": "4.41.1", "@rollup/rollup-linux-arm-gnueabihf": "4.41.1", "@rollup/rollup-linux-arm-musleabihf": "4.41.1", "@rollup/rollup-linux-arm64-gnu": "4.41.1", "@rollup/rollup-linux-arm64-musl": "4.41.1", "@rollup/rollup-linux-loongarch64-gnu": "4.41.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-gnu": "4.41.1", "@rollup/rollup-linux-riscv64-musl": "4.41.1", "@rollup/rollup-linux-s390x-gnu": "4.41.1", "@rollup/rollup-linux-x64-gnu": "4.41.1", "@rollup/rollup-linux-x64-musl": "4.41.1", "@rollup/rollup-win32-arm64-msvc": "4.41.1", "@rollup/rollup-win32-ia32-msvc": "4.41.1", "@rollup/rollup-win32-x64-msvc": "4.41.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
|
||||
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="],
|
||||
|
||||
"tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="],
|
||||
|
||||
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"trading-dashboard": ["trading-dashboard@workspace:apps/interface-services/trading-dashboard"],
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"turbo": ["turbo@2.5.4", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.4", "turbo-darwin-arm64": "2.5.4", "turbo-linux-64": "2.5.4", "turbo-linux-arm64": "2.5.4", "turbo-windows-64": "2.5.4", "turbo-windows-arm64": "2.5.4" }, "bin": { "turbo": "bin/turbo" } }, "sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA=="],
|
||||
|
||||
"turbo-darwin-64": ["turbo-darwin-64@2.5.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ=="],
|
||||
|
||||
"turbo-darwin-arm64": ["turbo-darwin-arm64@2.5.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2+Nx6LAyuXw2MdXb7pxqle3MYignLvS7OwtsP9SgtSBaMlnNlxl9BovzqdYAgkUW3AsYiQMJ/wBRb7d+xemM5A=="],
|
||||
|
||||
"turbo-linux-64": ["turbo-linux-64@2.5.4", "", { "os": "linux", "cpu": "x64" }, "sha512-5May2kjWbc8w4XxswGAl74GZ5eM4Gr6IiroqdLhXeXyfvWEdm2mFYCSWOzz0/z5cAgqyGidF1jt1qzUR8hTmOA=="],
|
||||
|
||||
"turbo-linux-arm64": ["turbo-linux-arm64@2.5.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-/2yqFaS3TbfxV3P5yG2JUI79P7OUQKOUvAnx4MV9Bdz6jqHsHwc9WZPpO4QseQm+NvmgY6ICORnoVPODxGUiJg=="],
|
||||
|
||||
"turbo-windows-64": ["turbo-windows-64@2.5.4", "", { "os": "win32", "cpu": "x64" }, "sha512-EQUO4SmaCDhO6zYohxIjJpOKRN3wlfU7jMAj3CgcyTPvQR/UFLEKAYHqJOnJtymbQmiiM/ihX6c6W6Uq0yC7mA=="],
|
||||
|
||||
"turbo-windows-arm64": ["turbo-windows-arm64@2.5.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-oQ8RrK1VS8lrxkLriotFq+PiF7iiGgkZtfLKF4DDKsmdbPo0O9R2mQxm7jHLuXraRCuIQDWMIw6dpcr7Iykf4A=="],
|
||||
|
||||
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
|
||||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"typescript-eslint": ["typescript-eslint@8.33.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.33.0", "@typescript-eslint/parser": "8.33.0", "@typescript-eslint/utils": "8.33.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, "sha512-5YmNhF24ylCsvdNW2oJwMzTbaeO4bg90KeGtMjUw0AGtHksgEPLRTUil+coHwCfiu4QjVJFnjp94DmU6zV7DhQ=="],
|
||||
|
||||
"undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
|
||||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="],
|
||||
|
||||
"vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
||||
"ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
|
||||
|
||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
"@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="],
|
||||
|
||||
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
|
||||
|
||||
"@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
|
||||
|
||||
"@headlessui/react/@floating-ui/react": ["@floating-ui/react@0.26.28", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw=="],
|
||||
|
||||
"@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="],
|
||||
|
||||
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"@headlessui/react/@floating-ui/react/@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
}
|
||||
}
|
||||
20
database/postgres/init/01-init-schemas.sql
Normal file
20
database/postgres/init/01-init-schemas.sql
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
-- Trading Bot Database Schema Initialization
|
||||
|
||||
-- Create schemas
|
||||
CREATE SCHEMA IF NOT EXISTS trading;
|
||||
CREATE SCHEMA IF NOT EXISTS strategy;
|
||||
CREATE SCHEMA IF NOT EXISTS risk;
|
||||
CREATE SCHEMA IF NOT EXISTS audit;
|
||||
|
||||
-- Set search path for the database
|
||||
ALTER DATABASE trading_bot SET search_path TO trading, strategy, risk, audit, public;
|
||||
|
||||
-- Create extensions
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
CREATE EXTENSION IF NOT EXISTS "btree_gin";
|
||||
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
|
||||
|
||||
-- Create a read-only user for analytics
|
||||
CREATE USER trading_reader WITH PASSWORD 'reader_pass_dev';
|
||||
GRANT CONNECT ON DATABASE trading_bot TO trading_reader;
|
||||
GRANT USAGE ON SCHEMA trading, strategy, risk, audit TO trading_reader;
|
||||
93
database/postgres/init/02-trading-tables.sql
Normal file
93
database/postgres/init/02-trading-tables.sql
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
-- Core trading tables
|
||||
|
||||
-- Symbols and instruments
|
||||
CREATE TABLE trading.symbols (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
symbol VARCHAR(20) NOT NULL UNIQUE,
|
||||
name VARCHAR(255),
|
||||
exchange VARCHAR(50),
|
||||
asset_type VARCHAR(20) DEFAULT 'equity',
|
||||
sector VARCHAR(100),
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Orders
|
||||
CREATE TABLE trading.orders (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
symbol_id UUID REFERENCES trading.symbols(id),
|
||||
strategy_id UUID,
|
||||
order_type VARCHAR(20) NOT NULL, -- 'market', 'limit', 'stop', etc.
|
||||
side VARCHAR(10) NOT NULL CHECK (side IN ('buy', 'sell')),
|
||||
quantity DECIMAL(18,8) NOT NULL CHECK (quantity > 0),
|
||||
price DECIMAL(18,8),
|
||||
stop_price DECIMAL(18,8),
|
||||
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'submitted', 'filled', 'cancelled', 'rejected')),
|
||||
broker_order_id VARCHAR(100),
|
||||
filled_quantity DECIMAL(18,8) DEFAULT 0,
|
||||
avg_fill_price DECIMAL(18,8),
|
||||
commission DECIMAL(18,8) DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
CONSTRAINT valid_prices CHECK (
|
||||
(order_type = 'market') OR
|
||||
(order_type = 'limit' AND price IS NOT NULL) OR
|
||||
(order_type = 'stop' AND stop_price IS NOT NULL)
|
||||
)
|
||||
);
|
||||
|
||||
-- Positions
|
||||
CREATE TABLE trading.positions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
symbol_id UUID REFERENCES trading.symbols(id),
|
||||
strategy_id UUID,
|
||||
quantity DECIMAL(18,8) NOT NULL,
|
||||
avg_cost DECIMAL(18,8) NOT NULL,
|
||||
market_value DECIMAL(18,8),
|
||||
unrealized_pnl DECIMAL(18,8),
|
||||
realized_pnl DECIMAL(18,8) DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(symbol_id, strategy_id)
|
||||
);
|
||||
|
||||
-- Executions/Fills
|
||||
CREATE TABLE trading.executions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
order_id UUID REFERENCES trading.orders(id),
|
||||
symbol_id UUID REFERENCES trading.symbols(id),
|
||||
side VARCHAR(10) NOT NULL CHECK (side IN ('buy', 'sell')),
|
||||
quantity DECIMAL(18,8) NOT NULL,
|
||||
price DECIMAL(18,8) NOT NULL,
|
||||
commission DECIMAL(18,8) DEFAULT 0,
|
||||
broker_execution_id VARCHAR(100),
|
||||
executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Accounts/Portfolios
|
||||
CREATE TABLE trading.accounts (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
account_type VARCHAR(50) DEFAULT 'paper', -- 'paper', 'live'
|
||||
broker VARCHAR(50),
|
||||
cash_balance DECIMAL(18,2) DEFAULT 0,
|
||||
buying_power DECIMAL(18,2) DEFAULT 0,
|
||||
total_value DECIMAL(18,2) DEFAULT 0,
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes for performance
|
||||
CREATE INDEX idx_orders_symbol_created ON trading.orders(symbol_id, created_at);
|
||||
CREATE INDEX idx_orders_status ON trading.orders(status);
|
||||
CREATE INDEX idx_orders_strategy ON trading.orders(strategy_id);
|
||||
CREATE INDEX idx_positions_strategy ON trading.positions(strategy_id);
|
||||
CREATE INDEX idx_executions_order ON trading.executions(order_id);
|
||||
CREATE INDEX idx_executions_symbol_time ON trading.executions(symbol_id, executed_at);
|
||||
|
||||
-- Grant permissions to reader
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA trading TO trading_reader;
|
||||
105
database/postgres/init/03-strategy-risk-tables.sql
Normal file
105
database/postgres/init/03-strategy-risk-tables.sql
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
-- Strategy and Risk Management Tables
|
||||
|
||||
-- Strategies
|
||||
CREATE TABLE strategy.strategies (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
version VARCHAR(20) DEFAULT '1.0.0',
|
||||
config JSONB DEFAULT '{}',
|
||||
parameters JSONB DEFAULT '{}',
|
||||
is_active BOOLEAN DEFAULT false,
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
created_by VARCHAR(255),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Strategy executions/runs
|
||||
CREATE TABLE strategy.executions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
strategy_id UUID REFERENCES strategy.strategies(id),
|
||||
status VARCHAR(20) DEFAULT 'running' CHECK (status IN ('running', 'stopped', 'error', 'completed')),
|
||||
started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
stopped_at TIMESTAMP WITH TIME ZONE,
|
||||
error_message TEXT,
|
||||
execution_stats JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Strategy signals
|
||||
CREATE TABLE strategy.signals (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
strategy_id UUID REFERENCES strategy.strategies(id),
|
||||
symbol_id UUID REFERENCES trading.symbols(id),
|
||||
signal_type VARCHAR(20) NOT NULL CHECK (signal_type IN ('buy', 'sell', 'hold')),
|
||||
strength DECIMAL(3,2) CHECK (strength >= 0 AND strength <= 1), -- 0.0 to 1.0
|
||||
confidence DECIMAL(3,2) CHECK (confidence >= 0 AND confidence <= 1),
|
||||
target_price DECIMAL(18,8),
|
||||
stop_loss DECIMAL(18,8),
|
||||
take_profit DECIMAL(18,8),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Risk limits
|
||||
CREATE TABLE risk.limits (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
strategy_id UUID REFERENCES strategy.strategies(id),
|
||||
account_id UUID REFERENCES trading.accounts(id),
|
||||
limit_type VARCHAR(50) NOT NULL, -- 'max_position_size', 'max_daily_loss', 'max_drawdown', etc.
|
||||
limit_value DECIMAL(18,8) NOT NULL,
|
||||
current_value DECIMAL(18,8) DEFAULT 0,
|
||||
threshold_warning DECIMAL(18,8), -- Warning at X% of limit
|
||||
is_active BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Risk events/alerts
|
||||
CREATE TABLE risk.events (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
limit_id UUID REFERENCES risk.limits(id),
|
||||
strategy_id UUID,
|
||||
event_type VARCHAR(50) NOT NULL, -- 'warning', 'breach', 'resolved'
|
||||
severity VARCHAR(20) DEFAULT 'medium' CHECK (severity IN ('low', 'medium', 'high', 'critical')),
|
||||
message TEXT NOT NULL,
|
||||
current_value DECIMAL(18,8),
|
||||
limit_value DECIMAL(18,8),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
acknowledged BOOLEAN DEFAULT false,
|
||||
acknowledged_by VARCHAR(255),
|
||||
acknowledged_at TIMESTAMP WITH TIME ZONE,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Performance tracking
|
||||
CREATE TABLE strategy.performance (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
strategy_id UUID REFERENCES strategy.strategies(id),
|
||||
date DATE NOT NULL,
|
||||
total_return DECIMAL(10,4),
|
||||
daily_return DECIMAL(10,4),
|
||||
sharpe_ratio DECIMAL(10,4),
|
||||
max_drawdown DECIMAL(10,4),
|
||||
win_rate DECIMAL(5,4),
|
||||
profit_factor DECIMAL(10,4),
|
||||
total_trades INTEGER DEFAULT 0,
|
||||
winning_trades INTEGER DEFAULT 0,
|
||||
losing_trades INTEGER DEFAULT 0,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
UNIQUE(strategy_id, date)
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX idx_strategies_active ON strategy.strategies(is_active, is_enabled);
|
||||
CREATE INDEX idx_executions_strategy ON strategy.executions(strategy_id);
|
||||
CREATE INDEX idx_signals_strategy_time ON strategy.signals(strategy_id, created_at);
|
||||
CREATE INDEX idx_signals_symbol ON strategy.signals(symbol_id);
|
||||
CREATE INDEX idx_limits_strategy ON risk.limits(strategy_id);
|
||||
CREATE INDEX idx_risk_events_severity ON risk.events(severity, created_at);
|
||||
CREATE INDEX idx_performance_strategy_date ON strategy.performance(strategy_id, date);
|
||||
|
||||
-- Grant permissions
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA strategy TO trading_reader;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA risk TO trading_reader;
|
||||
59
database/postgres/init/04-audit-tables.sql
Normal file
59
database/postgres/init/04-audit-tables.sql
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
-- Audit and System Tables
|
||||
|
||||
-- System events audit
|
||||
CREATE TABLE audit.system_events (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
service_name VARCHAR(100) NOT NULL,
|
||||
event_type VARCHAR(50) NOT NULL,
|
||||
event_data JSONB DEFAULT '{}',
|
||||
user_id VARCHAR(255),
|
||||
ip_address INET,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Trading events audit
|
||||
CREATE TABLE audit.trading_events (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
event_type VARCHAR(50) NOT NULL, -- 'order_created', 'order_filled', 'position_opened', etc.
|
||||
entity_type VARCHAR(50) NOT NULL, -- 'order', 'position', 'execution'
|
||||
entity_id UUID NOT NULL,
|
||||
old_values JSONB,
|
||||
new_values JSONB,
|
||||
changed_by VARCHAR(255),
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Service health monitoring
|
||||
CREATE TABLE audit.service_health (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
service_name VARCHAR(100) NOT NULL,
|
||||
status VARCHAR(20) NOT NULL CHECK (status IN ('healthy', 'unhealthy', 'degraded')),
|
||||
version VARCHAR(50),
|
||||
uptime_seconds INTEGER,
|
||||
memory_usage_mb INTEGER,
|
||||
cpu_usage_percent DECIMAL(5,2),
|
||||
last_health_check TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
metadata JSONB DEFAULT '{}',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Configuration changes
|
||||
CREATE TABLE audit.config_changes (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
config_key VARCHAR(255) NOT NULL,
|
||||
old_value TEXT,
|
||||
new_value TEXT,
|
||||
changed_by VARCHAR(255),
|
||||
reason TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Create indexes
|
||||
CREATE INDEX idx_system_events_service_time ON audit.system_events(service_name, created_at);
|
||||
CREATE INDEX idx_trading_events_type_time ON audit.trading_events(event_type, created_at);
|
||||
CREATE INDEX idx_trading_events_entity ON audit.trading_events(entity_type, entity_id);
|
||||
CREATE INDEX idx_service_health_name_time ON audit.service_health(service_name, created_at);
|
||||
CREATE INDEX idx_config_changes_key_time ON audit.config_changes(config_key, created_at);
|
||||
|
||||
-- Grant permissions
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA audit TO trading_reader;
|
||||
55
database/postgres/init/05-initial-data.sql
Normal file
55
database/postgres/init/05-initial-data.sql
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
-- Insert initial reference data
|
||||
|
||||
-- Insert common symbols
|
||||
INSERT INTO trading.symbols (symbol, name, exchange, asset_type, sector) VALUES
|
||||
('AAPL', 'Apple Inc.', 'NASDAQ', 'equity', 'Technology'),
|
||||
('GOOGL', 'Alphabet Inc.', 'NASDAQ', 'equity', 'Technology'),
|
||||
('MSFT', 'Microsoft Corporation', 'NASDAQ', 'equity', 'Technology'),
|
||||
('AMZN', 'Amazon.com Inc.', 'NASDAQ', 'equity', 'Consumer Discretionary'),
|
||||
('TSLA', 'Tesla Inc.', 'NASDAQ', 'equity', 'Consumer Discretionary'),
|
||||
('NVDA', 'NVIDIA Corporation', 'NASDAQ', 'equity', 'Technology'),
|
||||
('META', 'Meta Platforms Inc.', 'NASDAQ', 'equity', 'Technology'),
|
||||
('NFLX', 'Netflix Inc.', 'NASDAQ', 'equity', 'Communication Services'),
|
||||
('SPY', 'SPDR S&P 500 ETF Trust', 'NYSE', 'etf', 'Broad Market'),
|
||||
('QQQ', 'Invesco QQQ Trust', 'NASDAQ', 'etf', 'Technology'),
|
||||
('BTC-USD', 'Bitcoin USD', 'CRYPTO', 'cryptocurrency', 'Digital Assets'),
|
||||
('ETH-USD', 'Ethereum USD', 'CRYPTO', 'cryptocurrency', 'Digital Assets');
|
||||
|
||||
-- Insert default trading account
|
||||
INSERT INTO trading.accounts (name, account_type, broker, cash_balance, buying_power, total_value) VALUES
|
||||
('Demo Account', 'paper', 'demo', 100000.00, 100000.00, 100000.00);
|
||||
|
||||
-- Insert demo strategy
|
||||
INSERT INTO strategy.strategies (name, description, config, parameters, is_active) VALUES
|
||||
('Demo Mean Reversion', 'Simple mean reversion strategy for demonstration',
|
||||
'{"timeframe": "1h", "lookback_period": 20}',
|
||||
'{"rsi_oversold": 30, "rsi_overbought": 70, "position_size": 0.1}',
|
||||
false);
|
||||
|
||||
-- Insert basic risk limits
|
||||
INSERT INTO risk.limits (strategy_id, limit_type, limit_value, threshold_warning)
|
||||
SELECT s.id, 'max_position_size', 10000.00, 8000.00
|
||||
FROM strategy.strategies s
|
||||
WHERE s.name = 'Demo Mean Reversion';
|
||||
|
||||
INSERT INTO risk.limits (strategy_id, limit_type, limit_value, threshold_warning)
|
||||
SELECT s.id, 'max_daily_loss', 5000.00, 4000.00
|
||||
FROM strategy.strategies s
|
||||
WHERE s.name = 'Demo Mean Reversion';
|
||||
|
||||
-- Create updated_at trigger function
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = NOW();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Apply updated_at triggers
|
||||
CREATE TRIGGER update_symbols_updated_at BEFORE UPDATE ON trading.symbols FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_orders_updated_at BEFORE UPDATE ON trading.orders FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_positions_updated_at BEFORE UPDATE ON trading.positions FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_accounts_updated_at BEFORE UPDATE ON trading.accounts FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_strategies_updated_at BEFORE UPDATE ON strategy.strategies FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
CREATE TRIGGER update_limits_updated_at BEFORE UPDATE ON risk.limits FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
|
||||
34
docker-compose.dev.yml
Normal file
34
docker-compose.dev.yml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Development override for Docker Compose
|
||||
# Use: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
||||
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Development overrides for faster feedback
|
||||
dragonfly:
|
||||
command: >
|
||||
dragonfly
|
||||
--logtostderr
|
||||
--cache_mode=true
|
||||
--maxmemory=512mb
|
||||
--save_schedule=""
|
||||
--bind=0.0.0.0
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
postgres:
|
||||
environment:
|
||||
POSTGRES_DB: trading_bot_dev
|
||||
POSTGRES_USER: dev_user
|
||||
POSTGRES_PASSWORD: dev_pass
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
# Disable monitoring in development to save resources
|
||||
prometheus:
|
||||
profiles:
|
||||
- monitoring
|
||||
|
||||
grafana:
|
||||
profiles:
|
||||
- monitoring
|
||||
151
docker-compose.yml
Normal file
151
docker-compose.yml
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
services:
|
||||
# Dragonfly - Redis replacement for caching and events
|
||||
dragonfly:
|
||||
image: docker.dragonflydb.io/dragonflydb/dragonfly:latest
|
||||
container_name: trading-bot-dragonfly
|
||||
ports:
|
||||
- "6379:6379"
|
||||
command:
|
||||
- dragonfly
|
||||
- --logtostderr
|
||||
- --cache_mode=true
|
||||
- --maxmemory=2gb
|
||||
- --proactor_threads=8
|
||||
- --bind=0.0.0.0
|
||||
volumes:
|
||||
- dragonfly_data:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
# PostgreSQL - Operational data (orders, positions, strategies)
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: trading-bot-postgres
|
||||
environment:
|
||||
POSTGRES_DB: trading_bot
|
||||
POSTGRES_USER: trading_user
|
||||
POSTGRES_PASSWORD: trading_pass_dev
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./database/postgres/init:/docker-entrypoint-initdb.d
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U trading_user -d trading_bot"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
# QuestDB - Time-series data (OHLCV, indicators, performance)
|
||||
questdb:
|
||||
image: questdb/questdb:latest
|
||||
container_name: trading-bot-questdb
|
||||
ports:
|
||||
- "9000:9000" # Web console
|
||||
- "8812:8812" # PostgreSQL wire protocol
|
||||
- "9009:9009" # InfluxDB line protocol
|
||||
volumes:
|
||||
- questdb_data:/var/lib/questdb
|
||||
environment:
|
||||
- QDB_TELEMETRY_ENABLED=false
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/status"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
# Redis Insight - GUI for Dragonfly debugging
|
||||
redis-insight:
|
||||
image: redislabs/redisinsight:latest
|
||||
container_name: trading-bot-redis-insight
|
||||
ports:
|
||||
- "8001:8001"
|
||||
environment:
|
||||
- REDIS_HOSTS=local:dragonfly:6379
|
||||
depends_on:
|
||||
- dragonfly
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
# PgAdmin - PostgreSQL GUI
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:latest
|
||||
container_name: trading-bot-pgadmin
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: admin@example.com
|
||||
PGADMIN_DEFAULT_PASSWORD: admin123
|
||||
PGADMIN_CONFIG_SERVER_MODE: 'False'
|
||||
PGADMIN_DISABLE_POSTFIX: 'true'
|
||||
ports:
|
||||
- "8080:80"
|
||||
volumes:
|
||||
- pgadmin_data:/var/lib/pgadmin
|
||||
depends_on:
|
||||
- postgres
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
# Prometheus - Metrics collection (optional)
|
||||
prometheus:
|
||||
image: prom/prometheus:latest
|
||||
container_name: trading-bot-prometheus
|
||||
ports:
|
||||
- "9090:9090"
|
||||
volumes:
|
||||
- ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
||||
- '--web.console.templates=/etc/prometheus/consoles'
|
||||
- '--web.enable-lifecycle'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
# Grafana - Metrics visualization (optional)
|
||||
grafana:
|
||||
image: grafana/grafana:latest
|
||||
container_name: trading-bot-grafana
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_PASSWORD=admin123
|
||||
- GF_SECURITY_ADMIN_USER=admin
|
||||
- GF_USERS_ALLOW_SIGN_UP=false
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./monitoring/grafana/provisioning:/etc/grafana/provisioning
|
||||
depends_on:
|
||||
- prometheus
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- trading-bot-network
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
questdb_data:
|
||||
dragonfly_data:
|
||||
pgadmin_data:
|
||||
prometheus_data:
|
||||
grafana_data:
|
||||
|
||||
networks:
|
||||
trading-bot-network:
|
||||
driver: bridge
|
||||
233
docs/current-system-flow.md
Normal file
233
docs/current-system-flow.md
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
# 🔄 Current System Communication Flow
|
||||
|
||||
## Active Services (Currently Running)
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
%% External Data Sources
|
||||
subgraph "External APIs"
|
||||
EXT[Demo Data Generation<br/>Alpha Vantage Ready<br/>Yahoo Finance Ready]
|
||||
end
|
||||
|
||||
%% Currently Active Services
|
||||
subgraph "Active Services"
|
||||
MDG[Market Data Gateway<br/>Port 3001<br/>✅ Running]
|
||||
TD[Trading Dashboard<br/>Port 5173<br/>✅ Running]
|
||||
end
|
||||
|
||||
%% Storage Layer
|
||||
subgraph "Storage"
|
||||
DRAGONFLY[(Dragonfly<br/>📡 Events & Cache<br/>🔧 Configured)]
|
||||
end
|
||||
|
||||
%% Data Flow
|
||||
EXT -->|HTTP Fetch| MDG
|
||||
MDG -->|REST API| TD
|
||||
MDG -->|WebSocket Stream| TD
|
||||
MDG -->|Events| DRAGONFLY
|
||||
|
||||
%% Styling
|
||||
classDef active fill:#90EE90
|
||||
classDef storage fill:#FFE4B5
|
||||
classDef external fill:#FFB6C1
|
||||
|
||||
class MDG,TD active
|
||||
class DRAGONFLY storage
|
||||
class EXT external
|
||||
```
|
||||
|
||||
## Next Phase Services (Ready to Implement)
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
%% Current Services
|
||||
subgraph "Current Layer"
|
||||
MDG[Market Data Gateway<br/>✅ Operational]
|
||||
TD[Trading Dashboard<br/>✅ Operational]
|
||||
end
|
||||
|
||||
%% Next Phase Services
|
||||
subgraph "Next Phase - Priority 1"
|
||||
SO[Strategy Orchestrator<br/>🚧 Package Created<br/>📋 Ready to Implement]
|
||||
RG[Risk Guardian<br/>🚧 Package Created<br/>📋 Ready to Implement]
|
||||
end
|
||||
|
||||
%% Communication Infrastructure
|
||||
subgraph "Event Infrastructure"
|
||||
DRAGONFLY[(Dragonfly Streams<br/>✅ Configured)]
|
||||
WS[WebSocket Server<br/>✅ Active in MDG]
|
||||
end
|
||||
|
||||
%% Data Flows
|
||||
MDG -->|Market Data Events| DRAGONFLY
|
||||
MDG -->|Real-time Stream| WS
|
||||
WS -->|Live Updates| TD
|
||||
DRAGONFLY -->|Market Events| SO
|
||||
DRAGONFLY -->|All Events| RG
|
||||
|
||||
SO -->|Strategy Events| DRAGONFLY
|
||||
SO -->|Risk Check| RG
|
||||
RG -->|Risk Alerts| DRAGONFLY
|
||||
|
||||
%% Styling
|
||||
classDef current fill:#90EE90
|
||||
classDef next fill:#FFE4B5
|
||||
classDef infrastructure fill:#E6E6FA
|
||||
|
||||
class MDG,TD current
|
||||
class SO,RG next
|
||||
class DRAGONFLY,WS infrastructure
|
||||
```
|
||||
|
||||
## Detailed Communication Patterns
|
||||
|
||||
### 1. **Current System (Working)**
|
||||
|
||||
```
|
||||
┌─────────────────┐ HTTP REST ┌─────────────────┐
|
||||
│ Trading │ ←──────────────→ │ Market Data │
|
||||
│ Dashboard │ │ Gateway │
|
||||
│ (React/Tremor) │ ←──────────────→ │ (Hono/Bun) │
|
||||
└─────────────────┘ WebSocket └─────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Dragonfly Events │
|
||||
│ (Configured) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### 2. **Next Phase Implementation**
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Strategy │
|
||||
│ Orchestrator │ ──┐
|
||||
└─────────────────┘ │
|
||||
▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Market Data │→│ Dragonfly Event │←│ Risk Guardian │
|
||||
│ Gateway │ │ Stream │ │ │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Trading │ │ WebSocket │ │ Alert Manager │
|
||||
│ Dashboard │ │ Real-time │ │ (Future) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
## Event Flow Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant TD as Trading Dashboard
|
||||
participant MDG as Market Data Gateway
|
||||
participant DRAGONFLY as Dragonfly Events
|
||||
participant SO as Strategy Orchestrator (Next)
|
||||
participant RG as Risk Guardian (Next)
|
||||
|
||||
Note over TD,DRAGONFLY: Current System - Working
|
||||
|
||||
MDG->>MDG: Generate demo market data
|
||||
MDG->>TD: WebSocket real-time updates
|
||||
MDG->>DRAGONFLY: Publish MARKET_DATA events
|
||||
TD->>MDG: HTTP API requests
|
||||
MDG->>TD: JSON responses
|
||||
|
||||
Note over SO,RG: Next Phase - To Implement
|
||||
|
||||
DRAGONFLY->>SO: Subscribe to MARKET_DATA events
|
||||
SO->>SO: Analyze market conditions
|
||||
SO->>DRAGONFLY: Publish SIGNAL_GENERATED events
|
||||
|
||||
DRAGONFLY->>RG: Subscribe to ALL events
|
||||
RG->>RG: Monitor risk thresholds
|
||||
RG->>DRAGONFLY: Publish RISK_ALERT events
|
||||
|
||||
DRAGONFLY->>TD: All events via WebSocket
|
||||
TD->>TD: Update dashboard with alerts
|
||||
```
|
||||
|
||||
## Service Dependencies
|
||||
|
||||
### **Current Dependencies (Satisfied)**
|
||||
```
|
||||
Market Data Gateway
|
||||
├── ✅ Hono (Web framework)
|
||||
├── ✅ ioredis (Redis client)
|
||||
├── ✅ @stock-bot/config (Workspace package)
|
||||
├── ✅ @stock-bot/shared-types (Workspace package)
|
||||
└── ✅ ws (WebSocket library)
|
||||
|
||||
Trading Dashboard
|
||||
├── ✅ React + TypeScript
|
||||
├── ✅ Tremor UI (Financial components)
|
||||
├── ✅ Vite (Build tool)
|
||||
└── ✅ WebSocket client
|
||||
```
|
||||
|
||||
### **Next Phase Dependencies (Ready)**
|
||||
```
|
||||
Strategy Orchestrator
|
||||
├── ✅ Package.json created
|
||||
├── ✅ Dependencies specified
|
||||
├── 📋 Implementation needed
|
||||
└── 🔧 node-cron (Strategy scheduling)
|
||||
|
||||
Risk Guardian
|
||||
├── ✅ Package.json created
|
||||
├── ✅ Dependencies specified
|
||||
├── 📋 Implementation needed
|
||||
└── 🛡️ Risk monitoring logic
|
||||
```
|
||||
|
||||
## Port & Endpoint Map
|
||||
|
||||
| Service | Port | Endpoints | Status |
|
||||
|---------|------|-----------|--------|
|
||||
| **Market Data Gateway** | 3001 | `/health`, `/api/market-data/:symbol`, `/api/ohlcv/:symbol`, WebSocket | ✅ Active |
|
||||
| **Trading Dashboard** | 5173 | Vite dev server | ✅ Active |
|
||||
| **Strategy Orchestrator** | 4001 | `/health`, `/api/strategies`, `/api/signals` | 📋 Planned |
|
||||
| **Risk Guardian** | 3002 | `/health`, `/api/risk-checks`, `/api/limits` | 📋 Planned |
|
||||
|
||||
## Data Types & Events
|
||||
|
||||
### **Market Data Event**
|
||||
```typescript
|
||||
interface MarketDataEvent {
|
||||
type: 'MARKET_DATA';
|
||||
data: {
|
||||
symbol: string;
|
||||
price: number;
|
||||
bid: number;
|
||||
ask: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
};
|
||||
timestamp: Date;
|
||||
}
|
||||
```
|
||||
|
||||
### **Strategy Event (Next Phase)**
|
||||
```typescript
|
||||
interface StrategyEvent {
|
||||
type: 'SIGNAL_GENERATED' | 'STRATEGY_START' | 'STRATEGY_STOP';
|
||||
strategyId: string;
|
||||
signal?: TradingSignal;
|
||||
timestamp: Date;
|
||||
}
|
||||
```
|
||||
|
||||
### **Risk Event (Next Phase)**
|
||||
```typescript
|
||||
interface RiskEvent {
|
||||
type: 'RISK_ALERT' | 'RISK_CHECK';
|
||||
severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL';
|
||||
message: string;
|
||||
data: any;
|
||||
timestamp: Date;
|
||||
}
|
||||
```
|
||||
|
||||
This architecture shows how your current working system will expand into a comprehensive trading platform with clear communication patterns and event flows.
|
||||
358
docs/system-architecture.md
Normal file
358
docs/system-architecture.md
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
# 🏗️ Stock Bot System Architecture
|
||||
|
||||
## System Communication Flow Diagram
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
%% External Systems
|
||||
subgraph "External APIs"
|
||||
AV[Alpha Vantage API]
|
||||
YF[Yahoo Finance API]
|
||||
AL[Alpaca Broker API]
|
||||
IB[Interactive Brokers]
|
||||
NEWS[News APIs]
|
||||
end
|
||||
|
||||
%% Core Services Layer
|
||||
subgraph "Core Services"
|
||||
MDG[Market Data Gateway<br/>:3001]
|
||||
RG[Risk Guardian<br/>:3002]
|
||||
EE[Execution Engine<br/>:3003]
|
||||
PM[Portfolio Manager<br/>:3004]
|
||||
end
|
||||
|
||||
%% Intelligence Services Layer
|
||||
subgraph "Intelligence Services"
|
||||
SO[Strategy Orchestrator<br/>:4001]
|
||||
SG[Signal Generator<br/>:4002]
|
||||
BA[Backtesting Engine<br/>:4003]
|
||||
ML[ML Pipeline<br/>:4004]
|
||||
end
|
||||
|
||||
%% Data Services Layer
|
||||
subgraph "Data Services"
|
||||
HDS[Historical Data Service<br/>:5001]
|
||||
AS[Analytics Service<br/>:5002]
|
||||
DQS[Data Quality Service<br/>:5003]
|
||||
ETLS[ETL Service<br/>:5004]
|
||||
end
|
||||
|
||||
%% Platform Services Layer
|
||||
subgraph "Platform Services"
|
||||
LM[Log Manager<br/>:6001]
|
||||
CM[Config Manager<br/>:6002]
|
||||
AM[Alert Manager<br/>:6003]
|
||||
SM[Service Monitor<br/>:6004]
|
||||
end
|
||||
|
||||
%% Integration Services Layer
|
||||
subgraph "Integration Services"
|
||||
BAS[Broker Adapter<br/>:7001]
|
||||
DAS[Data Adapter<br/>:7002]
|
||||
NS[Notification Service<br/>:7003]
|
||||
WHS[Webhook Service<br/>:7004]
|
||||
end
|
||||
|
||||
%% Interface Services Layer
|
||||
subgraph "Interface Services"
|
||||
TD[Trading Dashboard<br/>:5173]
|
||||
API[REST API Gateway<br/>:8001]
|
||||
WS[WebSocket Server<br/>Embedded]
|
||||
end
|
||||
|
||||
%% Storage Layer
|
||||
subgraph "Storage Layer"
|
||||
DRAGONFLY[(Dragonfly<br/>Events & Cache)]
|
||||
QDB[(QuestDB<br/>Time Series)]
|
||||
PGDB[(PostgreSQL<br/>Relational)]
|
||||
FS[(File System<br/>Logs & Config)]
|
||||
end
|
||||
|
||||
%% Communication Flows
|
||||
|
||||
%% External to Core
|
||||
AV --> MDG
|
||||
YF --> MDG
|
||||
AL --> BAS
|
||||
IB --> BAS
|
||||
NEWS --> DAS
|
||||
|
||||
%% Core Service Communications
|
||||
MDG -->|Market Data Events| DRAGONFLY
|
||||
MDG -->|Real-time Stream| WS
|
||||
MDG -->|Cache| DRAGONFLY
|
||||
|
||||
RG -->|Risk Events| DRAGONFLY
|
||||
RG -->|Risk Alerts| AM
|
||||
RG -->|Position Limits| PM
|
||||
|
||||
EE -->|Order Events| DRAGONFLY
|
||||
EE -->|Trade Execution| BAS
|
||||
EE -->|Order Status| PM
|
||||
|
||||
PM -->|Portfolio Events| DRAGONFLY
|
||||
PM -->|P&L Updates| TD
|
||||
PM -->|Position Data| RG
|
||||
|
||||
%% Intelligence Communications
|
||||
SO -->|Strategy Events| DRAGONFLY
|
||||
SO -->|Signal Requests| SG
|
||||
SO -->|Execution Orders| EE
|
||||
SO -->|Risk Check| RG
|
||||
|
||||
SG -->|Trading Signals| SO
|
||||
SG -->|ML Requests| ML
|
||||
SG -->|Market Data| DRAGONFLY
|
||||
|
||||
BA -->|Backtest Results| SO
|
||||
BA -->|Historical Data| HDS
|
||||
|
||||
ML -->|Predictions| SG
|
||||
ML -->|Training Data| HDS
|
||||
|
||||
%% Data Service Communications
|
||||
HDS -->|Store Data| QDB
|
||||
HDS -->|Query Data| QDB
|
||||
HDS -->|Data Events| DRAGONFLY
|
||||
|
||||
AS -->|Analytics| QDB
|
||||
AS -->|Metrics| SM
|
||||
AS -->|Reports| TD
|
||||
|
||||
DQS -->|Data Quality| DRAGONFLY
|
||||
DQS -->|Alerts| AM
|
||||
|
||||
ETLS -->|Raw Data| DAS
|
||||
ETLS -->|Processed Data| HDS
|
||||
|
||||
%% Platform Communications
|
||||
LM -->|Logs| FS
|
||||
LM -->|Log Events| DRAGONFLY
|
||||
|
||||
CM -->|Config| FS
|
||||
CM -->|Config Updates| DRAGONFLY
|
||||
|
||||
AM -->|Alerts| NS
|
||||
AM -->|Alert Events| DRAGONFLY
|
||||
|
||||
SM -->|Health Checks| DRAGONFLY
|
||||
SM -->|Metrics| QDB
|
||||
|
||||
%% Integration Communications
|
||||
BAS -->|Orders| AL
|
||||
BAS -->|Orders| IB
|
||||
BAS -->|Order Updates| EE
|
||||
|
||||
DAS -->|Data Feed| MDG
|
||||
DAS -->|External Data| HDS
|
||||
|
||||
NS -->|Notifications| WHS
|
||||
NS -->|Alerts| TD
|
||||
|
||||
WHS -->|Webhooks| External
|
||||
|
||||
%% Interface Communications
|
||||
TD -->|API Calls| API
|
||||
TD -->|WebSocket| WS
|
||||
TD -->|Dashboard Data| PM
|
||||
|
||||
API -->|Service Calls| SO
|
||||
API -->|Data Queries| HDS
|
||||
API -->|System Status| SM
|
||||
|
||||
WS -->|Real-time Data| TD
|
||||
WS -->|Events| DRAGONFLY
|
||||
|
||||
%% Storage Access DRAGONFLY -.->|Events| SO
|
||||
DRAGONFLY -.->|Events| RG
|
||||
DRAGONFLY -.->|Events| PM
|
||||
DRAGONFLY -.->|Cache| MDG
|
||||
|
||||
QDB -.->|Time Series| HDS
|
||||
QDB -.->|Analytics| AS
|
||||
QDB -.->|Metrics| SM
|
||||
|
||||
PGDB -.->|Relational| PM
|
||||
PGDB -.->|Config| CM
|
||||
PGDB -.->|Users| API
|
||||
|
||||
%% Styling
|
||||
classDef external fill:#ff9999
|
||||
classDef core fill:#99ccff
|
||||
classDef intelligence fill:#99ff99
|
||||
classDef data fill:#ffcc99
|
||||
classDef platform fill:#cc99ff
|
||||
classDef integration fill:#ffff99
|
||||
classDef interface fill:#ff99cc
|
||||
classDef storage fill:#cccccc
|
||||
|
||||
class AV,YF,AL,IB,NEWS external
|
||||
class MDG,RG,EE,PM core
|
||||
class SO,SG,BA,ML intelligence
|
||||
class HDS,AS,DQS,ETLS data
|
||||
class LM,CM,AM,SM platform
|
||||
class BAS,DAS,NS,WHS integration
|
||||
class TD,API,WS interface
|
||||
class DRAGONFLY,QDB,PGDB,FS storage
|
||||
```
|
||||
|
||||
## Communication Patterns
|
||||
|
||||
### 1. **Event-Driven Architecture (Dragonfly Streams)**
|
||||
```
|
||||
┌─────────────┐ Dragonfly Events ┌─────────────┐
|
||||
│ Service │ ─────────────────→ │ Service │
|
||||
│ A │ │ B │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
**Event Types:**
|
||||
- `MARKET_DATA` - Real-time price updates
|
||||
- `ORDER_CREATED/FILLED/CANCELLED` - Order lifecycle
|
||||
- `SIGNAL_GENERATED` - Trading signals
|
||||
- `RISK_ALERT` - Risk threshold violations
|
||||
- `PORTFOLIO_UPDATE` - Position changes
|
||||
- `STRATEGY_START/STOP` - Strategy lifecycle
|
||||
|
||||
### 2. **Request-Response (HTTP/REST)**
|
||||
```
|
||||
┌─────────────┐ HTTP Request ┌─────────────┐
|
||||
│ Client │ ─────────────────→ │ Service │
|
||||
│ │ ←───────────────── │ │
|
||||
└─────────────┘ HTTP Response └─────────────┘
|
||||
```
|
||||
|
||||
**API Endpoints:**
|
||||
- `/api/market-data/:symbol` - Current market data
|
||||
- `/api/portfolio/positions` - Portfolio positions
|
||||
- `/api/strategies` - Strategy management
|
||||
- `/api/orders` - Order management
|
||||
- `/api/health` - Service health checks
|
||||
|
||||
### 3. **Real-time Streaming (WebSocket)**
|
||||
```
|
||||
┌─────────────┐ WebSocket ┌─────────────┐
|
||||
│ Client │ ←═════════════════→ │ Server │
|
||||
│ │ Bidirectional │ │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
**WebSocket Messages:**
|
||||
- Market data subscriptions
|
||||
- Portfolio updates
|
||||
- Trading signals
|
||||
- Risk alerts
|
||||
- System notifications
|
||||
|
||||
### 4. **Data Persistence**
|
||||
```
|
||||
┌─────────────┐ Store/Query ┌─────────────┐
|
||||
│ Service │ ─────────────────→ │ Database │
|
||||
│ │ ←───────────────── │ │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
**Storage Types:**
|
||||
- **Dragonfly**: Events, cache, sessions
|
||||
- **QuestDB**: Time-series data, metrics
|
||||
- **PostgreSQL**: Configuration, users, metadata
|
||||
- **File System**: Logs, configurations
|
||||
|
||||
## Service Communication Matrix
|
||||
|
||||
| Service | Publishes Events | Subscribes to Events | HTTP APIs | WebSocket | Storage |
|
||||
|---------|-----------------|---------------------|-----------|-----------|---------|
|
||||
| Market Data Gateway | ✅ Market Data | - | ✅ REST | ✅ Server | Dragonfly Cache |
|
||||
| Risk Guardian | ✅ Risk Alerts | ✅ All Events | ✅ REST | - | PostgreSQL |
|
||||
| Strategy Orchestrator | ✅ Strategy Events | ✅ Market Data, Signals | ✅ REST | - | PostgreSQL |
|
||||
| Execution Engine | ✅ Order Events | ✅ Strategy Events | ✅ REST | - | PostgreSQL |
|
||||
| Portfolio Manager | ✅ Portfolio Events | ✅ Order Events | ✅ REST | - | PostgreSQL |
|
||||
| Trading Dashboard | - | ✅ All Events | ✅ Client | ✅ Client | - |
|
||||
|
||||
## Data Flow Example: Trade Execution
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant TD as Trading Dashboard
|
||||
participant SO as Strategy Orchestrator
|
||||
participant SG as Signal Generator
|
||||
participant RG as Risk Guardian
|
||||
participant EE as Execution Engine
|
||||
participant BAS as Broker Adapter
|
||||
participant PM as Portfolio Manager
|
||||
participant DRAGONFLY as Dragonfly Events
|
||||
|
||||
Note over TD,DRAGONFLY: User starts a trading strategy
|
||||
|
||||
TD->>SO: POST /api/strategies/start
|
||||
SO->>DRAGONFLY: Publish STRATEGY_START event
|
||||
|
||||
Note over SO,SG: Strategy generates signals
|
||||
|
||||
SO->>SG: Request signals for AAPL
|
||||
SG->>SO: Return BUY signal
|
||||
SO->>DRAGONFLY: Publish SIGNAL_GENERATED event
|
||||
|
||||
Note over SO,RG: Risk check before execution
|
||||
|
||||
SO->>RG: Check risk limits
|
||||
RG->>SO: Risk approved
|
||||
|
||||
Note over SO,EE: Execute the trade
|
||||
|
||||
SO->>EE: Submit order
|
||||
EE->>DRAGONFLY: Publish ORDER_CREATED event
|
||||
EE->>BAS: Send order to broker
|
||||
BAS->>EE: Order filled
|
||||
EE->>DRAGONFLY: Publish ORDER_FILLED event
|
||||
|
||||
Note over PM,TD: Update portfolio and notify user
|
||||
|
||||
PM->>DRAGONFLY: Subscribe to ORDER_FILLED
|
||||
PM->>PM: Update positions PM->>DRAGONFLY: Publish PORTFOLIO_UPDATE
|
||||
TD->>DRAGONFLY: Subscribe to PORTFOLIO_UPDATE
|
||||
TD->>TD: Update dashboard
|
||||
```
|
||||
|
||||
## Port Allocation
|
||||
|
||||
| Service Category | Port Range | Services |
|
||||
|-----------------|------------|----------|
|
||||
| Core Services | 3001-3099 | Market Data Gateway (3001), Risk Guardian (3002) |
|
||||
| Intelligence Services | 4001-4099 | Strategy Orchestrator (4001), Signal Generator (4002) |
|
||||
| Data Services | 5001-5099 | Historical Data (5001), Analytics (5002) |
|
||||
| Platform Services | 6001-6099 | Log Manager (6001), Config Manager (6002) |
|
||||
| Integration Services | 7001-7099 | Broker Adapter (7001), Data Adapter (7002) |
|
||||
| Interface Services | 8001-8099 | API Gateway (8001), Dashboard (5173-Vite) |
|
||||
|
||||
## Security & Authentication
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "Security Layer"
|
||||
JWT[JWT Tokens]
|
||||
API_KEY[API Keys]
|
||||
TLS[TLS/HTTPS]
|
||||
RBAC[Role-Based Access]
|
||||
end
|
||||
|
||||
subgraph "External Security"
|
||||
BROKER_AUTH[Broker Authentication]
|
||||
DATA_AUTH[Data Provider Auth]
|
||||
WEBHOOK_SIG[Webhook Signatures]
|
||||
end
|
||||
|
||||
JWT --> API_KEY
|
||||
API_KEY --> TLS
|
||||
TLS --> RBAC
|
||||
RBAC --> BROKER_AUTH
|
||||
BROKER_AUTH --> DATA_AUTH
|
||||
DATA_AUTH --> WEBHOOK_SIG
|
||||
```
|
||||
|
||||
This architecture provides:
|
||||
- **Scalability**: Services can be scaled independently
|
||||
- **Reliability**: Event-driven communication with retry mechanisms
|
||||
- **Maintainability**: Clear separation of concerns
|
||||
- **Observability**: Centralized logging and monitoring
|
||||
- **Security**: Multiple layers of authentication and authorization
|
||||
140
docs/system-communication-chart.md
Normal file
140
docs/system-communication-chart.md
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
# 📊 Stock Bot System Communication - Quick Reference
|
||||
|
||||
## Current System (Active)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ TRADING BOT SYSTEM │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
External APIs Core Services Interface Services
|
||||
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Demo Data │──────▶│ Market Data │◀──────▶│ Trading │
|
||||
│ Alpha Vant. │ │ Gateway │ │ Dashboard │
|
||||
│ Yahoo Fin. │ │ Port: 3001 │ │ Port: 5173 │
|
||||
└─────────────┘ │ Status: ✅ LIVE │ │ Status: ✅ LIVE │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
│ ▲
|
||||
▼ │
|
||||
┌─────────────────┐ │
|
||||
│ Dragonfly Events │─────────────────┘
|
||||
│ Cache & Streams │
|
||||
│ Status: ✅ READY│
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Next Phase (Ready to Implement)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ EXPANDED TRADING SYSTEM │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
|
||||
Intelligence Services Core Services Interface Services
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Strategy │◀────▶│ Market Data │◀───▶│ Trading │
|
||||
│ Orchestrator │ │ Gateway │ │ Dashboard │
|
||||
│ Port: 4001 │ │ Port: 3001 │ │ Port: 5173 │
|
||||
│ Status: 📋 PLAN │ │ Status: ✅ LIVE │ │ Status: ✅ LIVE │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
▲ │ ▲
|
||||
│ ▼ │
|
||||
│ ┌─────────────────┐ │
|
||||
└──────────────▶│ Dragonfly Event │◀─────────────┘
|
||||
│ Stream Hub │
|
||||
│ Status: ✅ READY│
|
||||
└─────────────────┘
|
||||
▲
|
||||
│
|
||||
┌─────────────────┐
|
||||
│ Risk Guardian │
|
||||
│ Port: 3002 │
|
||||
│ Status: 📋 PLAN │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
## Communication Protocols
|
||||
|
||||
### HTTP REST API
|
||||
```
|
||||
Client ──── GET/POST ───▶ Server
|
||||
◀─── JSON ────────
|
||||
```
|
||||
|
||||
### WebSocket Real-time
|
||||
```
|
||||
Client ◀═══ Stream ═══▶ Server
|
||||
◀═══ Events ══▶
|
||||
```
|
||||
|
||||
### Dragonfly Event Bus
|
||||
```
|
||||
Service A ──── Publish ───▶ Dragonfly ──── Subscribe ───▶ Service B
|
||||
◀─── Confirm ──── ◀─── Events ────
|
||||
```
|
||||
|
||||
## Event Types
|
||||
|
||||
| Event Type | Publisher | Subscribers | Frequency |
|
||||
|------------|-----------|-------------|-----------|
|
||||
| `MARKET_DATA` | Market Data Gateway | Dashboard, Strategy Orchestrator | Every 5s |
|
||||
| `SIGNAL_GENERATED` | Strategy Orchestrator | Risk Guardian, Execution Engine | As needed |
|
||||
| `RISK_ALERT` | Risk Guardian | Dashboard, Alert Manager | As needed |
|
||||
| `PORTFOLIO_UPDATE` | Portfolio Manager | Dashboard, Risk Guardian | On trades |
|
||||
|
||||
## Service Status Matrix
|
||||
|
||||
| Service | Port | Status | Dependencies | Ready to Implement |
|
||||
|---------|------|--------|--------------|-------------------|
|
||||
| Market Data Gateway | 3001 | ✅ Running | Dragonfly, Config | ✅ Complete |
|
||||
| Trading Dashboard | 5173 | ✅ Running | MDG API | ✅ Complete |
|
||||
| Strategy Orchestrator | 4001 | 📋 Planned | Dragonfly, MDG | ✅ Package Ready |
|
||||
| Risk Guardian | 3002 | 📋 Planned | Dragonfly, Config | ✅ Package Ready |
|
||||
| Portfolio Manager | 3004 | ⏳ Future | Database, Orders | ❌ Not Started |
|
||||
| Execution Engine | 3003 | ⏳ Future | Brokers, Portfolio | ❌ Not Started |
|
||||
|
||||
## Data Flow Summary
|
||||
|
||||
1. **Market Data Flow**
|
||||
```
|
||||
External APIs → Market Data Gateway → Dragonfly Events → Dashboard
|
||||
→ Strategy Orchestrator
|
||||
```
|
||||
|
||||
2. **Trading Signal Flow**
|
||||
```
|
||||
Market Data → Strategy Orchestrator → Trading Signals → Risk Guardian
|
||||
→ Execution Engine
|
||||
```
|
||||
|
||||
3. **Risk Management Flow**
|
||||
```
|
||||
All Events → Risk Guardian → Risk Alerts → Alert Manager
|
||||
→ Risk Blocks → Strategy Orchestrator
|
||||
```
|
||||
|
||||
4. **User Interface Flow**
|
||||
```
|
||||
WebSocket ← Dashboard → REST API → Services
|
||||
Events ← → Commands →
|
||||
```
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
### Phase 1 (Current) ✅
|
||||
- [x] Market Data Gateway
|
||||
- [x] Trading Dashboard
|
||||
- [x] Dragonfly Infrastructure
|
||||
- [x] WebSocket Communication
|
||||
|
||||
### Phase 2 (Next) 📋
|
||||
- [ ] Strategy Orchestrator
|
||||
- [ ] Risk Guardian
|
||||
- [ ] Event-driven Strategy Execution
|
||||
- [ ] Risk Monitoring & Alerts
|
||||
|
||||
### Phase 3 (Future) ⏳
|
||||
- [ ] Portfolio Manager
|
||||
- [ ] Execution Engine
|
||||
- [ ] Broker Integration
|
||||
- [ ] Database Persistence
|
||||
|
||||
The system is designed for incremental development where each service can be implemented and tested independently while maintaining full system functionality.
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: true
|
||||
45
monitoring/prometheus.yml
Normal file
45
monitoring/prometheus.yml
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
rule_files:
|
||||
# - "first_rules.yml"
|
||||
# - "second_rules.yml"
|
||||
|
||||
scrape_configs:
|
||||
# Prometheus itself
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
# Trading Bot Services
|
||||
- job_name: 'market-data-gateway'
|
||||
static_configs:
|
||||
- targets: ['host.docker.internal:3001']
|
||||
metrics_path: '/metrics'
|
||||
scrape_interval: 5s
|
||||
|
||||
- job_name: 'strategy-orchestrator'
|
||||
static_configs:
|
||||
- targets: ['host.docker.internal:4001']
|
||||
metrics_path: '/metrics'
|
||||
scrape_interval: 10s
|
||||
|
||||
- job_name: 'risk-guardian'
|
||||
static_configs:
|
||||
- targets: ['host.docker.internal:3002']
|
||||
metrics_path: '/metrics'
|
||||
scrape_interval: 10s
|
||||
|
||||
# Infrastructure
|
||||
- job_name: 'dragonfly'
|
||||
static_configs:
|
||||
- targets: ['dragonfly:6379']
|
||||
|
||||
- job_name: 'postgres'
|
||||
static_configs:
|
||||
- targets: ['postgres:5432']
|
||||
|
||||
- job_name: 'questdb'
|
||||
static_configs:
|
||||
- targets: ['questdb:9000']
|
||||
26
monitoring/prometheus/prometheus.yml
Normal file
26
monitoring/prometheus/prometheus.yml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
rule_files:
|
||||
# - "first_rules.yml"
|
||||
# - "second_rules.yml"
|
||||
|
||||
scrape_configs:
|
||||
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
# Add other services as they become available
|
||||
# - job_name: 'trading-bot'
|
||||
# static_configs:
|
||||
# - targets: ['localhost:3001']
|
||||
|
||||
# - job_name: 'market-data-gateway'
|
||||
# static_configs:
|
||||
# - targets: ['localhost:3002']
|
||||
|
||||
# - job_name: 'risk-guardian'
|
||||
# static_configs:
|
||||
# - targets: ['localhost:3003']
|
||||
43
package.json
Normal file
43
package.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "stock-bot",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"description": "Advanced trading bot with microservice architecture", "scripts": {
|
||||
"dev": "turbo run dev",
|
||||
"build": "turbo run build",
|
||||
"test": "turbo run test",
|
||||
"lint": "turbo run lint",
|
||||
"clean": "turbo run clean",
|
||||
"start": "turbo run start",
|
||||
"backtest": "turbo run backtest",
|
||||
|
||||
"docker:start": "pwsh ./scripts/docker.ps1 start",
|
||||
"docker:stop": "pwsh ./scripts/docker.ps1 stop",
|
||||
"docker:restart": "pwsh ./scripts/docker.ps1 restart",
|
||||
"docker:status": "pwsh ./scripts/docker.ps1 status",
|
||||
"docker:logs": "pwsh ./scripts/docker.ps1 logs",
|
||||
"docker:reset": "pwsh ./scripts/docker.ps1 reset",
|
||||
"docker:admin": "pwsh ./scripts/docker.ps1 admin",
|
||||
"docker:monitoring": "pwsh ./scripts/docker.ps1 monitoring",
|
||||
|
||||
"infra:up": "docker-compose up -d dragonfly postgres questdb",
|
||||
"infra:down": "docker-compose down",
|
||||
"infra:reset": "docker-compose down -v && docker-compose up -d dragonfly postgres questdb",
|
||||
|
||||
"dev:full": "npm run infra:up && npm run docker:admin && turbo run dev",
|
||||
"dev:clean": "npm run infra:reset && npm run dev:full"
|
||||
},"workspaces": [
|
||||
"packages/*",
|
||||
"apps/*/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.12",
|
||||
"turbo": "^2.0.5",
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"packageManager": "bun@1.1.12",
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"bun": ">=1.1.0"
|
||||
}
|
||||
}
|
||||
21
packages/config/package.json
Normal file
21
packages/config/package.json
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "@stock-bot/config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared configuration for the trading bot",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch",
|
||||
"clean": "rm -rf dist",
|
||||
"test": "echo \"No tests yet\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@stock-bot/shared-types": "workspace:*",
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5",
|
||||
"@types/node": "^20.12.12"
|
||||
}
|
||||
}
|
||||
156
packages/config/src/index.ts
Normal file
156
packages/config/src/index.ts
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import { config } from 'dotenv';
|
||||
import type { DatabaseConfig, BrokerConfig, DataProviderConfig } from '@stock-bot/shared-types';
|
||||
|
||||
// Load environment variables
|
||||
config();
|
||||
|
||||
export const env = {
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
PORT: parseInt(process.env.PORT || '3000'),
|
||||
// Database URLs
|
||||
QUESTDB_URL: process.env.QUESTDB_URL || 'postgresql://admin:quest@localhost:8812/qdb',
|
||||
POSTGRES_URL: process.env.POSTGRES_URL || 'postgresql://postgres:password@localhost:5432/stockbot',
|
||||
DRAGONFLY_URL: process.env.DRAGONFLY_URL || 'redis://localhost:6379',
|
||||
|
||||
// API Keys
|
||||
ALPHA_VANTAGE_API_KEY: process.env.ALPHA_VANTAGE_API_KEY || '',
|
||||
ALPACA_API_KEY: process.env.ALPACA_API_KEY || '',
|
||||
ALPACA_SECRET_KEY: process.env.ALPACA_SECRET_KEY || '',
|
||||
|
||||
// Trading Configuration
|
||||
PAPER_TRADING: process.env.PAPER_TRADING === 'true',
|
||||
MAX_POSITION_SIZE: parseFloat(process.env.MAX_POSITION_SIZE || '0.1'),
|
||||
MAX_DAILY_LOSS: parseFloat(process.env.MAX_DAILY_LOSS || '1000'),
|
||||
|
||||
// Logging
|
||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||
|
||||
// Feature Flags
|
||||
ENABLE_ML_SIGNALS: process.env.ENABLE_ML_SIGNALS === 'true',
|
||||
ENABLE_SENTIMENT_ANALYSIS: process.env.ENABLE_SENTIMENT_ANALYSIS === 'true',
|
||||
} as const;
|
||||
|
||||
export const databaseConfig: DatabaseConfig = {
|
||||
questdb: {
|
||||
host: process.env.QUESTDB_HOST || 'localhost',
|
||||
port: parseInt(process.env.QUESTDB_PORT || '9000'),
|
||||
database: process.env.QUESTDB_DATABASE || 'qdb',
|
||||
},
|
||||
postgres: {
|
||||
host: process.env.POSTGRES_HOST || 'localhost',
|
||||
port: parseInt(process.env.POSTGRES_PORT || '5432'),
|
||||
database: process.env.POSTGRES_DATABASE || 'stockbot',
|
||||
username: process.env.POSTGRES_USERNAME || 'postgres',
|
||||
password: process.env.POSTGRES_PASSWORD || 'password',
|
||||
}, dragonfly: {
|
||||
host: process.env.DRAGONFLY_HOST || 'localhost',
|
||||
port: parseInt(process.env.DRAGONFLY_PORT || '6379'),
|
||||
password: process.env.DRAGONFLY_PASSWORD,
|
||||
},
|
||||
};
|
||||
|
||||
export const brokerConfigs: Record<string, BrokerConfig> = {
|
||||
alpaca: {
|
||||
name: 'Alpaca',
|
||||
apiKey: env.ALPACA_API_KEY,
|
||||
secretKey: env.ALPACA_SECRET_KEY,
|
||||
baseUrl: env.PAPER_TRADING
|
||||
? 'https://paper-api.alpaca.markets'
|
||||
: 'https://api.alpaca.markets',
|
||||
sandbox: env.PAPER_TRADING,
|
||||
},
|
||||
};
|
||||
|
||||
export const dataProviderConfigs: Record<string, DataProviderConfig> = {
|
||||
alphaVantage: {
|
||||
name: 'Alpha Vantage',
|
||||
apiKey: env.ALPHA_VANTAGE_API_KEY,
|
||||
baseUrl: 'https://www.alphavantage.co',
|
||||
rateLimits: {
|
||||
requestsPerSecond: 5,
|
||||
requestsPerDay: 500,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const serviceDefaults = {
|
||||
healthCheckInterval: 30000, // 30 seconds
|
||||
retryAttempts: 3,
|
||||
requestTimeout: 10000, // 10 seconds
|
||||
circuitBreakerThreshold: 5,
|
||||
circuitBreakerTimeout: 60000, // 1 minute
|
||||
};
|
||||
|
||||
export const tradingHours = {
|
||||
market: {
|
||||
open: '09:30',
|
||||
close: '16:00',
|
||||
timezone: 'America/New_York',
|
||||
},
|
||||
premarket: {
|
||||
open: '04:00',
|
||||
close: '09:30',
|
||||
timezone: 'America/New_York',
|
||||
},
|
||||
afterHours: {
|
||||
open: '16:00',
|
||||
close: '20:00',
|
||||
timezone: 'America/New_York',
|
||||
},
|
||||
};
|
||||
|
||||
export const riskDefaults = {
|
||||
maxPositionSize: 0.1, // 10% of portfolio
|
||||
maxDailyLoss: 0.02, // 2% of portfolio
|
||||
maxDrawdown: 0.1, // 10% of portfolio
|
||||
stopLossPercent: 0.05, // 5%
|
||||
takeProfitPercent: 0.15, // 15%
|
||||
};
|
||||
|
||||
// Environment-specific configurations
|
||||
export const getConfig = () => {
|
||||
const base = {
|
||||
env: env.NODE_ENV,
|
||||
port: env.PORT,
|
||||
database: databaseConfig,
|
||||
brokers: brokerConfigs,
|
||||
dataProviders: dataProviderConfigs,
|
||||
services: serviceDefaults,
|
||||
trading: {
|
||||
hours: tradingHours,
|
||||
risk: riskDefaults,
|
||||
paperTrading: env.PAPER_TRADING,
|
||||
},
|
||||
};
|
||||
|
||||
switch (env.NODE_ENV) {
|
||||
case 'development':
|
||||
return {
|
||||
...base,
|
||||
logging: {
|
||||
level: 'debug',
|
||||
console: true,
|
||||
file: false,
|
||||
},
|
||||
cache: {
|
||||
ttl: 300, // 5 minutes
|
||||
},
|
||||
};
|
||||
|
||||
case 'production':
|
||||
return {
|
||||
...base,
|
||||
logging: {
|
||||
level: 'info',
|
||||
console: false,
|
||||
file: true,
|
||||
},
|
||||
cache: {
|
||||
ttl: 60, // 1 minute
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
return base;
|
||||
}
|
||||
};
|
||||
18
packages/config/tsconfig.json
Normal file
18
packages/config/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
23
packages/shared-types/package.json
Normal file
23
packages/shared-types/package.json
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "@stock-bot/shared-types",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared TypeScript definitions for the trading bot",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch",
|
||||
"clean": "rm -rf dist",
|
||||
"test": "echo \"No tests yet\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
204
packages/shared-types/src/index.ts
Normal file
204
packages/shared-types/src/index.ts
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
// Market Data Types
|
||||
export interface OHLCV {
|
||||
symbol: string;
|
||||
timestamp: Date;
|
||||
open: number;
|
||||
high: number;
|
||||
low: number;
|
||||
close: number;
|
||||
volume: number;
|
||||
}
|
||||
|
||||
export interface MarketData {
|
||||
symbol: string;
|
||||
price: number;
|
||||
bid: number;
|
||||
ask: number;
|
||||
volume: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface OrderBook {
|
||||
symbol: string;
|
||||
bids: [number, number][]; // [price, size]
|
||||
asks: [number, number][]; // [price, size]
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
// Trading Types
|
||||
export type OrderSide = 'BUY' | 'SELL';
|
||||
export type OrderType = 'MARKET' | 'LIMIT' | 'STOP' | 'STOP_LIMIT';
|
||||
export type OrderStatus = 'PENDING' | 'FILLED' | 'PARTIALLY_FILLED' | 'CANCELLED' | 'REJECTED';
|
||||
|
||||
export interface Order {
|
||||
id: string;
|
||||
symbol: string;
|
||||
side: OrderSide;
|
||||
type: OrderType;
|
||||
quantity: number;
|
||||
price?: number;
|
||||
stopPrice?: number;
|
||||
status: OrderStatus;
|
||||
timestamp: Date;
|
||||
strategyId: string;
|
||||
}
|
||||
|
||||
export interface Position {
|
||||
symbol: string;
|
||||
quantity: number;
|
||||
averagePrice: number;
|
||||
marketValue: number;
|
||||
unrealizedPnL: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface Portfolio {
|
||||
cash: number;
|
||||
totalValue: number;
|
||||
positions: Position[];
|
||||
dayPnL: number;
|
||||
totalPnL: number;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
// Strategy Types
|
||||
export type SignalType = 'BUY' | 'SELL' | 'HOLD';
|
||||
|
||||
export interface TradingSignal {
|
||||
symbol: string;
|
||||
type: SignalType;
|
||||
strength: number; // 0-1
|
||||
price: number;
|
||||
timestamp: Date;
|
||||
strategyId: string;
|
||||
metadata?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface Strategy {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
isActive: boolean;
|
||||
riskLimits: RiskLimits;
|
||||
parameters: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface RiskLimits {
|
||||
maxPositionSize: number;
|
||||
maxDailyLoss: number;
|
||||
maxDrawdown: number;
|
||||
allowedSymbols?: string[];
|
||||
}
|
||||
|
||||
// Event Types
|
||||
export interface MarketDataEvent {
|
||||
type: 'MARKET_DATA';
|
||||
data: MarketData;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface OrderEvent {
|
||||
type: 'ORDER_CREATED' | 'ORDER_FILLED' | 'ORDER_CANCELLED';
|
||||
order: Order;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface SignalEvent {
|
||||
type: 'SIGNAL_GENERATED';
|
||||
signal: TradingSignal;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export type TradingEvent = MarketDataEvent | OrderEvent | SignalEvent;
|
||||
|
||||
// Service Types
|
||||
export interface ServiceConfig {
|
||||
name: string;
|
||||
version: string;
|
||||
environment: 'development' | 'staging' | 'production';
|
||||
port?: number;
|
||||
dependencies?: string[];
|
||||
}
|
||||
|
||||
export interface HealthStatus {
|
||||
service: string;
|
||||
status: 'healthy' | 'unhealthy' | 'degraded';
|
||||
timestamp: Date;
|
||||
details?: Record<string, any>;
|
||||
}
|
||||
|
||||
// API Response Types
|
||||
export interface ApiResponse<T> {
|
||||
success: boolean;
|
||||
data?: T;
|
||||
error?: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
hasNext: boolean;
|
||||
}
|
||||
|
||||
// Fundamental Data Types
|
||||
export interface CompanyFundamentals {
|
||||
symbol: string;
|
||||
marketCap: number;
|
||||
peRatio?: number;
|
||||
pbRatio?: number;
|
||||
roe?: number;
|
||||
revenue: number;
|
||||
netIncome: number;
|
||||
lastUpdated: Date;
|
||||
}
|
||||
|
||||
export interface NewsItem {
|
||||
id: string;
|
||||
headline: string;
|
||||
summary: string;
|
||||
sentiment: number; // -1 to 1
|
||||
symbols: string[];
|
||||
source: string;
|
||||
publishedAt: Date;
|
||||
}
|
||||
|
||||
// Configuration Types
|
||||
export interface DatabaseConfig {
|
||||
questdb: {
|
||||
host: string;
|
||||
port: number;
|
||||
database: string;
|
||||
};
|
||||
postgres: {
|
||||
host: string;
|
||||
port: number;
|
||||
database: string;
|
||||
username: string;
|
||||
password: string;
|
||||
}; dragonfly: {
|
||||
host: string;
|
||||
port: number;
|
||||
password?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface BrokerConfig {
|
||||
name: string;
|
||||
apiKey: string;
|
||||
secretKey: string;
|
||||
baseUrl: string;
|
||||
sandbox: boolean;
|
||||
}
|
||||
|
||||
export interface DataProviderConfig {
|
||||
name: string;
|
||||
apiKey: string;
|
||||
baseUrl: string;
|
||||
rateLimits: {
|
||||
requestsPerSecond: number;
|
||||
requestsPerDay: number;
|
||||
};
|
||||
}
|
||||
18
packages/shared-types/tsconfig.json
Normal file
18
packages/shared-types/tsconfig.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"declaration": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
143
scripts/docker.ps1
Normal file
143
scripts/docker.ps1
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/env pwsh
|
||||
|
||||
# Trading Bot Docker Management Script
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[ValidateSet("start", "stop", "restart", "status", "logs", "reset", "admin", "monitoring", "help")]
|
||||
[string]$Action,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$Service = "",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]$Dev = $false
|
||||
)
|
||||
|
||||
$ComposeFiles = if ($Dev) {
|
||||
"-f docker-compose.yml -f docker-compose.dev.yml"
|
||||
} else {
|
||||
"-f docker-compose.yml"
|
||||
}
|
||||
|
||||
switch ($Action) {
|
||||
"start" {
|
||||
Write-Host "🚀 Starting Trading Bot infrastructure..." -ForegroundColor Green
|
||||
if ($Service) {
|
||||
Invoke-Expression "docker-compose $ComposeFiles up -d $Service"
|
||||
} else {
|
||||
Invoke-Expression "docker-compose $ComposeFiles up -d dragonfly postgres questdb"
|
||||
}
|
||||
Write-Host "✅ Infrastructure started!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "🔗 Access Points:" -ForegroundColor Cyan
|
||||
Write-Host " Dragonfly: localhost:6379"
|
||||
Write-Host " PostgreSQL: localhost:5432"
|
||||
Write-Host " QuestDB Console: http://localhost:9000"
|
||||
Write-Host ""
|
||||
Write-Host "💡 Use './scripts/docker.ps1 admin' to start admin interfaces"
|
||||
}
|
||||
|
||||
"stop" {
|
||||
Write-Host "🛑 Stopping Trading Bot infrastructure..." -ForegroundColor Yellow
|
||||
if ($Service) {
|
||||
Invoke-Expression "docker-compose $ComposeFiles stop $Service"
|
||||
} else {
|
||||
Invoke-Expression "docker-compose $ComposeFiles down"
|
||||
}
|
||||
Write-Host "✅ Infrastructure stopped!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
"restart" {
|
||||
Write-Host "🔄 Restarting Trading Bot infrastructure..." -ForegroundColor Yellow
|
||||
if ($Service) {
|
||||
Invoke-Expression "docker-compose $ComposeFiles restart $Service"
|
||||
} else {
|
||||
Invoke-Expression "docker-compose $ComposeFiles restart"
|
||||
}
|
||||
Write-Host "✅ Infrastructure restarted!" -ForegroundColor Green
|
||||
}
|
||||
|
||||
"status" {
|
||||
Write-Host "📊 Trading Bot Infrastructure Status:" -ForegroundColor Cyan
|
||||
Invoke-Expression "docker-compose $ComposeFiles ps"
|
||||
Write-Host ""
|
||||
Write-Host "🔍 Health Checks:" -ForegroundColor Cyan
|
||||
docker ps --filter "name=trading-bot" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
}
|
||||
|
||||
"logs" {
|
||||
if ($Service) {
|
||||
Write-Host "📋 Logs for $Service:" -ForegroundColor Cyan
|
||||
Invoke-Expression "docker-compose $ComposeFiles logs -f $Service"
|
||||
} else {
|
||||
Write-Host "📋 All service logs:" -ForegroundColor Cyan
|
||||
Invoke-Expression "docker-compose $ComposeFiles logs -f"
|
||||
}
|
||||
}
|
||||
|
||||
"reset" {
|
||||
Write-Host "⚠️ Resetting Trading Bot infrastructure (will delete all data)..." -ForegroundColor Red
|
||||
$confirm = Read-Host "Are you sure? Type 'yes' to confirm"
|
||||
if ($confirm -eq "yes") {
|
||||
Invoke-Expression "docker-compose $ComposeFiles down -v"
|
||||
Write-Host "🗑️ Volumes removed"
|
||||
Invoke-Expression "docker-compose $ComposeFiles up -d dragonfly postgres questdb"
|
||||
Write-Host "✅ Infrastructure reset complete!" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "❌ Reset cancelled" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
"admin" {
|
||||
Write-Host "🔧 Starting admin interfaces..." -ForegroundColor Green
|
||||
Invoke-Expression "docker-compose $ComposeFiles up -d redis-insight pgadmin"
|
||||
Write-Host "✅ Admin interfaces started!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "🔗 Admin Access:" -ForegroundColor Cyan
|
||||
Write-Host " Redis Insight: http://localhost:8001"
|
||||
Write-Host " PgAdmin: http://localhost:8080"
|
||||
Write-Host " Email: admin@tradingbot.local"
|
||||
Write-Host " Password: admin123"
|
||||
}
|
||||
|
||||
"monitoring" {
|
||||
Write-Host "📊 Starting monitoring stack..." -ForegroundColor Green
|
||||
Invoke-Expression "docker-compose $ComposeFiles --profile monitoring up -d"
|
||||
Write-Host "✅ Monitoring started!" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "🔗 Monitoring Access:" -ForegroundColor Cyan
|
||||
Write-Host " Prometheus: http://localhost:9090"
|
||||
Write-Host " Grafana: http://localhost:3000"
|
||||
Write-Host " Username: admin"
|
||||
Write-Host " Password: admin123"
|
||||
}
|
||||
|
||||
"help" {
|
||||
Write-Host ""
|
||||
Write-Host "🤖 Trading Bot Docker Management" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Usage: ./scripts/docker.ps1 <action> [options]" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
Write-Host "Actions:" -ForegroundColor Yellow
|
||||
Write-Host " start Start infrastructure services"
|
||||
Write-Host " stop Stop infrastructure services"
|
||||
Write-Host " restart Restart infrastructure services"
|
||||
Write-Host " status Show service status"
|
||||
Write-Host " logs Show service logs"
|
||||
Write-Host " reset Reset all data (destructive)"
|
||||
Write-Host " admin Start admin interfaces"
|
||||
Write-Host " monitoring Start monitoring stack"
|
||||
Write-Host " help Show this help"
|
||||
Write-Host ""
|
||||
Write-Host "Options:" -ForegroundColor Yellow
|
||||
Write-Host " -Service Specify a specific service"
|
||||
Write-Host " -Dev Use development configuration"
|
||||
Write-Host ""
|
||||
Write-Host "Examples:" -ForegroundColor Cyan
|
||||
Write-Host " ./scripts/docker.ps1 start"
|
||||
Write-Host " ./scripts/docker.ps1 start -Dev"
|
||||
Write-Host " ./scripts/docker.ps1 logs -Service dragonfly"
|
||||
Write-Host " ./scripts/docker.ps1 admin"
|
||||
}
|
||||
}
|
||||
33
turbo.json
Normal file
33
turbo.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"ui": "tui",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"test": {
|
||||
"dependsOn": ["build"],
|
||||
"outputs": ["coverage/**"]
|
||||
},
|
||||
"lint": {
|
||||
"dependsOn": ["^lint"]
|
||||
},
|
||||
"clean": {
|
||||
"cache": false
|
||||
},
|
||||
"start": {
|
||||
"dependsOn": ["build"],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"backtest": {
|
||||
"dependsOn": ["build"],
|
||||
"cache": false
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue