Edwin Salguero Cursor commited on
Commit
8a5d4f8
·
1 Parent(s): 288f367

feat: add Yahoo ingest on the FinRL tree and stop extra branch creation

Browse files

Keep csv/alpaca/synthetic/FinRL paths unchanged. Add yfinance>=1.0 as an optional data_source, rewrite the README, and remove Dependabot plus the weekly dependency-updates workflow so only main and dev remain.

Co-authored-by: Cursor <cursoragent@cursor.com>

.github/dependabot.yml DELETED
@@ -1,59 +0,0 @@
1
- version: 2
2
- updates:
3
- # Python dependencies
4
- - package-ecosystem: "pip"
5
- directory: "/"
6
- schedule:
7
- interval: "weekly"
8
- day: "monday"
9
- time: "09:00"
10
- open-pull-requests-limit: 10
11
- reviewers:
12
- - "dataen10"
13
- assignees:
14
- - "dataen10"
15
- commit-message:
16
- prefix: "pip"
17
- prefix-development: "pip-dev"
18
- include: "scope"
19
- labels:
20
- - "dependencies"
21
- - "python"
22
-
23
- # Docker dependencies
24
- - package-ecosystem: "docker"
25
- directory: "/"
26
- schedule:
27
- interval: "weekly"
28
- day: "monday"
29
- time: "09:00"
30
- open-pull-requests-limit: 5
31
- reviewers:
32
- - "dataen10"
33
- assignees:
34
- - "dataen10"
35
- commit-message:
36
- prefix: "docker"
37
- include: "scope"
38
- labels:
39
- - "dependencies"
40
- - "docker"
41
-
42
- # GitHub Actions
43
- - package-ecosystem: "github-actions"
44
- directory: "/"
45
- schedule:
46
- interval: "weekly"
47
- day: "monday"
48
- time: "09:00"
49
- open-pull-requests-limit: 5
50
- reviewers:
51
- - "dataen10"
52
- assignees:
53
- - "dataen10"
54
- commit-message:
55
- prefix: "github-actions"
56
- include: "scope"
57
- labels:
58
- - "dependencies"
59
- - "github-actions"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.github/workflows/ci-cd.yml CHANGED
@@ -2,7 +2,7 @@ name: Algorithmic Trading CI/CD Pipeline
2
 
3
  on:
4
  push:
5
- branches: [ main, develop ]
6
  pull_request:
7
  branches: [ main ]
8
  release:
 
2
 
3
  on:
4
  push:
5
+ branches: [ main, dev ]
6
  pull_request:
7
  branches: [ main ]
8
  release:
.github/workflows/dependency-update.yml DELETED
@@ -1,65 +0,0 @@
1
- name: Dependency Updates
2
-
3
- on:
4
- schedule:
5
- - cron: '0 2 * * 1' # Every Monday at 2 AM
6
- workflow_dispatch:
7
-
8
- jobs:
9
- update-dependencies:
10
- name: Update Dependencies
11
- runs-on: ubuntu-latest
12
-
13
- steps:
14
- - name: Checkout code
15
- uses: actions/checkout@v4
16
-
17
- - name: Set up Python
18
- uses: actions/setup-python@v5
19
- with:
20
- python-version: '3.11'
21
-
22
- - name: Install pip-tools
23
- run: |
24
- python -m pip install --upgrade pip
25
- pip install pip-tools
26
-
27
- - name: Update requirements
28
- run: |
29
- pip-compile --upgrade requirements.in
30
- pip-compile --upgrade requirements-dev.in
31
-
32
- - name: Check for security vulnerabilities
33
- run: |
34
- pip install safety
35
- safety check --json --output safety-report.json
36
-
37
- - name: Create Pull Request
38
- uses: peter-evans/create-pull-request@v7
39
- with:
40
- token: ${{ secrets.GITHUB_TOKEN }}
41
- commit-message: 'chore: update dependencies'
42
- title: '🔧 Automated dependency updates'
43
- body: |
44
- ## Automated Dependency Updates
45
-
46
- This PR updates dependencies to their latest versions.
47
-
48
- ### 📋 Changes
49
- - Updated Python packages to latest versions
50
- - Security vulnerability fixes
51
- - Performance improvements
52
-
53
- ### 🔍 Security Report
54
- - [ ] No critical vulnerabilities
55
- - [ ] No high severity issues
56
- - [ ] Dependencies up to date
57
-
58
- ### 🧪 Testing
59
- - [ ] All tests pass
60
- - [ ] No breaking changes
61
- - [ ] Performance maintained
62
-
63
- **Auto-generated by GitHub Actions**
64
- branch: dependency-updates
65
- delete-branch: true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CI_CD_SETUP.md CHANGED
@@ -11,7 +11,7 @@ The CI/CD pipeline provides automated quality assurance, testing, deployment, an
11
  ### 1. **Main CI/CD Pipeline** (`.github/workflows/ci-cd.yml`)
12
 
13
  **Triggers:**
14
- - Push to `main` or `develop` branches
15
  - Pull requests to `main`
16
  - Release creation
17
 
 
11
  ### 1. **Main CI/CD Pipeline** (`.github/workflows/ci-cd.yml`)
12
 
13
  **Triggers:**
14
+ - Push to `main` or `dev` branches
15
  - Pull requests to `main`
16
  - Release creation
17
 
README.md CHANGED
@@ -1,600 +1,130 @@
1
- # Algorithmic Trading System with FinRL and Alpaca Integration
2
 
3
- A sophisticated algorithmic trading system that combines reinforcement learning (FinRL) with real-time market data and order execution through Alpaca Markets. This system supports both paper trading and live trading with advanced risk management and technical analysis.
4
 
5
- ## 🚀 Features
6
 
7
- ### Core Trading System
8
 
9
- - **Multi-source Data Ingestion**: CSV files, Alpaca Markets API, and synthetic data generation
10
- - **Technical Analysis**: 20+ technical indicators including RSI, MACD, Bollinger Bands, and more
11
- - **Risk Management**: Position sizing, drawdown limits, and portfolio protection
12
- - **Real-time Execution**: Live order placement and portfolio monitoring
13
 
14
- ### FinRL Reinforcement Learning
 
15
 
16
- - **Multiple Algorithms**: PPO, A2C, DDPG, and TD3 support
17
- - **Custom Trading Environment**: Gymnasium-compatible environment for RL training
18
- - **Real-time Integration**: Can execute real trades during training and inference
19
- - **Model Persistence**: Save and load trained models for consistent performance
20
 
21
- ### Alpaca Broker Integration
22
 
23
- - **Paper Trading**: Risk-free testing with virtual money
24
- - **Live Trading**: Real market execution (use with caution!)
25
- - **Market Data**: Real-time and historical data from Alpaca
26
- - **Account Management**: Portfolio monitoring and position tracking
27
- - **Order Types**: Market orders, limit orders, and order cancellation
 
28
 
29
- ### 🎨 Comprehensive UI System
30
 
31
- - **Streamlit UI**: Quick prototyping and data science workflows
32
- - **Dash UI**: Enterprise-grade interactive dashboards
33
- - **Jupyter UI**: Interactive notebook-based interfaces
34
- - **WebSocket API**: Real-time trading data streaming
35
- - **Multi-interface Support**: Choose the right UI for your needs
36
 
