Meridian.AI for Stocks #492
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Meridian.AI for Stocks | |
| on: | |
| schedule: | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| # Only one stock training run at a time — queued runs wait, never cancel in-progress | |
| concurrency: | |
| group: stock-training | |
| cancel-in-progress: false | |
| permissions: | |
| contents: write | |
| issues: write | |
| env: | |
| PYTHON_VERSION: '3.11' | |
| CACHE_VERSION: v3 | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| jobs: | |
| setup: | |
| name: Setup & Fetch Stock Data | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_data: ${{ steps.check_data.outputs.has_data }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Cache Dependencies | |
| id: cache-deps | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements.txt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-${{ env.CACHE_VERSION }}- | |
| - name: Install Dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install torch --index-url https://download.pytorch.org/whl/cpu | |
| pip install -r requirements.txt | |
| pip install comet-ml | |
| - name: Free Disk Space | |
| run: | | |
| sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/share/boost | |
| df -h | |
| - name: Fetch Stock Data | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| run: | | |
| echo "Fetching stock data..." | |
| python scripts/fetch_and_store_data.py \ | |
| --db-file training.db \ | |
| --asset-type stock \ | |
| --limit 50 | |
| - name: Check if Data was Fetched | |
| id: check_data | |
| run: | | |
| if [ -f training.db ]; then | |
| COUNT=$(sqlite3 training.db "SELECT COUNT(*) FROM market_data;") | |
| echo "Total rows: $COUNT" | |
| if [ "$COUNT" -gt "0" ]; then | |
| echo "has_data=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "has_data=false" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo "has_data=false" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload Database | |
| if: steps.check_data.outputs.has_data == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: stock-training-db-${{ github.run_number }} | |
| path: training.db | |
| retention-days: 1 | |
| train: | |
| name: Train Stock Model | |
| needs: setup | |
| if: needs.setup.outputs.has_data == 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Download Database | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: stock-training-db-${{ github.run_number }} | |
| - name: Cache Dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-${{ env.CACHE_VERSION }}-${{ hashFiles('requirements.txt') }} | |
| - name: Install Dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install torch --index-url https://download.pytorch.org/whl/cpu | |
| pip install -r requirements.txt | |
| pip install comet-ml huggingface_hub | |
| - name: Download Existing Model | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| run: | | |
| mkdir -p models | |
| python -c " | |
| import os | |
| from huggingface_hub import hf_hub_download | |
| try: | |
| hf_hub_download(repo_id='meridianal/ARA.AI', filename='models/Meridian.AI_Stocks.pt', local_dir='.', token=os.environ.get('HF_TOKEN')) | |
| print('Downloaded existing stock model (loader will skip if old version).') | |
| except Exception as e: | |
| print(f'No existing model found, will train fresh: {e}') | |
| " | |
| - name: Train Model | |
| env: | |
| COMET_API_KEY: ${{ secrets.COMET_API_KEY }} | |
| run: | | |
| echo "Training Stock Model (45min budget)" | |
| python scripts/train_stock_model.py \ | |
| --db-file training.db \ | |
| --output models/Meridian.AI_Stocks.pt \ | |
| --use-all-data \ | |
| --epochs 999 \ | |
| --max-time 45 \ | |
| --comet-api-key "$COMET_API_KEY" | |
| - name: Upload Model | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: stock-model-${{ github.run_number }} | |
| path: models/Meridian.AI_Stocks.pt | |
| retention-days: 7 | |
| deploy: | |
| name: Push to Hugging Face | |
| needs: [train, setup] | |
| if: needs.setup.outputs.has_data == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Install Dependencies | |
| run: | | |
| pip install huggingface_hub python-dotenv | |
| - name: Download Model | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: stock-model-${{ github.run_number }} | |
| path: models/ | |
| - name: Upload to HF Hub | |
| env: | |
| HF_TOKEN: ${{ secrets.HF_TOKEN }} | |
| run: | | |
| echo "Uploading Stock Model to Hugging Face..." | |
| python scripts/push_elite_models.py \ | |
| --model-path models/Meridian.AI_Stocks.pt \ | |
| --model-type stock | |
| cleanup: | |
| name: Cleanup & Report | |
| needs: [setup, train, deploy] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Delete Artifacts | |
| uses: geekyeggo/delete-artifact@v5 | |
| with: | |
| name: stock-training-db-${{ github.run_number }} | |
| continue-on-error: true | |
| - name: Close Previous Failure Issues on Success | |
| if: needs.train.result == 'success' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| // Find open automated stock-training issues and close them | |
| const issues = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'stock-training,automated', | |
| state: 'open', | |
| per_page: 10, | |
| }); | |
| for (const issue of issues.data) { | |
| await github.rest.issues.update({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| state: 'closed', | |
| state_reason: 'completed', | |
| }); | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: issue.number, | |
| body: `Resolved by successful training run [#${context.runNumber}](${context.payload.repository.html_url}/actions/runs/${context.runId}).`, | |
| }); | |
| } | |
| - name: Create Failure Issue | |
| if: needs.train.result == 'failure' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| // Check if an open issue already exists to avoid duplicates | |
| const existing = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| labels: 'stock-training,automated', | |
| state: 'open', | |
| per_page: 5, | |
| }); | |
| if (existing.data.length > 0) { | |
| // Comment on existing issue instead of creating a new one | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: existing.data[0].number, | |
| body: `Still failing — run [#${context.runNumber}](${context.payload.repository.html_url}/actions/runs/${context.runId})\n- Setup: ${{ needs.setup.result }}\n- Training: ${{ needs.train.result }}\n- Deploy: ${{ needs.deploy.result }}`, | |
| }); | |
| return; | |
| } | |
| const title = `Stock Training Failed — Run ${context.runNumber}`; | |
| const body = `## Stock Model Training Failure | |
| **Workflow:** [Run #${context.runNumber}](${context.payload.repository.html_url}/actions/runs/${context.runId}) | |
| **Triggered by:** ${context.eventName} | |
| **Commit:** ${context.sha.substring(0, 7)} | |
| ### Job Results | |
| | Job | Result | | |
| |-----|--------| | |
| | Setup | ${{ needs.setup.result }} | | |
| | Training | ${{ needs.train.result }} | | |
| | Deploy | ${{ needs.deploy.result }} | | |
| > This issue will auto-close when the next training run succeeds. | |
| [View Logs](${context.payload.repository.html_url}/actions/runs/${context.runId}) | |
| `; | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: title, | |
| body: body, | |
| labels: ['bug', 'stock-training', 'automated'] | |
| }); |