Child and robot interacting with projected bubbles

You’ve bought a VPS and written quant strategy code, but how do you get it to “run automatically” and “tell you what’s happening at any moment”? That’s where most people get stuck.

I solved this with OpenClaw.

What is OpenClaw

Simply put: a framework that lets AI assistants call various tools.

Its core capabilities are:

  • Provides CLI tools (openclaw message send) to send Telegram/Discord messages from any script
  • Has a Skill system to equip AI with various capabilities (GitHub, Notion, weather, custom scripts)
  • Supports cron scheduled tasks and remote triggering

For quant trading, the most useful part is message push notifications.

My Quant Signal Push Architecture

cron (every 30 minutes)
    ↓
signal_monitor.py (fetch data → compute signal → compare to last state)
    ↓
new signal?
    ├── yes → generate_report.py (generate PDF)
    │         ↓
    │      openclaw message send (send Telegram)
    │         ├── group (@ signal notifications)
    │         └── private message (attach PDF + timing stats)
    └── no → openclaw message send (private message check-in)

Runs on a Linux machine in the local network 24/7 without interruption.

Key Code Snippets

Sending Messages

import subprocess

def send_telegram(text, pdf_path, target):
    subprocess.run([
        "openclaw", "message", "send",
        "--channel", "telegram",
        "--target", target,
        "--message", text,
        "--media", pdf_path
    ], check=True)

# Push to group (fill target with group ID, negative number)
send_telegram(signal_text, pdf, "-100xxxxxxxxxx")
# Push to private message (fill target with personal user ID)
send_telegram(signal_text + f"\n Time elapsed: {elapsed:.2f}s", pdf, "your_UserID")

This single command replaces the entire Telegram Bot SDK integration, saving the hassle of handling webhooks and polling.

Incremental Caching: Avoid Duplicate Pushes

The easiest pitfall in signal systems: pushing the same signal every run.

Solution: use a state file to track already-pushed signals.

STATE_FILE = "~/.openclaw/workspace/.openclaw/signal_state.json"

def load_state():
    # Load previous state
    ...

def main():
    state = load_state()
    for asset in ASSETS:
        signal = compute_signal(asset)
        key = f"{asset}_{signal.type}_{signal.ts}"
        if state.get(key):
            continue  # Already pushed, skip
        state[key] = True
        push_signal(signal)
    save_state(state)

Combined with a “probe” mechanism (check the latest bar timestamp first, skip if unchanged), execution time drops from 60 seconds to 5–10 seconds per run.

Scheduled Tasks

# /etc/cron.d/quant_strategy
0,30 * * * * as cd ~/.openclaw/workspace && \
  MPLBACKEND=Agg ~/.openclaw/workspace/.venv_quant/bin/python \
  scripts/signal_monitor.py >> .openclaw/signal_monitor.log 2>&1

MPLBACKEND=Agg is key—servers lack displays, so matplotlib needs a non-interactive backend to generate images.

Real Telegram Messages You’ll Receive

When a signal appears:

【Hybrid Strategy Signal】2026-04-08 16:00:00
- BTC 4h: Buy, price 83420.5000, logic=adaptive, time 2026-04-08 16:00:00
- ETH 4h: Buy, price 1628.3300, logic=adaptive, time 2026-04-08 16:00:00
Latest hybrid PDF attached.

When there’s no signal (private message check-in):

【Hybrid Strategy Check-in】2026-04-08 16:30:00
No new buy/sell signals at the moment.
Strategy execution time: 7.43s
Computation mode: hot:cache_hit

The computation mode field tells you whether this run used cache (hot) or recomputed certain assets (warm/cold).

Running Dual Strategies on Two Machines

I run one strategy on each of two machines:

  • One runs the Tang-Chi version (original breakout strategy)
  • One runs the hybrid version (ATR/ADX adaptive)

Both strategies’ signals push to the same Telegram group for cross-checking. When both strategies give a buy signal, confidence is higher.

Handling Failures

Signals stop pushing: check logs first—usually yfinance data fetch timeout. Solution: add retry logic or temporarily switch data sources.

openclaw command not found: PATH issue. The cron environment has a narrower PATH than login shells—set a complete PATH at the top of the cron file or use absolute paths.

PDF generates garbled text: missing Chinese fonts. Fix: sudo apt-get install fonts-noto-cjk.


Having issues building your quant notification system? Find me on Telegram.

⚠️ This post documents personal practice only and does not constitute investment advice.


📷 Cover image · Generated with Flux Schnell AI · Panshi.io brand style