37
- ### Advanced Features
38
 
39
- - **Docker Support**: Containerized deployment for consistency
40
- - **Comprehensive Logging**: Detailed logs for debugging and performance analysis
41
- - **Backtesting Engine**: Historical performance evaluation
42
- - **Live Trading Simulation**: Real-time trading with configurable duration
43
- - **Performance Metrics**: Returns, Sharpe ratio, drawdown analysis
 
44
 
45
- ## 📋 Prerequisites
46
 
47
- - Python 3.8+
48
- - Alpaca Markets account (free paper trading available)
49
- - Docker (optional, for containerized deployment)
50
 
51
- ## 🛠️ Installation
 
 
52
 
53
- ### 1. Clone the Repository
54
 
55
- ```bash
56
- git clone https://github.com/ParallelLLC/algorithmic_trading.git
57
- cd algorithmic_trading
58
- ```
59
 
60
- ### 2. Install Dependencies
61
 
62
- ```bash
63
- pip install -r requirements.txt
64
- ```
 
 
 
 
 
 
 
65
 
66
- ### 3. Set Up Alpaca API Credentials
67
 
68
- Create a `.env` file in the project root:
69
 
70
- ```bash
71
- cp env.example .env
 
 
 
 
 
 
 
 
 
 
 
72
  ```
73
 
74
- Edit `.env` with your Alpaca credentials:
75
 
76
- ```env
77
- # Get these from https://app.alpaca.markets/paper/dashboard/overview
78
- ALPACA_API_KEY=your_paper_api_key_here
79
- ALPACA_SECRET_KEY=your_paper_secret_key_here
80
 
81
- # For live trading (use with caution!)
82
- # ALPACA_API_KEY=your_live_api_key_here
83
- # ALPACA_SECRET_KEY=your_live_secret_key_here
84
- ```
85
 
86
- ### 4. Configure Trading Parameters
 
 
 
 
 
 
87
 
88
- Edit `config.yaml` to customize your trading strategy:
89
 
90
  ```yaml
91
- # Data source configuration
92
  data_source:
93
- type: 'alpaca' # Options: 'alpaca', 'csv', 'synthetic'
94
-
95
- # Trading parameters
96
  trading:
97
  symbol: 'AAPL'
98
- timeframe: '1m'
99
- capital: 100000
100
-
101
- # Risk management
102
- risk:
103
- max_position: 100
104
- max_drawdown: 0.05
105
-
106
- # Execution settings
107
- execution:
108
- broker_api: 'alpaca_paper' # Options: 'paper', 'alpaca_paper', 'alpaca_live'
109
- order_size: 10
110
-
111
- # FinRL configuration
112
- finrl:
113
- algorithm: 'PPO'
114
- learning_rate: 0.0003
115
- training:
116
- total_timesteps: 100000
117
- save_best_model: true
118
  ```
119
 
120
- ## 🚀 Quick Start
121
-
122
- ### 1. Launch the UI (Recommended)
123
-
124
- ```bash
125
- # Launch Streamlit UI (best for beginners)
126
- python ui_launcher.py streamlit
127
-
128
- # Launch Dash UI (best for production)
129
- python ui_launcher.py dash
130
-
131
- # Launch Jupyter Lab
132
- python ui_launcher.py jupyter
133
-
134
- # Launch all UIs
135
- python ui_launcher.py all
136
- ```
137
-
138
- ### 2. Run the Demo
139
-
140
  ```bash
141
  python demo.py
 
 
142
  ```
143
 
