Install
openclaw skills install xpulseReal-time X/Twitter social signal scanner for prediction market traders. Scans via DuckDuckGo (zero API cost) with two-stage local Qwen AI filtering: tradeab...
openclaw skills install xpulseXpulse is a real-time X/Twitter signal scanner optimized for prediction market traders. It detects market-relevant signals on configurable topics using a three-stage intelligence pipeline:
Key philosophy: Zero API costs (DuckDuckGo + local Qwen), fail-closed design (silence over noise), position-aware filtering (no irrelevant alerts).
pip install ddgs pyyaml
ollama pull qwen3:latestollama serve (background process)Create or update your config.yaml:
kalshi:
enabled: true
api_key_id: "your-key-id"
private_key_file: "/path/to/private.key"
ollama:
enabled: true
model: "qwen3:latest"
timeout_seconds: 30
xpulse:
enabled: true
check_interval_minutes: 30
topics:
- "tariff"
- "Ukraine"
- "inflation"
- "Fed rate"
- "Trump policy"
min_confidence: 0.7 # Stage 1: signal confidence threshold
materiality_gate: true # Stage 2: enable 48h deduplication
position_gate: true # Stage 3: only alert on matched positions
max_history_entries: 200
topics list should contain:
site:x.com {topic}| Parameter | Default | Meaning |
|---|---|---|
min_confidence | 0.7 | Stage 1: Only keep signals with ≥70% AI confidence of being tradeable |
check_interval_minutes | 30 | Minimum minutes between scans (prevent API hammering) |
materiality_gate | true | Stage 2: Enable/disable deduplication filter |
position_gate | true | Stage 3: Enable/disable Kalshi position matching |
max_history_entries | 200 | Max entries in 48h signal history (auto-trimmed) |
Input: List of topics (e.g., ["tariff", "inflation", "Ukraine"])
Process:
site:x.com {topic}You are a prediction market analyst. Given these recent X/Twitter posts about '{topic}',
determine if there's a tradeable signal.
{combined_posts}
Respond in JSON: {"has_signal": true/false, "confidence": 0.0-1.0,
"direction": "bullish/bearish/neutral", "summary": "one line"}
Thresholding: Only keep signals where:
has_signal == trueconfidence >= min_confidence (default 0.7)Output: List of candidate signals with direction + confidence
Input: Candidate signals from Stage 1 + 48h signal history
Purpose: Prevent alert fatigue by filtering out:
Process:
You are a personal alert filter for a prediction market trader.
Your job is to PREVENT notification fatigue.
RECENTLY SENT ALERTS (what the user already knows):
- [3h ago] tariff: Trump announces new steel tariffs (confidence: 0.82)
- [6h ago] inflation: Core CPI steady at 3.2% (confidence: 0.75)
CANDIDATE NEW SIGNALS:
- [tariff] Trump discusses additional tariffs (confidence: 0.68)
- [inflation] Fed signals no near-term cuts (confidence: 0.79)
RULES:
- REJECT if same story as recent alert (even different wording)
- REJECT if ongoing background noise
- REJECT if no concrete new event
- ACCEPT only if genuinely new development or significant escalation
- When in doubt, REJECT. The user prefers silence over noise.
Respond in JSON: {"keep": [list of topic strings to keep], "reasoning": "..."}
Fail-Closed Design:
Output: Filtered list (subset of Stage 1 candidates)
Input: Filtered signals from Stage 2 + active Kalshi positions
Purpose: Only alert the user about signals that match their active positions
Process:
Example:
Signal: topic="Trump tariff", summary="Trump announces new steel tariffs"
Signal words: {trump, tariff, steel, announces, new}
Kalshi position: ticker="TRUMP-TARIFF-2025", title="Will Trump enact steel tariffs?"
Position keywords: {trump, tariff, 2025, steel, enact}
Overlap: {trump, tariff, steel} = 3 keywords ✓ MATCH
→ Signal passes to iMessage alert
Suppressed Signals:
.x_signal_cache.json) for morning briefOutput: Critical signals matched to positions → iMessage alert
All signals (pre-filter) are cached in .x_signal_cache.json:
{
"signals": [
{
"topic": "tariff",
"confidence": 0.82,
"direction": "bearish",
"summary": "Trump announces new steel tariffs",
"post_count": 3
}
],
"topics_scanned": 5,
"timestamp": 1709990400.0
}
Used for:
# Single run
python -m xpulse.xpulse
# With full logging
DEBUG=1 python -m xpulse.xpulse
# Dry run (logs but doesn't send alerts)
python -m xpulse.xpulse --dry-run
# Force a run regardless of interval
python -m xpulse.xpulse --force
Via OpenClaw config:
skills:
xpulse:
enabled: true
schedule: "*/30 * * * *"
timeout_seconds: 120
Via crontab:
# Add to crontab -e:
*/30 * * * * cd /path/to/xpulse && python -m xpulse.xpulse >> /tmp/xpulse.log 2>&1
Via launchd (macOS):
<!-- ~/.launchd/com.xpulse.scanner.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.xpulse.scanner</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/path/to/xpulse.py</string>
</array>
<key>StartInterval</key>
<integer>1800</integer>
<key>StandardOutPath</key>
<string>/tmp/xpulse.log</string>
<key>StandardErrorPath</key>
<string>/tmp/xpulse.log</string>
</dict>
</plist>
⚠️ X signal — affects your Kalshi positions:
📈 Trump tariff: Trump announces new 25% steel tariffs (82% conf) [TRUMP-TARIFF-2025]
📉 Inflation: Core CPI stays sticky, Fed unlikely to cut (75% conf) [FED-RATE-2026]
➡️ Ukraine: Peace talks accelerate, settlement probability rising (68% conf) [UKRAINE-2026]
{
"signals": [
{
"topic": "Trump tariff",
"confidence": 0.82,
"direction": "bearish",
"summary": "Trump announces new 25% steel tariffs",
"post_count": 3
},
{
"topic": "inflation",
"confidence": 0.75,
"direction": "bearish",
"summary": "Core CPI stays sticky, Fed unlikely to cut further",
"post_count": 2
}
],
"topics_scanned": 5,
"timestamp": 1709990400.0
}
See references/materiality-gate.md for detailed explanation of:
See references/position-matching.md for:
Error: Connection refused or ollama: command not found
Fix:
# Start Ollama in background
ollama serve &
# Or install if missing:
# Install from https://ollama.ai, then:
ollama pull qwen:latest
Error: ollama run qwen:latest fails
Fix:
ollama pull qwen:latest
# Or use qwen3 if available:
# ollama pull qwen3:latest
Likely causes:
Debug:
from ddgs import DDGS
d = DDGS()
results = list(d.text("site:x.com Trump tariff", max_results=5))
print(results)
Expected behavior: If you have no active Kalshi positions, all signals are suppressed (logged silently). This is intentional — position-aware alerting is the goal.
To test: Either:
position_gate: false in config (alerts all non-materiality signals)Automatic: History is capped at 200 entries (oldest trimmed first)
Manual reset:
rm ~/.openclaw/state/x_signal_history.json
You are a prediction market analyst. Given these recent X/Twitter posts about '{topic}',
determine if there's a tradeable signal.
{combined_posts}
Respond in JSON: {"has_signal": true/false, "confidence": 0.0-1.0,
"direction": "bullish/bearish/neutral", "summary": "one line"}
You are a personal alert filter for a prediction market trader.
Your job is to PREVENT notification fatigue. Only let through signals that are genuinely NEW and MATERIAL.
RECENTLY SENT ALERTS (what the user already knows):
{history_block}
CANDIDATE NEW SIGNALS:
{candidate_block}
RULES:
- REJECT if the signal covers the same story/development as a recent alert (even with different wording)
- REJECT if it's ongoing background noise (e.g. 'Trump discusses tariffs' when tariffs have been in the news for days)
- REJECT if there's no concrete new event, just commentary or speculation
- ACCEPT only if: (a) a genuinely new development occurred (vote, announcement, emergency, data release, market move),
OR (b) a significant escalation/reversal of something previously reported
- When in doubt, REJECT. The user prefers silence over noise.
Respond in JSON: {"keep": [list of topic strings to keep], "reasoning": "one line explaining why"}
Social signal intelligence layer for the Prediction Market Trading Stack.
| Connected Skill | How It Connects |
|---|---|
| Market Morning Brief | X signals appear in your daily morning digest |
| Kalshalyst | Social signals validate or challenge contrarian edges |
| Prediction Market Arbiter | Correlate social sentiment with cross-platform divergences |
| Kalshi Command Center | Act on position-matched signals via trade execution |
Install the complete stack:
clawhub install kalshalyst kalshi-command-center polymarket-command-center prediction-market-arbiter xpulse portfolio-drift-monitor market-morning-brief personality-engine
Battle-tested in production trading environments. Design principles:
10 runs per day = ~10-20 minutes total runtime + zero API cost. Minimal resource usage.
references/materiality-gate.md — Signal deduplication + 48h windowreferences/position-matching.md — Keyword overlap logicfrom xpulse import check_x_signals
# Single run
result = check_x_signals(state={}, dry_run=False, force=False)
# Returns: bool (True if alert sent, False otherwise)
# State dict tracks:
state = {
"last_x_signal_check": 1709990400.0, # Unix timestamp of last scan
"last_x_signals_silent": [...] # Suppressed signals (logged only)
}
Xpulse is designed for iteration:
min_confidence to catch more signals (noisier), raise to reduce false positivesposition_gate: false if you want ALL signals (not recommended)Author: KingMadeLLC
Found a bug? Have a feature request? Want to share results?
Part of the OpenClaw Prediction Stack — the first prediction market skill suite on ClawHub.