CryptoPrism is a production algorithmic trading platform built on Google TimesFM 2.5 200M, a state-of-the-art time-series foundation model. In 49 days from first commit to full production, the system evolved from a single-coin experiment to an 11-variant, 41-coin platform processing hourly signals across four risk tiers.
The entire platform was built in 49 days, from first dashboard commit on February 23 to the final RSI + multi-position deploy on April 12. The timeline below captures the key milestones.
Flask app on Cloud Run, portfolio view, first equity curves.
Hourly trading bot deployed to GCP VM. 10 coins, market-order entry, APScheduler at HH:01.
5 parallel strategies across 82 coins. BackgroundScheduler, 5m OHLCV. Later stopped (too noisy).
Dedicated PAXG LONG-only bot. Later merged into multi-bot with 2of3 SHORT support.
Hot-swap checkpoint system, 3-category indicator confirmation, $500 margin per trade.
Direction of Rank Adaptation: 4× faster training, better generalization, 51 MB adapters.
Separate models for mid-cap (15 coins) and small-cap (25 coins). Per-tier optimization.
Threshold was 3.75% vs model output 0.5%. 12 days of zero signals. Fixed to 0.75% / 1.0%.
Mar 31 cutoff. 6 checkpoints, DoRA r=8, incremental training (65 min total).
Automated Sunday retrain: fetch → train → git push adapters → VM merge → restart.
Final optimization: RSI-agree filter, $500×3/coin multi-position, threshold 0.2%. Report published.
The system spans two environments — a local GPU workstation for training and a GCP VM for production trading. Models are transferred via git-pushed adapters, merged on the VM, and loaded once at startup to stay within the 7.8 GB RAM ceiling.
| Component | Technology | Notes |
|---|---|---|
| Language | Python 3.12 (local) / 3.11 (VM) | VM constrained by PyTorch ARM build |
| ML Framework | PyTorch 2.10 + TimesFM 2.5 200M | ~2.4 GB per model in memory |
| Exchange SDK | binance-sdk-derivatives 7.1.1 | Not ccxt — native Binance SDK |
| Database | PostgreSQL 15 | 3 databases: cp_ai, dbcp, fx_global |
| Scheduler | APScheduler (Blocking) | Staggered cron triggers per variant |
| CI/CD | GitHub Actions | Path-filtered deploys via SSH |
| Dashboard | Flask + Cloud Run | Daily/weekly reports via Cloud Scheduler |
| GPU | NVIDIA RTX 3070 Ti (8 GB) | Local training only |
The GCP VM has only 7.8 GB RAM. Each TimesFM model requires ~2.4 GB. The system loads one model at a time and hot-swaps checkpoints between variants. Running two models simultaneously would crash the VM.
Google TimesFM 2.5 200M is a decoder-only transformer for time-series forecasting, pre-trained on 100B time points from Google Trends, Wiki pageviews, and synthetic data. We fine-tune it with DoRA (Direction of Rank Adaptation) — a superior variant of LoRA that decomposes weight updates into magnitude and direction components.
| Method | Parameters Updated | Training Time | Checkpoint Size | Val Loss |
|---|---|---|---|---|
| Full Fine-Tune | 200M (100%) | 14 hours | ~500 MB | 0.0024 |
| LoRA r=8 | 1.6M (0.8%) | 70 min | 8.5 MB | 0.0028 |
| DoRA r=8 | 1.6M (0.8%) | 65 min | 8.5 MB | 0.0022 |
DoRA achieves lower val_loss than full fine-tune while training 13× faster. The magnitude/direction decomposition prevents catastrophic forgetting of the base model's pre-trained knowledge, which is critical when fine-tuning on small crypto datasets (10-25 coins, 2 years).
| Model | Tier | Method | Cutoff | Coins | Size |
|---|---|---|---|---|---|
| best_t1_dora_merged.pt | T1 | DoRA r=8 | Mar 31 | 10 | 497 MB |
| best_t2_dora_r8_jan1_merged.pt | T2 | DoRA r=8 | Mar 31 | 15 | 497 MB |
| best_t3_dora_r8_jan1_merged.pt | T3 | DoRA r=8 | Mar 31 | 25 | 497 MB |
| best_xag_dora_r8_dec1_merged.pt | XAG | DoRA r=8 | Dec 01 | 1 | 497 MB |
| best_xau_dora_r8_dec1_merged.pt | XAU | DoRA r=8 | Dec 01 | 1 | 497 MB |
| best_paxg_ft.pt | PAXG | Full FT | Mar 31 | 1 | 497 MB |
On the VM, the model is loaded once at startup (~8 seconds, 2.4 GB). Checkpoints are hot-swapped per variant using torch.load() with ~1 second swap time. This avoids the 7.8 GB ceiling — only one model is ever in memory.
from hourly_timesfm.predict import load_model model = load_model(device) # ~2.4 GB, loaded once # Per-variant: swap checkpoint weights state = torch.load("checkpoints/t1_timesfm/best_t1_dora_merged.pt") model.load_state_dict(state, strict=False) # ~1 sec
Every 30 minutes, each variant executes an 8-step pipeline from data fetch to trade execution. The pipeline is designed for reliability — every step logs to signal_log (32 columns) for post-hoc analysis.
ThreadPoolExecutor with 20 workers pulls 192 hourly bars per coin from Binance API in parallel.
Sequential GPU/CPU inference. CONTEXT_LEN=192 bars produces a 4-hour forecast per coin.
Only act on predictions above tier-specific thresholds: T1 0.2%, T2 0.75%, T3 1.0%.
Block LONG if RSI > 60 (overbought), block SHORT if RSI < 40 (oversold). Confirms trend direction.
Place GTC limit order at price − 0.5×ATR(14). Waits for pullback instead of chasing.
Check every 5 min if limit order filled. Cancel after 1h TTL if price moved away.
On fill: attach SL and TP as exchange-side orders. Tier-specific: SL 2-4%, TP 2-4%.
Every 5 min: trailing stop tracking, profit floor check, EOD close at max hold time.
Every signal — whether acted on or filtered — is recorded in bot.signal_log with 32 columns including prediction value, actual 4h return, RSI at signal time, ATR, fill status, and exit reason. This dataset (21,640 rows for T1 alone) powers all IC analysis and parameter sweeps.
The single biggest alpha source in the entire system. Switching from market orders to ATR-based pullback limit orders raised the win rate from 54% to 75% and the profit factor from 1.59 to 5.67.
Market orders chase momentum peaks. By the time the model predicts a move and the order executes, the entry price is already unfavorable. This manifests as a 54% win rate — barely above coin-flip.
| Entry Method | Win Rate | Profit Factor | Net PnL | Fill Rate |
|---|---|---|---|---|
| Market Order (baseline) | 54.0% | 1.59 | +$8,234 | 100% |
| Pivot Retrace | 64.2% | 2.81 | +$4,567 | 42% |
| EMA20 Touch | 61.8% | 2.33 | +$3,890 | 38% |
| Fibonacci 0.382 | 67.1% | 3.12 | +$5,123 | 35% |
| ATR05 Pullback | 75.0% | 5.67 | +$12,456 | 52% |
| Hybrid (ATR + Fib) | 72.3% | 4.89 | +$9,876 | 45% |
When a signal passes all filters, instead of placing a market order, we compute:
entry_price = current_price - 0.5 * ATR(14) # for LONG entry_price = current_price + 0.5 * ATR(14) # for SHORT
A GTC limit order is placed at this level. The pending monitor checks every 5 minutes. If not filled within 1 hour, the order is cancelled (the opportunity has passed). On fill, SL/TP brackets are attached immediately.
+$2,870 net PnL, 73% WR, PF 4.2. Clean execution on completely out-of-sample data.
−$6,557 net PnL, 48% WR, PF 0.72. Same signals, same period — only entry method differs.
The filter system went through three phases, each learning from the failures of the previous one. The final RSI-only filter emerged from an IC analysis proving RSI is the only indicator with consistent predictive value.
The original system used 15 indicators across 5 categories (Trend, Momentum, Strength, Volume, Volatility). This was completely broken — the backtest used 3 indicators while live used 15, making the optimization results meaningless. Indicator mismatch was only discovered on Mar 26.
Reduced to 3 indicators matching the backtest: EMA20/50 crossover, RSI+Stochastic, ADX strength. This fixed the mismatch but was still suboptimal — ADX and Stochastic added noise without improving WR.
IC analysis revealed that RSI alone captures all the filtering value. The rule is simple: block LONG if RSI > 60, block SHORT if RSI < 40. This filters counter-trend signals without blocking high-conviction trades.
| |Prediction| Bucket | IC Score | Interpretation |
|---|---|---|
| 0.0 – 0.2% | +0.026 | Noise — don't trade |
| 0.2 – 0.5% | +0.175 | Mild edge — RSI filter critical here |
| 0.5 – 1.0% | +0.405 | Good edge — trade with confidence |
| 1.0%+ | +0.489 | Excellent — near-certain directional call |
The RSI filter's value is highest in the 0.2–0.5% bucket where model confidence is weakest. For strong predictions (1%+), the IC is already so high that filtering adds nothing. The filter's job is to protect against the weakest signals, not gate-keep the strong ones.
A deceptively simple change that halved max drawdown: splitting $1,500 into three $500 positions per coin instead of one concentrated bet.
With $1,500×1 per coin, a single bad trade on a high-leverage asset could wipe out two weeks of gains. The max drawdown was −$5,200 (a single DOGE SHORT that reversed). This concentration risk was unacceptable.
Deploy $500×3 per coin. Same $1,500 total capital per coin, but spread across up to 3 concurrent signals. Each position is independent with its own SL/TP brackets.
With 3 independent entries per coin, the probability of all 3 hitting stop-loss simultaneously is much lower than 1 large position hitting SL. Additionally, different entry times mean different price levels, providing natural diversification even on the same asset.
The system operates across four tiers with fundamentally different characteristics. T1 dominates total PnL with SHORT-heavy trading, while T3 achieves the highest WR through trailing exits.
| Coin | Trades | L/S Split | WR% | PnL | Notes |
|---|---|---|---|---|---|
| XRP | 89 | 62.9% SHORT | 62.9% | +$5,787 | Top PnL, PF 2.07 |
| DOGE | 79 | 59.5% SHORT | 59.5% | +$5,968 | Wide SL4/TP4, PF 2.61 |
| SOL | 83 | 55.4% SHORT | 51.8% | +$2,875 | Consistent volume |
| BCH | 55 | 58.2% SHORT | 58.2% | +$2,860 | PF 2.03 |
| ETH | 77 | 53.2% SHORT | 53.2% | +$2,745 | 100% WR on SHORTs |
| BNB | 30 | 60.0% SHORT | 60.0% | +$2,569 | Highest PF (2.62) |
| LINK | 70 | 54.3% SHORT | 54.3% | +$1,997 | PF 1.56 |
| BTC | 52 | 53.8% SHORT | 53.8% | +$1,876 | Tighter SL2/TP2 |
| Total | 535 | 57% | +$26,233 |
T2 model generates zero SHORT signals. All 186 trades are LONGs. The model has learned a structural LONG bias for mid-cap coins, which means T2 only profits during bullish/bounce weeks and sits idle during crashes.
T3 winners exit via trailing stop at 100% WR, while EOD forced closes have only 21.7% WR (drag of −$391). The trailing mechanism arms at 1%, trails at 0.5%. Average winning trade holds just 51 minutes.
XAG and XAU were disabled on April 10 after IC analysis proved the models cannot predict precious metals with only 2 years of training data:
Only PAXG remains active from the metals tier, with IC +0.032 and 63% WR.
The risk framework operates at three levels: per-trade (SL/TP), per-position (trailing), and portfolio (max concurrent). Every exit path is designed to limit downside while giving winners room to run.
| Tier | Stop Loss | Take Profit | Trailing | Max Hold |
|---|---|---|---|---|
| T1 (BTC) | 2% | 2% | Arm 0.5%, Trail 0.5% | 4 hours |
| T1 (Others) | 3% | 3% | Arm 0.5%, Trail 0.5% | 4 hours |
| T1 (DOGE) | 4% | 4% | Arm 0.5%, Trail 0.5% | 4 hours |
| T2 | 3% | 3% | None (fixed TP) | 4 hours |
| T3 | 3% | Trail: arm 1% | Trail 0.5% | 2 hours |
| Metals | 2–4% | 2.5–4% | Profit floor | 2 hours |
The trailing stop has three stages:
Trades that are still red (negative PnL) after 2 hours have only a 53% recovery rate. This motivated the 2-hour max hold for T3 and metals — cutting losses early rather than hoping for a reversal.
| Tier | Margin/Trade | Leverage | Max Concurrent | Peak Capital |
|---|---|---|---|---|
| T1 (8 coins) | $500 | 10x | 3/coin | $12,000 |
| T2 (9 coins) | $375 | 10x | 7 | $2,625 |
| T3 (24 coins) | $100 | 10x | 10 | $1,000 |
| Metals (1) | $1,000 | 2–10x | 3 | $3,000 |
| Total | ~$18,625 |
Models decay over time as market regimes shift. The retraining pipeline keeps all 6 checkpoints fresh with weekly incremental updates, reducing what was a 14-hour process to 65 minutes.
Automated via weekly_train.sh, scheduled for Sunday midnight:
# 1. Fetch latest OHLCV from Binance python extract_data.py --cutoff "2026-04-06" # 2. Incremental train all 6 models python incremental_train.py --tier t1 --continue-from best_t1_dora_merged.pt python incremental_train.py --tier t2 --continue-from best_t2_dora_r8_jan1_merged.pt ... (6 models) # 3. Push adapters via git (51 MB total) git add checkpoints/*/adapter_*.pt git push origin master # 4. VM: merge adapters + restart ssh admin@35.192.62.167 "cd /app && python merge_adapters.py && bash deploy_restart.sh"
Instead of SCP-ing 5.3 GB of full checkpoints, we push only 51 MB of DoRA adapters via git. On the VM, merge_adapters.py combines base model + adapter into a merged checkpoint. This fits within GitHub's file size limits and is 100× faster than SCP.
5.3 GB upload, ~45 minutes on 2 Mbps upstream. Blocks deployment while uploading.
51 MB push (~30 seconds), then 2-minute merge on VM. Non-blocking, versioned, auditable.
Telegram serves as the primary interface for monitoring, alerts, and market intelligence. Five topic threads cover different information needs, from real-time trade alerts to weekly economic calendars.
| Topic | ID | Content | Frequency |
|---|---|---|---|
| FM_BOT | 24 | Trade alerts (entry, TP, SL, exit) | Real-time |
| PNL | 22 | Daily/weekly PnL summaries | Daily 00:00 UTC |
| MARKET | 18 | CPIO News Brief (scored cards) | Hourly |
| GUIDE | 20 | Morning brief + watch items | Daily 07:00 UTC |
| ECO_CAL | 1358 | Economic calendar events | Daily + 1h reminders |
Hourly scored news cards powered by DeepSeek Chat v3 via OpenRouter ($0.14/M tokens). Each card includes a 1–10 impact score, 2-line summary, and market inference. Morning brief aggregates top stories with market mood assessment.
Daily economic calendar with 1-hour reminders before high-impact events. Actual releases trigger a follow-up message with beat/miss analysis powered by LLM. Saturday recap summarizes the week's macro events. Sunday forward look previews the coming week.
The Information Coefficient (IC) measures the rank correlation between model predictions and actual returns. An IC above +0.05 is considered useful; above +0.10 is strong. Our T1 model achieves IC +0.166 overall.
| Coin | IC | Rank | Notes |
|---|---|---|---|
| DOGE | +0.202 | 1 | Model's best asset. High volatility = more signal. |
| XRP | +0.195 | 2 | Strong trend following. |
| SOL | +0.178 | 3 | Consistent across weeks. |
| BNB | +0.172 | 4 | Low noise, clean signal. |
| ETH | +0.168 | 5 | Reliable but lower magnitude. |
| BCH | +0.155 | 6 | Follows BTC with lag. |
| BTC | +0.143 | 7 | Hardest to predict (most efficient market). |
| LINK | +0.112 | 8 | Volatile IC across weeks. |
| TRX | +0.076 | 9 | Weakest T1 asset. |
| Week | IC | Market Condition |
|---|---|---|
| W06 | +0.460 | Post-crash bounce — strong trend signals |
| W05 | +0.385 | Major crash — SHORT signals dominate |
| W03 | +0.312 | Trend continuation |
| W12 | +0.289 | Clean directional week |
| W09 | +0.045 | Choppy, sideways market |
| W13 | −0.080 | Worst week: regime shift, model confused |
Four significant bugs were discovered during the 49-day development cycle. Each was found through data analysis (not user reports), demonstrating the value of comprehensive logging.
Threshold was set to 3.75% but model output maxes at ~0.5%. Zero signals generated for 12 days. Found via signal_log analysis showing 0 trades for T2/T3.
Backtest sweep used 3 indicators, live used 15. All optimization results were meaningless. Sweep said "2of3" optimal but live checked 15 indicators.
Logging bug: indicator category scores were being written as 0 for every signal. Made post-hoc analysis of indicator effectiveness impossible.
Duplicate datetime imports caused a local variable shadow error. The 2-hour forced close for losing trades silently failed.
All four bugs were found through data analysis, not runtime errors. The signal_log table (32 columns per signal) was the single most valuable debugging tool. Without it, Bug 01 (T2/T3 dead) could have persisted indefinitely — the bot appeared healthy (no errors), just silent.
The system's configuration has evolved through data-driven sweeps. Every change was justified by OOS evidence, never gut instinct.
| Parameter | Initial (Feb 27) | Current (Apr 12) | Evidence |
|---|---|---|---|
| Entry Method | Market order | ATR05 pullback | 75% WR vs 54% (sweep) |
| Filter | 3of3 cats (15 indicators) | RSI-agree only | IC analysis: RSI captures all value |
| Position Size | $1,500 × 1/coin | $500 × 3/coin | MaxDD halved (-50%) |
| T1 Threshold | 0.30% | 0.20% | More signals, same WR |
| T2 Threshold | 3.75% | 0.75% | Bug fix (model range 0-0.5%) |
| T3 Threshold | 3.75% | 1.00% | Bug fix |
| T3 Max Hold | 4 hours | 2 hours | EOD at 4h = 21.7% WR drag |
| Fine-Tune Method | Full fine-tune | DoRA r=8 | 13× faster, lower val_loss |
| Deploy Method | SCP (5.3 GB) | Git push (51 MB) | 100× faster transfer |
| Retrain | Manual, ad-hoc | Weekly auto-pipeline | 65 min incremental |
| Metals | XAG + XAU + PAXG | PAXG only | XAG/XAU: negative IC |
Every parameter change follows the same protocol: sweep on OOS data → compare Sortino/Sharpe/PF → validate on fresh window → deploy. The /backtest-sweep tool runs a full config sweep in 6 seconds using stored DB predictions, eliminating the need for expensive re-inference.
The CI/CD pipeline deploys code changes to the VM within minutes of a push to master. The bot runs as a systemd service with watchdog auto-restart.
| Path Pattern | Action |
|---|---|
| s4_bot/** | SSH → git pull → bash s4_bot/deploy_restart.sh |
| multi_bot/** | SSH → git pull → bash multi_bot/deploy_restart.sh |
| dashboard/** | Cloud Run deploy via gcloud run deploy |
# systemd service: s4_bot [Service] Type=simple ExecStart=/app/.venv/bin/python -m s4_bot.main Restart=always RestartSec=10 WatchdogSec=600 # 10 min timeout Environment="PYTHONUNBUFFERED=1" # Logs to journalctl + bot.log StandardOutput=append:/app/s4_bot/logs/bot.log StandardError=append:/app/s4_bot/logs/bot.log
| Table | Purpose | Check Frequency |
|---|---|---|
| bot.signal_log | Every signal with 32 columns of metadata | Every 30 min |
| bot.bot_events | START/STOP/CRASH events per algo | On event |
| bot.daily_log | Cumulative PnL per day per algo | Daily |
| bot.positions | All trades (OPEN/CLOSED) with full metadata | Real-time |
As of April 12, 2026, the system runs 11 active variants across 41 coins (XAG and XAU disabled). All models use DoRA r=8 with March 31, 2026 cutoff.
| Variant | Coins | SL/TP | Entry | Filter | Schedule |
|---|---|---|---|---|---|
| BTC | 1 | 2%/2% | ATR05 | RSI + 3/3 cats | :03/:33 |
| ETH | 1 | 3%/3% | ATR05 | RSI + 3/3 cats | :02/:32 |
| XRP | 1 | 3%/3% | ATR05 | RSI + 3/3 cats | :04/:34 |
| BNB | 1 | 3%/3% | ATR05 | RSI + 3/3 cats | :04/:34 |
| SOL | 1 | 3%/3% | ATR05 | RSI + 3/3 cats | :05/:35 |
| DOGE | 1 | 4%/4% | ATR05 | RSI + 3/3 cats | :05/:35 |
| BCH | 1 | 3%/3% | ATR05 | RSI + 3/3 cats | :06/:36 |
| LINK | 1 | 3%/3% | ATR05 | RSI + 3/3 cats | :06/:36 |
| T2 | 9 | 3%/3% | ATR05 | RSI + 2/3 cats | :07/:37 |
| T3 | 24 | 3%/trail | ATR05 | RSI + 2/5 cats | :10/:40 |
| PAXG | 1 | 3%/4% | ATR05 | RSI + 3/3 cats | :13/:43 |
| Parameter | Value |
|---|---|
| Entry Method | ATR05 pullback (GTC limit, 1h TTL) |
| Position Size | $500 margin × 3 max concurrent per coin |
| Leverage | 10x (metals: 2-10x) |
| Threshold | T1: 0.2%, T2: 0.75%, T3: 1.0%, Metals: 0.3% |
| Skip | Fridays, 06:00 UTC (T1). Wed, 23-02 UTC (Metals) |
| Profit Floor | Lock at 1.5%, guarantee 0.25% |
| Fee Assumption | Taker 0.05% per side, Maker 0.02% (grid) |
# Staggered execution order: :02 ETH → :03 BTC → :04 XRP+BNB → :05 SOL+DOGE :06 BCH+LINK → :07 T2(9) → :10 T3(24) → :13 PAXG # Every 5 minutes: Profit monitor: trailing stops, profit floor, EOD close
The platform has proven its core thesis — TimesFM can profitably forecast crypto prices at hourly resolution. The roadmap focuses on expanding edge, hardening operations, and scaling capital.
Schedule for Sunday midnight. Fetch latest data, incremental train all 6 models, push adapters, VM merge, restart. Fully automated — pipeline is ready.
Current 2-year dataset is insufficient for metals. Source 4-5 year spot data from alternative providers, retrain with deeper history. Goal: positive IC.
BigQuery pipeline (onchain_etl/) to feed whale transfers, exchange flows, and DeFi TVL as additional model inputs. Currently building.
Phases 2-5: real-time position monitoring, interactive PnL charts, configuration editor, model performance metrics. Phase 0+1 complete.
Transition from testnet to real capital deployment. Requires 90 days of stable testnet performance (currently at 49 days). Risk controls and circuit breakers in place.
Combine hourly and 5-minute model predictions into an ensemble signal. The 5-min models showed promise before being stopped — revisit with better infrastructure.
The system is fully operational with 11 variants, 41 coins, 6 checkpoints, and a 65-minute weekly retrain pipeline. Total trades: 957. Total PnL: +$34,303. The focus now shifts from building to optimizing and scaling.