144
- This will:
145
- - Test data ingestion from Alpaca
146
- - Demonstrate FinRL training
147
- - Show trading workflow execution
148
- - Run backtesting on historical data
149
-
150
- ### 3. Start Paper Trading
151
-
152
- ```bash
153
- python -m agentic_ai_system.main --mode live --duration 60
154
- ```
155
-
156
- ### 4. Run Backtesting
157
-
158
- ```bash
159
- python -m agentic_ai_system.main --mode backtest --start-date 2024-01-01 --end-date 2024-01-31
160
- ```
161
-
162
- ## 📊 Usage Examples
163
-
164
- ### Basic Trading Workflow
165
-
166
- ```python
167
- from agentic_ai_system.main import load_config
168
- from agentic_ai_system.orchestrator import run
169
-
170
- # Load configuration
171
- config = load_config()
172
-
173
- # Run single trading cycle
174
- result = run(config)
175
- print(f"Trading result: {result}")
176
- ```
177
-
178
- ### FinRL Training
179
-
180
- ```python
181
- from agentic_ai_system.finrl_agent import FinRLAgent, FinRLConfig
182
- from agentic_ai_system.data_ingestion import load_data
183
-
184
- # Load data and configuration
185
- config = load_config()
186
- data = load_data(config)
187
-
188
- # Initialize FinRL agent
189
- finrl_config = FinRLConfig(algorithm='PPO', learning_rate=0.0003)
190
- agent = FinRLAgent(finrl_config)
191
-
192
- # Train the agent
193
- result = agent.train(
194
- data=data,
195
- config=config,
196
- total_timesteps=100000,
197
- use_real_broker=False # Use simulation for training
198
- )
199
-
200
- print(f"Training completed: {result}")
201
- ```
202
-
203
- ### Alpaca Integration
204
-
205
- ```python
206
- from agentic_ai_system.alpaca_broker import AlpacaBroker
207
-
208
- # Initialize Alpaca broker
209
- config = load_config()
210
- broker = AlpacaBroker(config)
211
-
212
- # Get account information
213
- account_info = broker.get_account_info()
214
- print(f"Account balance: ${account_info['buying_power']:,.2f}")
215
-
216
- # Place a market order
217
- result = broker.place_market_order(
218
- symbol='AAPL',
219
- quantity=10,
220
- side='buy'
221
- )
222
- print(f"Order result: {result}")
223
- ```
224
-
225
- ### Real-time Trading with FinRL
226
-
227
- ```python
228
- from agentic_ai_system.finrl_agent import FinRLAgent
229
-
230
- # Load trained model
231
- agent = FinRLAgent(FinRLConfig())
232
- agent.model = agent._load_model('models/finrl_best/best_model', config)
233
-
234
- # Make predictions with real execution
235
- result = agent.predict(
236
- data=recent_data,
237
- config=config,
238
- use_real_broker=True # Execute real trades!
239
- )
240
- ```
241
-
242
- ## 🏗️ Architecture
243
-
244
- ### System Components
245
-
246
- ```
247
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
248
- │ Data Sources │ │ Strategy Agent │ │ Execution Agent │
249
- │ │ │ │ │ │
250
- │ • Alpaca API │───▶│ • Technical │───▶│ • Alpaca Broker │
251
- │ • CSV Files │ │ Indicators │ │ • Order Mgmt │
252
- │ • Synthetic │ │ • Signal Gen │ │ • Risk Control │
253
- └─────────────────┘ └─────────────────┘ └─────────────────┘
254
- │ │ │
255
- ▼ ▼ ▼
256
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
257
- │ Data Ingestion │ │ FinRL Agent │ │ Portfolio │
258
- │ │ │ │ │ Management │
259
- │ • Validation │ │ • PPO/A2C/DDPG │ │ • Positions │
260
- │ • Indicators │ │ • Training │ │ • P&L Tracking │
261
- │ • Preprocessing │ │ • Prediction │ │ • Risk Metrics │
262
- └─────────────────┘ └─────────────────┘ └─────────────────┘
263
- ```
264
-
265
- ### Data Flow
266
-
267
- 1. **Data Ingestion**: Market data from Alpaca, CSV, or synthetic sources
268
- 2. **Preprocessing**: Technical indicators, data validation, and feature engineering
269
- 3. **Strategy Generation**: Traditional technical analysis or FinRL predictions
270
- 4. **Risk Management**: Position sizing and portfolio protection
271
- 5. **Order Execution**: Real-time order placement through Alpaca
272
- 6. **Performance Tracking**: Continuous monitoring and logging
273
-
274
- ## 📁 Project Directory Structure
275
-
276
- ```
277
- algorithmic_trading/
278
- ├── 📄 README.md # Project documentation
279
- ├── 📄 LICENSE # Apache License 2.0
280
- ├── 📄 requirements.txt # Python dependencies
281
- ├── 📄 config.yaml # Main configuration file
282
- ├── 📄 env.example # Environment variables template
283
- ├── 📄 .gitignore # Git ignore rules
284
- ├── 📄 pytest.ini # Test configuration
285
-
286
- ├── 🐳 Docker/
287
- │ ├── 📄 Dockerfile # Container definition
288
- │ ├── 📄 docker-entrypoint.sh # Container startup script
289
- │ ├── 📄 .dockerignore # Docker ignore rules
290
- │ ├── 📄 docker-compose.yml # Default compose file
291
- │ ├── 📄 docker-compose.dev.yml # Development environment
292
- │ ├── 📄 docker-compose.prod.yml # Production environment
293
- │ ├── 📄 docker-compose.hub.yml # Docker Hub deployment
294
- │ └── 📄 docker-compose.prod.yml # Production environment
295
-
296
- ├── 🤖 agentic_ai_system/ # Core AI trading system
297
- │ ├── 📄 main.py # Main entry point
298
- │ ├── 📄 orchestrator.py # System coordination
299
- │ ├── 📄 agent_base.py # Base agent class
300
- │ ├── 📄 data_ingestion.py # Market data processing
301
- │ ├── 📄 strategy_agent.py # Trading strategy logic
302
- │ ├── 📄 execution_agent.py # Order execution
303
- │ ├── 📄 finrl_agent.py # FinRL reinforcement learning
304
- │ ├── 📄 alpaca_broker.py # Alpaca API integration
305
- │ ├── 📄 synthetic_data_generator.py # Test data generation
306
- │ └── 📄 logger_config.py # Logging configuration
307
-
308
- ├── 🎨 ui/ # User interface system
309
- │ ├── 📄 __init__.py # UI package initialization
310
- │ ├── 📄 streamlit_app.py # Streamlit web application
311
- │ ├── 📄 dash_app.py # Dash enterprise dashboard
312
- │ ├── 📄 jupyter_widgets.py # Jupyter interactive widgets
313
- │ └── 📄 websocket_server.py # Real-time WebSocket server
314
-
315
- ├── 🧪 tests/ # Test suite
316
- │ ├── 📄 __init__.py
317
- │ ├── 📄 test_data_ingestion.py
318
- │ ├── 📄 test_strategy_agent.py
319
- │ ├── 📄 test_execution_agent.py
320
- │ ├── 📄 test_finrl_agent.py
321
- │ ├── 📄 test_synthetic_data_generator.py
322
- │ └── 📄 test_integration.py
323
-
324
- ├── 📊 data/ # Market data storage
325
- │ └── 📄 synthetic_market_data.csv
326
-
327
- ├── 🧠 models/ # Trained AI models
328
- │ └── 📁 finrl_best/ # Best FinRL models
329
-
330
- ├── 📈 plots/ # Generated charts/visualizations
331
-
332
- ├── 📝 logs/ # System logs
333
- │ ├── 📄 trading_system.log
334
- │ ├── 📄 trading.log
335
- │ ├── 📄 performance.log
336
- │ ├── 📄 errors.log
337
- │ ├── 📁 finrl_tensorboard/ # FinRL training logs
338
- │ └── 📁 finrl_eval/ # Model evaluation logs
339
-
340
- ├── 🔧 scripts/ # Utility scripts
341
- │ ├── 📄 docker-build.sh # Docker build automation
342
- │ └── 📄 docker-hub-deploy.sh # Docker Hub deployment
343
-
344
- ├── 📄 demo.py # Main demo script
345
- ├── 📄 finrl_demo.py # FinRL-specific demo
346
- ├── 📄 ui_launcher.py # UI launcher script
347
- ├── 📄 UI_SETUP.md # UI setup documentation
348
- ├── 📄 DOCKER_HUB_SETUP.md # Docker Hub documentation
349
-
350
- └── 🐍 .venv/ # Python virtual environment
351
- ```
352
-
353
- ## 🏗️ Architecture Overview
354
-
355
- ### Core Components:
356
-
357
- - **Data Layer**: Market data ingestion and preprocessing
358
- - **Strategy Layer**: Technical analysis and signal generation
359
- - **AI Layer**: FinRL reinforcement learning agents
360
- - **Execution Layer**: Order management and broker integration
361
- - **Orchestration**: System coordination and workflow management
362
-
363
- ### Key Features:
364
-
365
- - **Modular Design**: Each component is independent and testable
366
- - **Docker Support**: Complete containerization for deployment
367
- - **Testing**: Comprehensive test suite for all components
368
- - **Logging**: Detailed logging for monitoring and debugging
369
- - **Configuration**: Centralized configuration management
370
- - **Documentation**: Extensive documentation and examples
371
-
372
- ### Development Workflow:
373
-
374
- 1. **Data Ingestion** → Market data from Alpaca/CSV/synthetic sources
375
- 2. **Strategy Generation** → Technical indicators and FinRL predictions
376
- 3. **Risk Management** → Position sizing and portfolio protection
377
- 4. **Order Execution** → Real-time trading through Alpaca
378
- 5. **Performance Tracking** → Continuous monitoring and logging
379
-
380
- ## 🔧 Configuration
381
-
382
- ### Alpaca Settings
383
-
384
- ```yaml
385
- alpaca:
386
- api_key: '' # Set via environment variable
387
- secret_key: '' # Set via environment variable
388
- paper_trading: true
389
- base_url: 'https://paper-api.alpaca.markets'
390
- live_url: 'https://api.alpaca.markets'
391
- data_url: 'https://data.alpaca.markets'
392
- account_type: 'paper' # 'paper' or 'live'
393
- ```
394
-
395
- ### FinRL Settings
396
-
397
- ```yaml
398
- finrl:
399
- algorithm: 'PPO' # PPO, A2C, DDPG, TD3
400
- learning_rate: 0.0003
401
- batch_size: 64
402
- buffer_size: 1000000
403
- training:
404
- total_timesteps: 100000
405
- eval_freq: 10000
406
- save_best_model: true
407
- model_save_path: 'models/finrl_best/'
408
- inference:
409
- use_trained_model: false
410
- model_path: 'models/finrl_best/best_model'
411
- ```
412
-
413
- ### Risk Management
414
-
415
- ```yaml
416
- risk:
417
- max_position: 100
418
- max_drawdown: 0.05
419
- stop_loss: 0.02
420
- take_profit: 0.05
421
- ```
422
-
423
- ## 🎨 User Interface System
424
-
425
- The project includes a comprehensive UI system with multiple interface options:
426
-
427
- ### Available UIs
428
-
429
- #### Streamlit UI (Recommended for beginners)
430
- - **URL**: http://localhost:8501
431
- - **Features**: Interactive widgets, real-time data visualization, easy configuration
432
- - **Best for**: Data scientists, quick experiments, rapid prototyping
433
-
434
- #### Dash UI (Recommended for production)
435
- - **URL**: http://localhost:8050
436
- - **Features**: Enterprise-grade dashboards, advanced charts, professional styling
437
- - **Best for**: Production dashboards, real-time monitoring, complex analytics
438
-
439
- #### Jupyter UI (For research)
440
- - **URL**: http://localhost:8888
441
- - **Features**: Interactive notebooks, code execution, rich documentation
442
- - **Best for**: Research, experimentation, educational purposes
443
-
444
- #### WebSocket API (For developers)
445
- - **URL**: ws://localhost:8765
446
- - **Features**: Real-time data streaming, trading signals, portfolio updates
447
- - **Best for**: Real-time trading signals, live data streaming
448
-
449
- ### Quick UI Launch
450
-
451
- ```bash
452
- # Launch individual UIs
453
- python ui_launcher.py streamlit # Streamlit UI
454
- python ui_launcher.py dash # Dash UI
455
- python ui_launcher.py jupyter # Jupyter Lab
456
- python ui_launcher.py websocket # WebSocket server
457
-
458
- # Launch all UIs at once
459
- python ui_launcher.py all
460
- ```
461
-
462
- ### UI Features
463
-
464
- - **Real-time Data Visualization**: Live market data charts and indicators
465
- - **Portfolio Monitoring**: Real-time portfolio value and P&L tracking
466
- - **Trading Controls**: Start/stop trading, backtesting, risk management
467
- - **FinRL Training**: Interactive model training and evaluation
468
- - **Alpaca Integration**: Account management and order execution
469
- - **Configuration Management**: Easy parameter tuning and strategy setup
470
-
471
- For detailed UI documentation, see [UI_SETUP.md](UI_SETUP.md).
472
-
473
- ## 📈 Performance Monitoring
474
-
475
- ### Logging
476
-
477
- The system provides comprehensive logging:
478
-
479
- - `logs/trading_system.log`: Main system logs
480
- - `logs/trading.log`: Trading-specific events
481
- - `logs/performance.log`: Performance metrics
482
- - `logs/finrl_tensorboard/`: FinRL training logs
483
-
484
- ### Metrics Tracked
485
-
486
- - Portfolio value and returns
487
- - Trade execution statistics
488
- - Risk metrics (Sharpe ratio, drawdown)
489
- - FinRL training progress
490
- - Alpaca account status
491
-
492
- ### Real-time Monitoring
493
-
494
- ```python
495
- # Get account information
496
- account_info = broker.get_account_info()
497
- print(f"Portfolio Value: ${account_info['portfolio_value']:,.2f}")
498
-
499
- # Get current positions
500
- positions = broker.get_positions()
501
- for pos in positions:
502
- print(f"{pos['symbol']}: {pos['quantity']} shares")
503
-
504
- # Check market status
505
- market_open = broker.is_market_open()
506
- print(f"Market: {'OPEN' if market_open else 'CLOSED'}")
507
- ```
508
-
509
- ## 🐳 Docker Deployment
510
-
511
- ### Build and Run
512
-
513
- ```bash
514
- # Build the image
515
- docker build -t algorithmic-trading .
516
-
517
- # Run with environment variables
518
- docker run -it --env-file .env algorithmic-trading
519
-
520
- # Run with Jupyter Lab for development
521
- docker-compose -f docker-compose.dev.yml up
522
- ```
523
-
524
- ### Production Deployment
525
-
526
- ```bash
527
- # Use production compose file
528
- docker-compose -f docker-compose.prod.yml up -d
529
-
530
- # Monitor logs
531
- docker-compose -f docker-compose.prod.yml logs -f
532
- ```
533
-
534
- ## 🧪 Testing
535
-
536
- ### Run All Tests
537
-
538
- ```bash
539
- pytest tests/ -v
540
- ```
541
-
542
- ### Test Specific Components
543
-
544
- ```bash
545
- # Test Alpaca integration
546
- pytest tests/test_alpaca_integration.py -v
547
-
548
- # Test FinRL agent
549
- pytest tests/test_finrl_agent.py -v
550
-
551
- # Test trading workflow
552
- pytest tests/test_integration.py -v
553
- ```
554
-
555
- ## ⚠️ Important Notes
556
-
557
- ### Paper Trading vs Live Trading
558
-
559
- - **Paper Trading**: Uses virtual money, safe for testing
560
- - **Live Trading**: Uses real money, use with extreme caution
561
- - Always test strategies thoroughly in paper trading before going live
562
-
563
- ### Risk Management
564
-
565
- - Set appropriate position limits and drawdown thresholds
566
- - Monitor your portfolio regularly
567
- - Use stop-loss orders to limit potential losses
568
- - Never risk more than you can afford to lose
569
-
570
- ### API Rate Limits
571
-
572
- - Alpaca has rate limits on API calls
573
- - The system includes built-in delays to respect these limits
574
- - Monitor your API usage in the Alpaca dashboard
575
-
576
- ## 🤝 Contributing
577
-
578
- 1. Fork the repository
579
- 2. Create a feature branch
580
- 3. Make your changes
581
- 4. Add tests for new functionality
582
- 5. Submit a pull request
583
-
584
- ## 📄 License
585
 
586
- This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
587
 
588
- ## 🆘 Support
589
 
590
- - **Documentation**: Check the logs and configuration files
591
- - **Issues**: Report bugs and feature requests on GitHub
592
- - **Alpaca Support**: Contact Alpaca for API-related issues
593
- - **Community**: Join our Discord/Telegram for discussions
 
 
 
594
 
595
- ## 🔗 Useful Links
596
 
597
- - [Alpaca Markets Documentation](https://alpaca.markets/docs/)
598
- - [FinRL Documentation](https://finrl.readthedocs.io/)
599
- - [Stable Baselines3 Documentation](https://stable-baselines3.readthedocs.io/)
600
- - [Gymnasium Documentation](https://gymnasium.farama.org/)
 
1
+ # Algorithmic Trading
2
 
3
+ FinRL reinforcement-learning trading with Alpaca execution, plus optional Yahoo Finance OHLCV for unlabeled real-price research. Parallel LLC.
4
 
5
+ This is **research and paper-trading infrastructure**. Live capital requires a separate evaluation contract, feature-parity tests, and a rewritten execution path. Do not treat `paper_trading: false` as a promotion gate.
6
 
7
+ ---
8
 
9
+ ## 1. Title and Summary
 
 
 
10
 
11
+ **Algorithmic Trading**
12
+ Northwestern-trained data-engineering practice applied to a trading loop: ingest OHLCV, compute indicators or train a FinRL policy, size orders under position and drawdown caps, route to paper or live Alpaca.
13
 
14
+ GitHub `main` is the FinRL / Docker / Streamlit tree. `dev` is the integration branch. Yahoo is an additive `data_source.type`, not a replacement for Alpaca or FinRL.
 
 
 
15
 
16
+ **Design themes**
17
 
18
+ * Four ingest paths: CSV replay, synthetic GBM, Alpaca REST, Yahoo (`yfinance>=1.0`)
19
+ * FinRL policies (PPO, A2C, DDPG, TD3) on a Gymnasium-style environment
20
+ * Alpaca for authenticated market data and order routing (paper by default)
21
+ * Yahoo for delayed public bars when no broker key is available
22
+ * Secrets from environment (`ALPACA_API_KEY`, `ALPACA_SECRET_KEY`), never committed
23
+ * Tests and Docker/CI as already present on this tree
24
 
25
+ ---
26
 
27
+ ## 2. Concepts and Methods
 
 
 
 
28
 
29
+ ### Market data
30
 
31
+ | Source | When to use | Failure modes |
32
+ | ------ | ----------- | ------------- |
33
+ | **CSV** | Offline replay; default in `config.yaml` | Missing path or OHLCV columns → `None` |
34
+ | **Synthetic** | Unit tests and demos | GBM is not tradable edge |
35
+ | **Alpaca** | Authenticated bars and live/paper orders | Auth, feed, and rate-limit failures |
36
+ | **Yahoo** | Real Close without a broker account | Unofficial API, ~15 min delay, interval lookback caps (1m ≈ 7 days). Pin `yfinance>=1.0`; 0.2.x fails against the current chart API |
37
 
38
+ `load_data` dispatches on `data_source.type`. Existing `alpaca` / `csv` / `synthetic` branches are unchanged.
39
 
40
+ ### Strategy and FinRL
 
 
41
 
42
+ * `StrategyAgent`: SMA, RSI, Bollinger, MACD on Close; teaching rule, not an alpha claim
43
+ * `FinRLAgent`: PPO / A2C / DDPG / TD3 via Stable-Baselines3; persist under `models/`
44
+ * `ExecutionAgent` / `AlpacaBroker`: paper simulation or Alpaca market/limit orders
45
 
46
+ Backtests in this repo are in-sample passes unless you add a purged walk-forward yourself. Leakage is the null hypothesis.
47
 
48
+ ---
 
 
 
49
 
50
+ ## 3. Stack
51
 
52
+ | Layer | Tools |
53
+ | ----- | ----- |
54
+ | Language | Python 3.11 (CI); 3.8+ stated for local |
55
+ | RL | FinRL / Stable-Baselines3, Gym/Gymnasium, PyTorch |
56
+ | Broker | alpaca-py |
57
+ | Market data | Alpaca REST; yfinance ≥ 1.0 (Yahoo) |
58
+ | Tabular | pandas, NumPy, scikit-learn |
59
+ | UI | Streamlit, Dash, Jupyter widgets |
60
+ | Deploy | Docker Compose, GitHub Actions |
61
+ | Tests | pytest, pytest-cov |
62
 
63
+ ---
64
 
65
+ ## 4. Structure
66
 
67
+ ```
68
+ algorithmic_trading/
69
+ ├── agentic_ai_system/ # ingest, strategy, FinRL, Alpaca, Yahoo
70
+ ├── ui/ # Streamlit, Dash, Jupyter, WebSocket
71
+ ├── tests/
72
+ ├── models/ # trained artifacts (gitignored bodies)
73
+ ├── data/ # generated CSV (gitignored)
74
+ ├── scripts/ # Docker / deploy helpers
75
+ ├── .github/workflows/ # CI/CD, release, backtesting
76
+ ├── config.yaml
77
+ ├── requirements.txt
78
+ ├── Dockerfile
79
+ └── docker-compose*.yml
80
  ```
81
 
82
+ Branch policy: **`main`** (protected) and **`dev`** only. Do not re-enable Dependabot or the Monday `dependency-updates` workflow; those created extra branches.
83
 
84
+ ---
 
 
 
85
 
86
+ ## 5. Quick start
 
 
 
87
 
88
+ ```bash
89
+ git clone https://github.com/ParallelLLC/algorithmic_trading.git
90
+ cd algorithmic_trading
91
+ python -m venv .venv && source .venv/bin/activate
92
+ pip install -r requirements.txt
93
+ cp .env.example .env # Alpaca keys if using alpaca ingest or orders
94
+ ```
95
 
96
+ Default ingest is CSV. For Yahoo daily bars without a broker:
97
 
98
  ```yaml
 
99
  data_source:
100
+ type: 'yahoo'
 
 
101
  trading:
102
  symbol: 'AAPL'
103
+ timeframe: '1d'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  ```
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  ```bash
107
  python demo.py
108
+ python -m agentic_ai_system.main --mode backtest --start-date 2024-01-01 --end-date 2024-12-31
109
+ pytest tests/ -q
110
  ```
111
 
112
+ UI launchers and Docker are documented in `UI_SETUP.md` and `DOCKER_HUB_SETUP.md`. Paper-trade before live. Yahoo is not a SIP tape.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ ---
115
 
116
+ ## 6. Configuration (additive Yahoo keys)
117
 
118
+ | Key | Meaning |
119
+ | --- | ------- |
120
+ | `data_source.type` | `csv` \| `synthetic` \| `alpaca` \| `yahoo` |
121
+ | `yahoo.start_date` / `end_date` | Historical window; clamped per Yahoo interval limits |
122
+ | `yahoo.auto_adjust` | Passed to `yfinance` |
123
+ | `execution.broker_api` | `paper` \| `alpaca_paper` \| `alpaca_live` |
124
+ | `finrl.algorithm` | PPO, A2C, DDPG, TD3 |
125
 
126
+ ---
127
 
128
+ **License:** Apache License 2.0
129
+ **Organization:** [Parallel LLC](https://github.com/ParallelLLC)
130
+ **Repository:** <https://github.com/ParallelLLC/algorithmic_trading>
 
agentic_ai_system/data_ingestion.py CHANGED
@@ -27,6 +27,8 @@ def load_data(config: Dict[str, Any]) -> Optional[pd.DataFrame]:
27
  return _load_csv_data(config)
28
  elif data_source == 'synthetic':
29
  return _load_synthetic_data(config)
 
 
30
  else:
31
  logger.error(f"Unsupported data source: {data_source}")
32
  return None
@@ -75,6 +77,30 @@ def _load_alpaca_data(config: Dict[str, Any]) -> Optional[pd.DataFrame]:
75
  logger.error(f"Error loading Alpaca data: {e}")
76
  return None
77
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  def _load_csv_data(config: Dict[str, Any]) -> Optional[pd.DataFrame]:
79
  """Load market data from CSV file"""
80
  try:
 
27
  return _load_csv_data(config)
28
  elif data_source == 'synthetic':
29
  return _load_synthetic_data(config)
30
+ elif data_source == 'yahoo':
31
+ return _load_yahoo_data(config)
32
  else:
33
  logger.error(f"Unsupported data source: {data_source}")
34
  return None
 
77
  logger.error(f"Error loading Alpaca data: {e}")
78
  return None
79
 
80
+ def _load_yahoo_data(config: Dict[str, Any]) -> Optional[pd.DataFrame]:
81
+ """Load OHLCV bars from Yahoo Finance via yfinance. Does not replace Alpaca."""
82
+ try:
83
+ from .yahoo_data_stream import YahooDataStream
84
+
85
+ stream = YahooDataStream(config)
86
+ trading = config.get('trading', {})
87
+ symbol = trading.get('symbol') or (trading.get('symbols') or ['AAPL'])[0]
88
+ yahoo_cfg = config.get('yahoo', {})
89
+ start_date = yahoo_cfg.get('start_date', '2024-01-01')
90
+ end_date = yahoo_cfg.get('end_date', '2026-12-31')
91
+
92
+ data = stream.get_historical_data(symbol, start_date, end_date)
93
+ if data is None or data.empty:
94
+ logger.error("No Yahoo data returned for %s", symbol)
95
+ return None
96
+
97
+ logger.info("Loaded %s Yahoo bars for %s", len(data), symbol)
98
+ return data
99
+ except Exception as e:
100
+ logger.error("Error loading Yahoo data: %s", e)
101
+ return None
102
+
103
+
104
  def _load_csv_data(config: Dict[str, Any]) -> Optional[pd.DataFrame]:
105
  """Load market data from CSV file"""
106
  try:
agentic_ai_system/yahoo_data_stream.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import threading
3
+ import time
4
+ from typing import Any, Callable, Dict, List, Optional
5
+
6
+ import pandas as pd
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+ _INTERVAL_MAP = {
11
+ '1min': '1m',
12
+ '1m': '1m',
13
+ '5min': '5m',
14
+ '5m': '5m',
15
+ '15min': '15m',
16
+ '15m': '15m',
17
+ '30min': '30m',
18
+ '30m': '30m',
19
+ '1H': '1h',
20
+ '1h': '1h',
21
+ '60min': '1h',
22
+ '1D': '1d',
23
+ '1d': '1d',
24
+ '1day': '1d',
25
+ }
26
+
27
+ # Yahoo lookback limits by interval. Requesting more returns empty or errors.
28
+ _MAX_LOOKBACK = {
29
+ '1m': pd.Timedelta(days=7),
30
+ '2m': pd.Timedelta(days=60),
31
+ '5m': pd.Timedelta(days=60),
32
+ '15m': pd.Timedelta(days=60),
33
+ '30m': pd.Timedelta(days=60),
34
+ '60m': pd.Timedelta(days=730),
35
+ '90m': pd.Timedelta(days=60),
36
+ '1h': pd.Timedelta(days=730),
37
+ '1d': None,
38
+ '5d': None,
39
+ '1wk': None,
40
+ '1mo': None,
41
+ '3mo': None,
42
+ }
43
+
44
+
45
+ class YahooDataStream:
46
+ """
47
+ Market data from Yahoo Finance via yfinance.
48
+
49
+ Yahoo has no public equities WebSocket. This polls OHLCV bars.
50
+ Quotes are typically delayed (~15 minutes for US equities).
51
+ Unofficial API: rate limits and schema changes are expected failure modes.
52
+ """
53
+
54
+ def __init__(self, config: Dict[str, Any]):
55
+ self.config = config
56
+ trading = config.get('trading', {})
57
+ if trading.get('symbols'):
58
+ self.symbols = list(trading['symbols'])
59
+ elif trading.get('symbol'):
60
+ self.symbols = [trading['symbol']]
61
+ else:
62
+ self.symbols = ['AAPL']
63
+ yahoo_cfg = config.get('yahoo', {})
64
+ self.poll_interval = int(yahoo_cfg.get('poll_interval_seconds', 60))
65
+ self.auto_adjust = bool(yahoo_cfg.get('auto_adjust', False))
66
+ self.interval = self._map_interval(config.get('trading', {}).get('timeframe', '1d'))
67
+ self.data_callbacks: List[Callable] = []
68
+ self.is_connected = False
69
+ self.data_buffer: Dict[str, Dict[str, Any]] = {}
70
+ self._stop_event = threading.Event()
71
+ self._poll_thread: Optional[threading.Thread] = None
72
+ self._last_bar_ts: Dict[str, pd.Timestamp] = {}
73
+ self._buffer_size = int(config.get('realtime_data', {}).get('buffer_size', 100))
74
+
75
+ for symbol in self.symbols:
76
+ self.data_buffer[symbol] = {
77
+ 'trades': [],
78
+ 'quotes': [],
79
+ 'bars': [],
80
+ 'latest_bar': None,
81
+ }
82
+
83
+ logger.info(
84
+ "Initialized YahooDataStream symbols=%s interval=%s poll_interval=%ss",
85
+ self.symbols,
86
+ self.interval,
87
+ self.poll_interval,
88
+ )
89
+
90
+ @staticmethod
91
+ def _map_interval(timeframe: str) -> str:
92
+ mapped = _INTERVAL_MAP.get(str(timeframe), None)
93
+ if mapped is None:
94
+ logger.warning("Unknown timeframe %s, defaulting to 1d", timeframe)
95
+ return '1d'
96
+ return mapped
97
+
98
+ def connect(self) -> None:
99
+ """Start polling Yahoo for new bars."""
100
+ if self.is_connected:
101
+ logger.info("Yahoo data stream already connected")
102
+ return
103
+
104
+ self._stop_event.clear()
105
+ self._poll_once()
106
+ self._poll_thread = threading.Thread(target=self._poll_loop, name='yahoo-poll', daemon=True)
107
+ self._poll_thread.start()
108
+ self.is_connected = True
109
+ logger.info("Yahoo data stream polling started")
110
+
111
+ def disconnect(self) -> None:
112
+ self._stop_event.set()
113
+ self.is_connected = False
114
+ if self._poll_thread and self._poll_thread.is_alive():
115
+ self._poll_thread.join(timeout=min(5, self.poll_interval + 1))
116
+ logger.info("Disconnected from Yahoo data stream")
117
+
118
+ def is_streaming(self) -> bool:
119
+ return self.is_connected and self._poll_thread is not None and self._poll_thread.is_alive()
120
+
121
+ def add_data_callback(self, callback: Callable) -> None:
122
+ self.data_callbacks.append(callback)
123
+
124
+ def get_latest_data(self, symbol: str) -> Dict[str, Any]:
125
+ if symbol not in self.data_buffer:
126
+ return {}
127
+ buffer = self.data_buffer[symbol]
128
+ return {
129
+ 'latest_trade': buffer['trades'][-1] if buffer['trades'] else None,
130
+ 'latest_quote': buffer['quotes'][-1] if buffer['quotes'] else None,
131
+ 'latest_bar': buffer['latest_bar'],
132
+ 'recent_trades': buffer['trades'][-10:] if buffer['trades'] else [],
133
+ 'recent_quotes': buffer['quotes'][-10:] if buffer['quotes'] else [],
134
+ }
135
+
136
+ def get_historical_data(self, symbol: str, start_date: str, end_date: str) -> pd.DataFrame:
137
+ start, end = self._clamp_window(start_date, end_date, self.interval)
138
+ try:
139
+ raw = self._download(symbol, start=start, end=end, interval=self.interval)
140
+ df = self._normalize_ohlcv(raw)
141
+ if df.empty:
142
+ logger.warning("No Yahoo historical data for %s between %s and %s", symbol, start, end)
143
+ else:
144
+ logger.info("Loaded %s Yahoo bars for %s (%s to %s)", len(df), symbol, start, end)
145
+ return df
146
+ except Exception as e:
147
+ logger.error("Error fetching Yahoo historical data for %s: %s", symbol, e, exc_info=True)
148
+ return pd.DataFrame()
149
+
150
+ def test_connection(self) -> bool:
151
+ try:
152
+ symbol = self.symbols[0] if self.symbols else 'AAPL'
153
+ df = self._download(symbol, period='5d', interval='1d')
154
+ if df is None or df.empty:
155
+ logger.warning("Yahoo connection test returned no data for %s", symbol)
156
+ return False
157
+ logger.info("Yahoo connection test succeeded for %s (%s rows)", symbol, len(df))
158
+ return True
159
+ except Exception as e:
160
+ logger.error("Yahoo connection test failed: %s", e)
161
+ return False
162
+
163
+ def get_connection_status(self) -> Dict[str, Any]:
164
+ return {
165
+ 'is_connected': self.is_connected,
166
+ 'provider': 'yahoo',
167
+ 'interval': self.interval,
168
+ 'poll_interval_seconds': self.poll_interval,
169
+ 'symbols': self.symbols,
170
+ 'data_buffers': {
171
+ symbol: len(buffer['bars']) for symbol, buffer in self.data_buffer.items()
172
+ },
173
+ }
174
+
175
+ def generate_simulated_data(self, symbol: str) -> Dict[str, Any]:
176
+ import random
177
+
178
+ latest_data = self.get_latest_data(symbol)
179
+ base_price = 150.0
180
+ if latest_data.get('latest_bar'):
181
+ base_price = latest_data['latest_bar']['close']
182
+ elif latest_data.get('latest_trade'):
183
+ base_price = latest_data['latest_trade']['price']
184
+
185
+ price_change = random.uniform(-0.01, 0.01) * base_price
186
+ new_price = base_price + price_change
187
+ simulated_bar = {
188
+ 'symbol': symbol,
189
+ 'open': base_price,
190
+ 'high': max(base_price, new_price),
191
+ 'low': min(base_price, new_price),
192
+ 'close': new_price,
193
+ 'volume': random.randint(100, 1000),
194
+ 'timestamp': int(time.time() * 1_000_000),
195
+ }
196
+ self._store_bar(symbol, simulated_bar, emit=False)
197
+ return simulated_bar
198
+
199
+ def _poll_loop(self) -> None:
200
+ while not self._stop_event.wait(self.poll_interval):
201
+ try:
202
+ self._poll_once()
203
+ except Exception as e:
204
+ logger.error("Yahoo poll loop error: %s", e, exc_info=True)
205
+
206
+ def _poll_once(self) -> None:
207
+ for symbol in self.symbols:
208
+ try:
209
+ raw = self._download(symbol, period='5d', interval=self.interval)
210
+ df = self._normalize_ohlcv(raw)
211
+ if df.empty:
212
+ logger.warning("Yahoo poll returned no bars for %s", symbol)
213
+ continue
214
+ self._ingest_new_bars(symbol, df)
215
+ except Exception as e:
216
+ logger.error("Yahoo poll failed for %s: %s", symbol, e)
217
+
218
+ def _ingest_new_bars(self, symbol: str, df: pd.DataFrame) -> None:
219
+ last_ts = self._last_bar_ts.get(symbol)
220
+ rows = df
221
+ if last_ts is not None:
222
+ rows = df[df['timestamp'] > last_ts]
223
+ if rows.empty:
224
+ return
225
+
226
+ for _, row in rows.iterrows():
227
+ ts = pd.Timestamp(row['timestamp'])
228
+ bar = {
229
+ 'symbol': symbol,
230
+ 'open': float(row['open']),
231
+ 'high': float(row['high']),
232
+ 'low': float(row['low']),
233
+ 'close': float(row['close']),
234
+ 'volume': float(row['volume']),
235
+ 'timestamp': int(ts.timestamp() * 1_000_000),
236
+ }
237
+ self._store_bar(symbol, bar, emit=True)
238
+ self._last_bar_ts[symbol] = ts
239
+
240
+ def _store_bar(self, symbol: str, bar: Dict[str, Any], emit: bool) -> None:
241
+ buffer = self.data_buffer[symbol]
242
+ buffer['bars'].append(bar)
243
+ buffer['latest_bar'] = bar
244
+ if len(buffer['bars']) > self._buffer_size:
245
+ buffer['bars'] = buffer['bars'][-self._buffer_size:]
246
+ if emit:
247
+ self._notify_callbacks('bar', bar)
248
+
249
+ def _notify_callbacks(self, data_type: str, data: Dict[str, Any]) -> None:
250
+ for callback in self.data_callbacks:
251
+ try:
252
+ callback(data_type, data)
253
+ except Exception as e:
254
+ logger.error("Error in data callback: %s", e)
255
+
256
+ def _clamp_window(self, start_date: str, end_date: str, interval: str) -> tuple:
257
+ start = pd.to_datetime(start_date, utc=True).tz_convert(None)
258
+ end = pd.to_datetime(end_date, utc=True).tz_convert(None)
259
+ max_lookback = _MAX_LOOKBACK.get(interval)
260
+ if max_lookback is not None:
261
+ earliest = pd.Timestamp.now(tz='UTC').tz_convert(None) - max_lookback
262
+ if start < earliest:
263
+ logger.warning(
264
+ "Yahoo %s bars only cover ~%s; clamping start from %s to %s",
265
+ interval,
266
+ max_lookback,
267
+ start.date(),
268
+ earliest.date(),
269
+ )
270
+ start = earliest
271
+ if end < start:
272
+ end = start + pd.Timedelta(days=1)
273
+ return start.strftime('%Y-%m-%d'), end.strftime('%Y-%m-%d')
274
+
275
+ def _download(
276
+ self,
277
+ symbol: str,
278
+ start: Optional[str] = None,
279
+ end: Optional[str] = None,
280
+ period: Optional[str] = None,
281
+ interval: str = '1d',
282
+ ) -> pd.DataFrame:
283
+ import yfinance as yf
284
+
285
+ kwargs: Dict[str, Any] = {
286
+ 'tickers': symbol,
287
+ 'interval': interval,
288
+ 'auto_adjust': self.auto_adjust,
289
+ 'progress': False,
290
+ 'threads': False,
291
+ }
292
+ if period:
293
+ kwargs['period'] = period
294
+ else:
295
+ kwargs['start'] = start
296
+ kwargs['end'] = end
297
+ return yf.download(**kwargs)
298
+
299
+ @staticmethod
300
+ def _normalize_ohlcv(df: pd.DataFrame) -> pd.DataFrame:
301
+ if df is None or df.empty:
302
+ return pd.DataFrame(columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
303
+
304
+ out = df.copy()
305
+ if isinstance(out.columns, pd.MultiIndex):
306
+ out.columns = [str(col[0]).strip().lower() for col in out.columns]
307
+ else:
308
+ out.columns = [str(c).strip().lower() for c in out.columns]
309
+ rename = {}
310
+ if 'datetime' in out.columns:
311
+ rename['datetime'] = 'timestamp'
312
+ out = out.rename(columns=rename)
313
+
314
+ if 'timestamp' not in out.columns:
315
+ out = out.reset_index()
316
+ time_col = out.columns[0]
317
+ out = out.rename(columns={time_col: 'timestamp'})
318
+
319
+ out['timestamp'] = pd.to_datetime(out['timestamp'], utc=True).dt.tz_localize(None)
320
+
321
+ required = ['timestamp', 'open', 'high', 'low', 'close', 'volume']
322
+ missing = [c for c in required if c not in out.columns]
323
+ if missing:
324
+ logger.error("Yahoo response missing columns: %s", missing)
325
+ return pd.DataFrame(columns=required)
326
+
327
+ out = out[required].dropna()
328
+ out = out.sort_values('timestamp').drop_duplicates(subset=['timestamp'])
329
+ return out.reset_index(drop=True)
config.yaml CHANGED
@@ -29,6 +29,14 @@ alpaca:
29
  websocket_url: 'wss://stream.data.alpaca.markets/v2/iex' # WebSocket URL
30
  account_type: 'paper' # 'paper' or 'live'
31
 
 
 
 
 
 
 
 
 
32
  # Synthetic data generation settings
33
  synthetic_data:
34
  base_price: 150.0
 
29
  websocket_url: 'wss://stream.data.alpaca.markets/v2/iex' # WebSocket URL
30
  account_type: 'paper' # 'paper' or 'live'
31
 
32
+ # Yahoo Finance (optional). Set data_source.type: 'yahoo' to use.
33
+ # Unofficial API, typically delayed; 1m lookback is ~7 days.
34
+ yahoo:
35
+ poll_interval_seconds: 60
36
+ auto_adjust: false
37
+ start_date: '2024-01-01'
38
+ end_date: '2026-12-31'
39
+
40
  # Synthetic data generation settings
41
  synthetic_data:
42
  base_price: 150.0
requirements.txt CHANGED
@@ -15,7 +15,7 @@ torch>=1.9.0
15
 
16
  # Alpaca integration
17
  alpaca-py>=0.42.0
18
- yfinance>=0.2.0
19
 
20
  # Testing
21
  pytest>=6.0.0
 
15
 
16
  # Alpaca integration
17
  alpaca-py>=0.42.0
18
+ yfinance>=1.0
19
 
20
  # Testing
21
  pytest>=6.0.0
tests/test_data_ingestion.py CHANGED
@@ -95,7 +95,25 @@ class TestDataIngestion:
95
 
96
  assert isinstance(result, pd.DataFrame)
97
  mock_generate.assert_called_once_with(config)
98
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  def test_load_data_invalid_type(self, config):
100
  """Test loading data with invalid type"""
101
  config['data_source']['type'] = 'invalid_type'
 
95
 
96
  assert isinstance(result, pd.DataFrame)
97
  mock_generate.assert_called_once_with(config)
98
+
99
+ def test_load_data_yahoo_type(self, config):
100
+ config['data_source']['type'] = 'yahoo'
101
+ mock_df = pd.DataFrame({
102
+ 'timestamp': pd.date_range('2024-01-01', periods=10, freq='D'),
103
+ 'open': [150] * 10,
104
+ 'high': [155] * 10,
105
+ 'low': [145] * 10,
106
+ 'close': [152] * 10,
107
+ 'volume': [1000] * 10,
108
+ })
109
+ mock_stream = MagicMock()
110
+ mock_stream.get_historical_data.return_value = mock_df
111
+ with patch('agentic_ai_system.yahoo_data_stream.YahooDataStream', return_value=mock_stream):
112
+ result = load_data(config)
113
+ assert isinstance(result, pd.DataFrame)
114
+ assert len(result) == 10
115
+ mock_stream.get_historical_data.assert_called_once()
116
+
117
  def test_load_data_invalid_type(self, config):
118
  """Test loading data with invalid type"""
119
  config['data_source']['type'] = 'invalid_type'
tests/test_yahoo_data_stream.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import pytest
3
+ from unittest.mock import patch
4
+
5
+ from agentic_ai_system.yahoo_data_stream import YahooDataStream
6
+
7
+
8
+ @pytest.fixture
9
+ def yahoo_config():
10
+ return {
11
+ 'data_source': {'type': 'yahoo'},
12
+ 'yahoo': {'poll_interval_seconds': 1, 'auto_adjust': False},
13
+ 'trading': {
14
+ 'symbol': 'AAPL',
15
+ 'timeframe': '1d',
16
+ },
17
+ 'realtime_data': {'buffer_size': 10},
18
+ }
19
+
20
+
21
+ def _sample_yahoo_frame():
22
+ idx = pd.date_range('2024-06-03', periods=3, freq='D', tz='America/New_York')
23
+ return pd.DataFrame(
24
+ {
25
+ 'Open': [190.0, 191.0, 192.0],
26
+ 'High': [191.5, 192.5, 193.5],
27
+ 'Low': [189.0, 190.0, 191.0],
28
+ 'Close': [191.0, 192.0, 193.0],
29
+ 'Volume': [1_000_000, 1_100_000, 1_200_000],
30
+ },
31
+ index=idx,
32
+ )
33
+
34
+
35
+ class TestYahooDataStream:
36
+ def test_initialization_from_symbol(self, yahoo_config):
37
+ stream = YahooDataStream(yahoo_config)
38
+ assert stream.symbols == ['AAPL']
39
+ assert stream.interval == '1d'
40
+
41
+ def test_normalize_ohlcv(self, yahoo_config):
42
+ stream = YahooDataStream(yahoo_config)
43
+ df = stream._normalize_ohlcv(_sample_yahoo_frame())
44
+ assert list(df.columns) == ['timestamp', 'open', 'high', 'low', 'close', 'volume']
45
+ assert len(df) == 3
46
+ assert df['close'].iloc[-1] == 193.0
47
+
48
+ def test_clamp_intraday_lookback(self, yahoo_config):
49
+ yahoo_config['trading']['timeframe'] = '1m'
50
+ stream = YahooDataStream(yahoo_config)
51
+ start, end = stream._clamp_window('2020-01-01', '2026-01-01', '1m')
52
+ assert start > '2020-01-01'
53
+ assert end >= start
54
+
55
+ def test_get_historical_data(self, yahoo_config):
56
+ stream = YahooDataStream(yahoo_config)
57
+ with patch.object(stream, '_download', return_value=_sample_yahoo_frame()):
58
+ df = stream.get_historical_data('AAPL', '2024-01-01', '2024-12-31')
59
+ assert len(df) == 3
60
+ assert 'open' in df.columns