Feed Hunter portal: dark theme dashboard on localhost:8888

- Web portal with 5 views: dashboard, feed, investigations, sims, status
- Enhanced triage with 40+ claim patterns
- Position monitor script
- Pipeline report generator
- Systemd service for portal
This commit is contained in:
2026-02-08 00:06:24 -06:00
parent 8638500190
commit 5ce3e812a1
9 changed files with 2137 additions and 993 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,329 @@
#!/usr/bin/env python3
"""
Feed Hunter Position Monitor
Checks current prices for active simulation positions and updates P&L
Usage:
python3 monitor-positions.py [--update] [--alert-threshold 0.1]
"""
import argparse
import json
import os
import re
import time
from datetime import datetime, timezone
from urllib.parse import urlparse
import urllib.request
import urllib.error
# Price sources for different asset types
PRICE_SOURCES = {
'polymarket': {
'url_pattern': r'polymarket\.com.*?(@[\w]+|markets?[\w/-]+)',
'api_base': 'https://gamma-api.polymarket.com/markets',
'method': 'polymarket_api'
},
'crypto': {
'url_pattern': r'(bitcoin|btc|ethereum|eth|solana|sol|cardano|ada)',
'api_base': 'https://api.coingecko.com/api/v3/simple/price',
'method': 'coingecko_api'
},
'stock': {
'url_pattern': r'(nasdaq|nyse|stock)',
'api_base': 'https://api.example.com/stock', # Placeholder
'method': 'stock_api'
}
}
def detect_asset_type(position):
"""Detect what type of asset this position represents"""
asset = position.get('asset', '').lower()
source_url = position.get('source_post', '')
strategy = position.get('strategy', '').lower()
# Check if it's a prediction market
if 'polymarket' in source_url or 'polymarket' in asset or 'polymarket' in strategy:
return 'polymarket'
elif 'super bowl' in asset or 'win' in asset or 'seahawks' in asset:
return 'polymarket' # Sports bets are usually prediction markets
elif any(crypto in asset for crypto in ['btc', 'eth', 'sol', 'bitcoin', 'ethereum', 'solana']):
return 'crypto'
elif any(stock in asset for stock in ['stock', 'nasdaq', 'nyse', 'spy', 'tsla']):
return 'stock'
else:
return 'unknown'
def get_polymarket_price(position):
"""Get current price from Polymarket API"""
try:
# Extract market info from source URL or asset name
source_url = position.get('source_post', '')
asset = position.get('asset', '')
# For now, return mock price update
# In real implementation, would parse URL and call Polymarket API
if 'seahawks' in asset.lower() and 'super bowl' in asset.lower():
# Mock price movement for Seahawks Super Bowl bet
import random
# Simulate some price movement around entry price
entry_price = position.get('entry_price', 0.68)
price_change = random.uniform(-0.05, 0.05)
new_price = max(0.01, min(0.99, entry_price + price_change))
return {
'success': True,
'price': round(new_price, 2),
'timestamp': datetime.now(timezone.utc).isoformat(),
'source': 'polymarket_mock'
}
except Exception as e:
print(f"Polymarket price fetch error: {e}")
return {'success': False, 'error': 'Price fetch failed'}
def get_crypto_price(position):
"""Get crypto price from CoinGecko"""
try:
asset = position.get('asset', '').lower()
# Map asset names to CoinGecko IDs
coin_map = {
'bitcoin': 'bitcoin',
'btc': 'bitcoin',
'ethereum': 'ethereum',
'eth': 'ethereum',
'solana': 'solana',
'sol': 'solana'
}
coin_id = None
for name, id in coin_map.items():
if name in asset:
coin_id = id
break
if not coin_id:
return {'success': False, 'error': 'Unknown crypto asset'}
url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd"
with urllib.request.urlopen(url, timeout=10) as response:
data = json.loads(response.read().decode())
price = data[coin_id]['usd']
return {
'success': True,
'price': price,
'timestamp': datetime.now(timezone.utc).isoformat(),
'source': 'coingecko'
}
except Exception as e:
print(f"Crypto price fetch error: {e}")
return {'success': False, 'error': str(e)}
def get_stock_price(position):
"""Get stock price (placeholder - would need real API)"""
# Placeholder for stock price API
return {'success': False, 'error': 'Stock price API not implemented'}
def get_current_price(position):
"""Get current price for a position based on asset type"""
asset_type = detect_asset_type(position)
if asset_type == 'polymarket':
return get_polymarket_price(position)
elif asset_type == 'crypto':
return get_crypto_price(position)
elif asset_type == 'stock':
return get_stock_price(position)
else:
return {'success': False, 'error': f'Unknown asset type: {asset_type}'}
def calculate_pnl(position, current_price):
"""Calculate P&L for a position"""
entry_price = position.get('entry_price', 0)
quantity = position.get('quantity', 0)
size = position.get('size', 0)
position_type = position.get('type', 'long')
if position_type == 'bet':
# For prediction market bets (binary outcome)
# P&L = quantity * (current_price - entry_price)
unrealized_pnl = quantity * (current_price - entry_price)
unrealized_pnl_pct = ((current_price / entry_price) - 1) * 100 if entry_price > 0 else 0
else:
# For regular long positions
# P&L = size * (current_price / entry_price - 1)
unrealized_pnl = size * (current_price / entry_price - 1) if entry_price > 0 else 0
unrealized_pnl_pct = ((current_price / entry_price) - 1) * 100 if entry_price > 0 else 0
return {
'unrealized_pnl': round(unrealized_pnl, 2),
'unrealized_pnl_pct': round(unrealized_pnl_pct, 2),
'current_price': current_price
}
def check_stop_loss_take_profit(position, current_price):
"""Check if position hits stop loss or take profit"""
stop_loss = position.get('stop_loss')
take_profit = position.get('take_profit')
triggers = []
if stop_loss and current_price <= stop_loss:
triggers.append({
'type': 'stop_loss',
'trigger_price': stop_loss,
'current_price': current_price,
'message': f"STOP LOSS triggered at {current_price} (target: {stop_loss})"
})
if take_profit and current_price >= take_profit:
triggers.append({
'type': 'take_profit',
'trigger_price': take_profit,
'current_price': current_price,
'message': f"TAKE PROFIT triggered at {current_price} (target: {take_profit})"
})
return triggers
def load_active_positions():
"""Load active positions from JSON file"""
positions_file = 'data/simulations/active.json'
if not os.path.exists(positions_file):
return None
try:
with open(positions_file, 'r') as f:
return json.load(f)
except Exception as e:
print(f"Error loading positions: {e}")
return None
def save_active_positions(data):
"""Save updated positions to JSON file"""
positions_file = 'data/simulations/active.json'
try:
with open(positions_file, 'w') as f:
json.dump(data, f, indent=2)
return True
except Exception as e:
print(f"Error saving positions: {e}")
return False
def send_alert(position, trigger_info, pnl_info):
"""Send alert about position trigger (placeholder)"""
print(f"🚨 ALERT: {trigger_info['message']}")
print(f" Position: {position['asset'][:50]}...")
print(f" P&L: ${pnl_info['unrealized_pnl']:.2f} ({pnl_info['unrealized_pnl_pct']:+.1f}%)")
print(f" Strategy: {position.get('strategy', 'Unknown')}")
print("")
def monitor_positions(update=False, alert_threshold=0.1):
"""Monitor all active positions"""
print("🖤 Feed Hunter - Position Monitor")
print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("")
# Load active positions
data = load_active_positions()
if not data:
print("❌ No active positions file found")
return
positions = data.get('positions', [])
if not positions:
print("📭 No active positions to monitor")
return
print(f"📊 Monitoring {len(positions)} active positions...")
print("")
updated = False
alerts_sent = 0
for i, position in enumerate(positions):
pos_id = position.get('id', f'pos_{i}')
asset = position.get('asset', 'Unknown')
print(f"[{i+1}] {asset[:50]}...")
print(f" ID: {pos_id}")
print(f" Entry: ${position.get('entry_price', 0):.2f}")
print(f" Strategy: {position.get('strategy', 'Unknown')}")
# Get current price
price_result = get_current_price(position)
if price_result['success']:
current_price = price_result['price']
print(f" Current: ${current_price:.2f} ({price_result['source']})")
# Calculate P&L
pnl_info = calculate_pnl(position, current_price)
pnl_color = "💚" if pnl_info['unrealized_pnl'] >= 0 else "❤️"
print(f" P&L: {pnl_color} ${pnl_info['unrealized_pnl']:.2f} ({pnl_info['unrealized_pnl_pct']:+.1f}%)")
# Check stop loss / take profit
triggers = check_stop_loss_take_profit(position, current_price)
for trigger in triggers:
print(f" 🚨 {trigger['message']}")
send_alert(position, trigger, pnl_info)
alerts_sent += 1
# Update position if requested
if update:
position.update(pnl_info)
position['last_updated'] = datetime.now(timezone.utc).isoformat()
updated = True
else:
print(f" ❌ Price fetch failed: {price_result.get('error', 'Unknown error')}")
print("")
# Save updates if any
if updated and update:
if save_active_positions(data):
print("✅ Positions updated and saved")
else:
print("❌ Failed to save position updates")
# Summary
print("📈 Monitor Summary:")
print(f" Positions checked: {len(positions)}")
print(f" Alerts sent: {alerts_sent}")
if updated:
print(f" Updates saved: ✅")
print("")
def main():
parser = argparse.ArgumentParser(description="Monitor active simulation positions")
parser.add_argument("--update", action="store_true", help="Update positions with current prices")
parser.add_argument("--alert-threshold", type=float, default=0.1, help="P&L threshold for alerts (10% default)")
parser.add_argument("--interval", type=int, help="Run continuously every N seconds")
args = parser.parse_args()
try:
if args.interval:
print(f"🔄 Starting continuous monitoring every {args.interval} seconds")
print("Press Ctrl+C to stop")
print("")
while True:
monitor_positions(update=args.update, alert_threshold=args.alert_threshold)
print(f"⏱️ Sleeping for {args.interval} seconds...")
time.sleep(args.interval)
else:
monitor_positions(update=args.update, alert_threshold=args.alert_threshold)
except KeyboardInterrupt:
print("\n⏹️ Monitor stopped")
except Exception as e:
print(f"❌ Monitor error: {e}")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,63 @@
# Feed Hunter Portal
Self-contained web dashboard for monitoring the X/Twitter feed intelligence pipeline.
## Quick Start
The portal is already running at **http://localhost:8888/**
## Views
- **Dashboard** (`/`) - Overview of active simulations, recent scrapes, signal counts
- **Feed** (`/feed`) - Latest scraped posts with triage status (color-coded)
- **Investigations** (`/investigations`) - Detailed investigation reports
- **Simulations** (`/simulations`) - Active paper positions, P&L, trade history
- **Status** (`/status`) - Pipeline health, last run times, Chrome status
## Management
**Manual Control:**
```bash
# Start portal
cd portal && python3 server.py
# Stop portal
pkill -f "python3 server.py"
```
**Systemd Service:**
```bash
# Check status
systemctl --user status feed-hunter-portal
# Start/stop/restart
systemctl --user start feed-hunter-portal
systemctl --user stop feed-hunter-portal
systemctl --user restart feed-hunter-portal
# View logs
journalctl --user -u feed-hunter-portal -f
```
## Features
- **Dark theme** optimized for monitoring
- **Mobile-friendly** responsive design
- **Real-time updates** every 30 seconds on dashboard
- **Color-coded** triage status (High Value = green, Investigate = orange, etc.)
- **P&L tracking** with profit/loss indicators
- **No external dependencies** - uses Python stdlib only
## Data Sources
The portal reads from:
- `../data/x-feed/*/posts.json` - Scraped Twitter posts
- `../data/x-feed/*/triage.json` - Triage results
- `../data/investigations/*.json` - Investigation reports
- `../data/simulations/active.json` - Active paper positions
- `../data/simulations/history.json` - Closed trades
- `../config.json` - Pipeline configuration
## Development
Single file architecture in `server.py` with embedded CSS/JS. Easy to modify and extend.

View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,333 @@
#!/bin/bash
# Feed Hunter — Full pipeline with HTML reporting
# Usage: ./run-and-report.sh [scroll_pages] [output_dir]
#
# Runs: scrape → triage → investigate → simulate → generate HTML report
set -e
PAGES=${1:-8}
OUTPUT_DIR=${2:-"reports/$(date +%Y%m%d-%H%M%S)"}
BASE="/home/wdjones/.openclaw/workspace"
SKILL="$BASE/skills/deep-scraper/scripts"
PROJECT="$BASE/projects/feed-hunter"
DATA="$BASE/data/x-feed"
echo "=== Feed Hunter Pipeline + Report ==="
echo "$(date '+%Y-%m-%d %H:%M:%S %Z')"
echo "Output: $OUTPUT_DIR"
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Ensure Chrome is running with debug port
if ! curl -s http://127.0.0.1:9222/json >/dev/null 2>&1; then
echo "Starting Chrome..."
bash "$SKILL/launch-chrome-debug.sh"
fi
# Stage 1: Scrape
echo ""
echo "--- Stage 1: Scrape ($PAGES pages) ---"
python3 -u "$SKILL/scrape-x-feed.py" --port 9222 --scroll-pages "$PAGES" 2>&1 | tee "$OUTPUT_DIR/scrape.log"
# Find latest scrape
LATEST=$(ls -dt "$DATA"/20* | head -1)
echo "Latest scrape: $LATEST"
# Stage 2: Triage
echo ""
echo "--- Stage 2: Triage ---"
python3 "$SKILL/triage-posts.py" "$LATEST/posts.json" 2>&1 | tee "$OUTPUT_DIR/triage.log"
# Stage 3: Generate investigation tasks
TRIAGE="$LATEST/triage.json"
INVESTIGATION_COUNT=0
if [ -f "$TRIAGE" ]; then
INVESTIGATION_COUNT=$(python3 -c "import json; d=json.load(open('$TRIAGE')); print(len(d.get('investigation_queue',[])))")
if [ "$INVESTIGATION_COUNT" -gt 0 ]; then
echo ""
echo "--- Stage 3: Investigation Tasks ---"
python3 "$PROJECT/investigate.py" "$TRIAGE" --output "$LATEST/investigations" 2>&1 | tee "$OUTPUT_DIR/investigate.log"
else
echo ""
echo ">>> No posts worth investigating this run."
fi
else
echo ">>> No triage output found."
fi
# Stage 4: Update simulations if new investigations found
SIMULATION_UPDATES=0
if [ -f "$PROJECT/data/investigations" ] && [ "$INVESTIGATION_COUNT" -gt 0 ]; then
echo ""
echo "--- Stage 4: Simulation Updates ---"
python3 "$PROJECT/simulator.py" --check-investigations 2>&1 | tee "$OUTPUT_DIR/simulation.log" || true
SIMULATION_UPDATES=1
fi
# Stage 5: Generate HTML Report
echo ""
echo "--- Stage 5: Generate Report ---"
# Get summary stats
POSTS_COUNT=$(python3 -c "import json; d=json.load(open('$LATEST/posts.json')); print(len(d.get('posts',[])))")
HIGH_VALUE=0
WORTH_INVESTIGATING=0
DISMISSED=0
if [ -f "$TRIAGE" ]; then
HIGH_VALUE=$(python3 -c "import json; d=json.load(open('$TRIAGE')); print(len(d.get('high_value',[])))")
WORTH_INVESTIGATING=$(python3 -c "import json; d=json.load(open('$TRIAGE')); print(len(d.get('worth_investigating',[])))")
DISMISSED=$(python3 -c "import json; d=json.load(open('$TRIAGE')); print(len(d.get('dismissed',[])))")
fi
# Get investigation results
VERIFIED_CLAIMS=0
ACTIONABLE_STRATEGIES=0
if [ -d "$LATEST/investigations" ]; then
VERIFIED_CLAIMS=$(find "$LATEST/investigations" -name "*.json" -exec grep -l '"verdict".*"VERIFIED"' {} \; | wc -l)
ACTIONABLE_STRATEGIES=$(find "$LATEST/investigations" -name "*.json" -exec grep -l '"actionable".*true' {} \; | wc -l)
fi
# Get simulation status
ACTIVE_POSITIONS=0
TOTAL_PNL=0
if [ -f "$PROJECT/data/simulations/active.json" ]; then
ACTIVE_POSITIONS=$(python3 -c "import json; d=json.load(open('$PROJECT/data/simulations/active.json')); print(len(d.get('positions',[])))" 2>/dev/null || echo "0")
TOTAL_PNL=$(python3 -c "import json; d=json.load(open('$PROJECT/data/simulations/active.json')); print(sum(p.get('unrealized_pnl',0) for p in d.get('positions',[])))" 2>/dev/null || echo "0")
fi
cat > "$OUTPUT_DIR/report.html" << EOF
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Feed Hunter Report - $(date '+%Y-%m-%d %H:%M')</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #0d1117;
color: #f0f6fc;
line-height: 1.6;
margin: 0;
padding: 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
border-bottom: 2px solid #30363d;
padding-bottom: 2rem;
margin-bottom: 2rem;
}
.header h1 {
color: #58a6ff;
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
.header .subtitle {
color: #8b949e;
font-size: 1.1rem;
}
.metrics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.5rem;
margin-bottom: 3rem;
}
.metric-card {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 1.5rem;
text-align: center;
}
.metric-value {
font-size: 2.5rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.metric-label {
color: #8b949e;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.positive { color: #3fb950; }
.negative { color: #f85149; }
.neutral { color: #58a6ff; }
.section {
background: #161b22;
border: 1px solid #30363d;
border-radius: 8px;
padding: 2rem;
margin-bottom: 2rem;
}
.section h2 {
color: #f0f6fc;
border-bottom: 1px solid #30363d;
padding-bottom: 1rem;
margin-bottom: 1.5rem;
}
.status-good {
color: #3fb950;
font-weight: bold;
}
.status-warn {
color: #ffab40;
font-weight: bold;
}
.status-error {
color: #f85149;
font-weight: bold;
}
.log-snippet {
background: #0d1117;
border: 1px solid #30363d;
border-radius: 4px;
padding: 1rem;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 0.85rem;
max-height: 200px;
overflow-y: auto;
white-space: pre-wrap;
margin: 1rem 0;
}
.timestamp {
color: #8b949e;
font-size: 0.9rem;
text-align: right;
margin-top: 2rem;
border-top: 1px solid #30363d;
padding-top: 1rem;
}
.link-button {
display: inline-block;
background: #58a6ff;
color: #fff;
padding: 0.75rem 1.5rem;
border-radius: 6px;
text-decoration: none;
font-weight: bold;
margin: 0.5rem 0.5rem 0.5rem 0;
transition: background 0.2s;
}
.link-button:hover {
background: #4493e1;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🖤 Feed Hunter Report</h1>
<div class="subtitle">Pipeline execution completed at $(date '+%Y-%m-%d %H:%M:%S %Z')</div>
</div>
<div class="metrics">
<div class="metric-card">
<div class="metric-value neutral">$POSTS_COUNT</div>
<div class="metric-label">Posts Scraped</div>
</div>
<div class="metric-card">
<div class="metric-value $([ $HIGH_VALUE -gt 0 ] && echo 'positive' || echo 'neutral')">$HIGH_VALUE</div>
<div class="metric-label">High Value</div>
</div>
<div class="metric-card">
<div class="metric-value $([ $INVESTIGATION_COUNT -gt 0 ] && echo 'positive' || echo 'neutral')">$INVESTIGATION_COUNT</div>
<div class="metric-label">Investigated</div>
</div>
<div class="metric-card">
<div class="metric-value $([ $VERIFIED_CLAIMS -gt 0 ] && echo 'positive' || echo 'neutral')">$VERIFIED_CLAIMS</div>
<div class="metric-label">Verified Claims</div>
</div>
<div class="metric-card">
<div class="metric-value $([ $ACTIVE_POSITIONS -gt 0 ] && echo 'positive' || echo 'neutral')">$ACTIVE_POSITIONS</div>
<div class="metric-label">Active Positions</div>
</div>
<div class="metric-card">
<div class="metric-value $(python3 -c "print('positive' if $TOTAL_PNL >= 0 else 'negative')")">$${TOTAL_PNL}</div>
<div class="metric-label">Total P&L</div>
</div>
</div>
<div class="section">
<h2>📊 Pipeline Summary</h2>
<p><strong>Scraping:</strong> Collected $POSTS_COUNT posts from $PAGES pages</p>
<p><strong>Triaging:</strong> $HIGH_VALUE high-value, $WORTH_INVESTIGATING worth investigating, $DISMISSED dismissed</p>
<p><strong>Investigation:</strong> $INVESTIGATION_COUNT posts analyzed, $VERIFIED_CLAIMS claims verified</p>
<p><strong>Simulations:</strong> $ACTIVE_POSITIONS active positions, \$$TOTAL_PNL unrealized P&L</p>
</div>
<div class="section">
<h2>🔍 Investigation Highlights</h2>
$(if [ $VERIFIED_CLAIMS -gt 0 ]; then
echo "<div class='status-good'>✓ Found $VERIFIED_CLAIMS verified claims worth tracking</div>"
else
echo "<div class='status-warn'>⚠ No verified claims found in this run</div>"
fi)
$(if [ $ACTIONABLE_STRATEGIES -gt 0 ]; then
echo "<div class='status-good'>✓ Identified $ACTIONABLE_STRATEGIES actionable strategies</div>"
else
echo "<div class='status-warn'>⚠ No actionable strategies identified</div>"
fi)
</div>
<div class="section">
<h2>📈 Simulation Status</h2>
$(if [ $ACTIVE_POSITIONS -gt 0 ]; then
echo "<div class='status-good'>✓ $ACTIVE_POSITIONS positions actively tracked</div>"
echo "<div>Current P&L: <span class='$(python3 -c "print('positive' if $TOTAL_PNL >= 0 else 'negative')")'>\$$TOTAL_PNL</span></div>"
else
echo "<div class='status-warn'>⚠ No active positions</div>"
fi)
</div>
<div class="section">
<h2>📱 Quick Actions</h2>
<a href="http://localhost:8888/" class="link-button">Open Portal Dashboard</a>
<a href="http://localhost:8888/feed" class="link-button">View Latest Feed</a>
<a href="http://localhost:8888/investigations" class="link-button">Investigation Reports</a>
<a href="http://localhost:8888/simulations" class="link-button">Simulation Tracker</a>
</div>
<div class="section">
<h2>📋 Execution Logs</h2>
<h3>Scraping Output</h3>
<div class="log-snippet">$(tail -n 10 "$OUTPUT_DIR/scrape.log" 2>/dev/null || echo "No scrape log found")</div>
<h3>Triage Output</h3>
<div class="log-snippet">$(tail -n 10 "$OUTPUT_DIR/triage.log" 2>/dev/null || echo "No triage log found")</div>
$(if [ $INVESTIGATION_COUNT -gt 0 ]; then
echo "<h3>Investigation Output</h3>"
echo "<div class='log-snippet'>$(tail -n 10 "$OUTPUT_DIR/investigate.log" 2>/dev/null || echo "No investigation log found")</div>"
fi)
</div>
<div class="timestamp">
Report generated: $(date '+%Y-%m-%d %H:%M:%S %Z')<br>
Data directory: $LATEST<br>
Report saved: $OUTPUT_DIR/report.html
</div>
</div>
</body>
</html>
EOF
echo ""
echo "=== Pipeline + Report Complete ==="
echo "📊 Dashboard: http://localhost:8888/"
echo "📄 Report: $OUTPUT_DIR/report.html"
echo "📁 Data: $LATEST"
echo ""
echo "Summary:"
echo " Posts: $POSTS_COUNT | High Value: $HIGH_VALUE | Investigated: $INVESTIGATION_COUNT"
echo " Verified: $VERIFIED_CLAIMS | Actionable: $ACTIONABLE_STRATEGIES"
echo " Active Positions: $ACTIVE_POSITIONS | P&L: \$$TOTAL_PNL"

View File

@ -16,38 +16,97 @@ from datetime import datetime, timezone
# Patterns that suggest a verifiable claim # Patterns that suggest a verifiable claim
CLAIM_PATTERNS = [ CLAIM_PATTERNS = [
# Specific dollar amounts and profits
(r'\$(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)\s*(profit|gained|made|earned|up|return)', 'dollar_profit'),
(r'made\s+\$(\d{1,3}(?:,\d{3})*(?:\.\d{2})?)', 'dollar_profit'),
(r'(\d{1,3}(?:,\d{3})*)\s*(k|K)\s*(profit|gained|made|earned|up|return)', 'k_profit'),
(r'(\d+(?:\.\d+)?)\s*(million|M)\s+(profit|made|earned|up)', 'million_profit'),
# Performance claims # Performance claims
(r'(\d+[\d.]*)\s*%\s*(win|success|profit|return|accuracy|hit rate)', 'performance_claim'), (r'(\d+[\d.]*)\s*%\s*(win|success|profit|return|accuracy|hit rate)', 'performance_claim'),
(r'wins?\s+(\d+[\d.]*)\s*%', 'performance_claim'), (r'wins?\s+(\d+[\d.]*)\s*%', 'performance_claim'),
(r'(\d+[\d.]*)\s*%\s+of the time', 'performance_claim'), (r'(\d+[\d.]*)\s*%\s+of the time', 'performance_claim'),
(r'(\d+[\d.]*)x\s+(return|profit|gain)', 'multiplier_claim'), (r'(\d+[\d.]*)x\s+(return|profit|gain|returns)', 'multiplier_claim'),
(r'(\d+[\d.]*)\s*x\s+in\s+(\d+)\s+(day|week|month|year)s?', 'timeframe_multiplier'),
# Trading track record claims
(r'(\d+)\s+(win|profitable)\s+(trade|bet|position)s?\s+in\s+a\s+row', 'streak_claim'),
(r'(\d+)\s+out\s+of\s+(\d+)\s+(trade|bet|call)s?\s+(right|correct|profitable)', 'ratio_claim'),
(r'(\d+[\d.]*)\s*%\s+accuracy\s+(this|last)\s+(week|month|year)', 'accuracy_claim'),
(r'portfolio\s+up\s+(\d+[\d.]*)\s*%', 'portfolio_performance'),
# Copy/follow trading # Copy/follow trading
(r'copy(ing|cat)?\s+(trader|user|bet|position|strat)', 'copy_trading'), (r'copy(ing|cat)?\s+(trader|user|bet|position|strat)', 'copy_trading'),
(r'follow\s+(this|my|their)\s+(trade|bet|position|strat)', 'copy_trading'), (r'follow\s+(this|my|their)\s+(trade|bet|position|strat)', 'copy_trading'),
(r'mirror(ing)?\s+(trade|bet|position)', 'copy_trading'), (r'mirror(ing)?\s+(trade|bet|position)', 'copy_trading'),
(r'best\s+(trader|performer)\s+(on|in)\s+\w+', 'top_trader'),
# Crypto/DeFi specific claims
(r'(\d+[\d.]*)\s+(eth|btc|sol|bnb|ada|dot)\s+(profit|gained|made|up)', 'crypto_profit'),
(r'yield\s+farming\s+.{0,30}\s*(\d+[\d.]*)\s*%', 'yield_claim'),
(r'staking\s+.{0,30}\s*(\d+[\d.]*)\s*%\s*(apy|apr)', 'staking_yield'),
(r'liquidity\s+provider?\s+.{0,30}\s*(\d+[\d.]*)\s*%', 'lp_yield'),
(r'(\d+[\d.]*)x\s+(leverage|margin)', 'leverage_claim'),
# NFT claims
(r'(flip|sold|bought)\s+.{0,30}\s+for\s+(\d+[\d.]*)\s+(eth|sol)', 'nft_flip'),
(r'(\d+[\d.]*)\s+(eth|sol)\s+floor', 'nft_floor'),
(r'mint\s+(price|cost)\s+(\d+[\d.]*)', 'mint_price'),
# Arbitrage/spread # Arbitrage/spread
(r'(arb|arbitrage|spread|mismatch|mispriced)', 'arbitrage_opp'), (r'(arb|arbitrage|spread|mismatch|mispriced)', 'arbitrage_opp'),
(r'risk[\s-]?free', 'arbitrage_opp'), (r'risk[\s-]?free', 'arbitrage_opp'),
(r'guaranteed\s+(profit|return|money)', 'arbitrage_opp'), (r'guaranteed\s+(profit|return|money)', 'arbitrage_opp'),
(r'(\d+[\d.]*)\s*%\s+guaranteed', 'guaranteed_return'),
# Prediction/betting # Sports betting / Prediction markets
(r'(polymarket|kalshi|manifold|prediction\s+market)', 'prediction_market'), (r'(polymarket|kalshi|manifold|prediction\s+market)', 'prediction_market'),
(r'(odds|probability)\s+.{0,20}\s*(\d+[\d.]*)\s*%', 'odds_claim'), (r'(odds|probability)\s+.{0,20}\s*(\d+[\d.]*)\s*%', 'odds_claim'),
(r'(yes|no)\s+shares?\s+at\s+(\d+)', 'shares_price'), (r'(yes|no)\s+shares?\s+at\s+(\d+)', 'shares_price'),
(r'betting\s+(\d+[\d.]*)\s*%\s+on', 'betting_confidence'),
(r'(\d+[\d.]*)\s*%\s+chance\s+(of|that)', 'probability_estimate'),
# Price/target claims # Price/target claims
(r'(target|pt|price target)\s*[:\s]+\$?\s*(\d[\d,.]*)', 'price_target'), (r'(target|pt|price target)\s*[:\s]+\$?\s*(\d[\d,.]*)', 'price_target'),
(r'(entry|buy)\s*(at|zone|point)\s*[:\s]+\$?\s*(\d[\d,.]*)', 'entry_point'), (r'(entry|buy)\s*(at|zone|point)\s*[:\s]+\$?\s*(\d[\d,.]*)', 'entry_point'),
(r'next\s+(resistance|support)\s+at\s+\$?(\d[\d,.]*)', 'technical_level'),
(r'going\s+to\s+\$?(\d[\d,.]*)', 'price_prediction'),
# Airdrop/free money # Airdrop/free money
(r'(airdrop|free\s+money|free\s+tokens?|claiming)', 'airdrop'), (r'(airdrop|free\s+money|free\s+tokens?|claiming)', 'airdrop'),
(r'(step\s+\d|how\s+to\s+(get|claim|earn))', 'howto'), (r'(step\s+\d|how\s+to\s+(get|claim|earn))', 'howto'),
(r'eligible\s+for\s+(\d+[\d.]*)\s+tokens?', 'airdrop_amount'),
# User/account references # Signal/alert claims
(r'(signal|alert|call)\s+.{0,30}\s*(\d+[\d.]*)\s*%\s+(gain|profit)', 'signal_performance'),
(r'last\s+(\d+)\s+(signal|call|alert)s?\s+all\s+(profit|green|won)', 'signal_streak'),
(r'(\d+[\d.]*)\s*%\s+win\s+rate\s+on\s+(signal|call)s?', 'signal_winrate'),
# Time-sensitive claims
(r'(today|yesterday|this\s+week)\s+.{0,30}\s*(\d+[\d.]*)\s*%\s+(up|gain|profit)', 'recent_performance'),
(r'(\d+[\d.]*)\s*%\s+in\s+(\d+)\s+(hour|day|week)s?', 'timeframe_performance'),
(r'last\s+(\d+)\s+(day|week|month)s?\s+.{0,30}\s*(\d+[\d.]*)\s*%', 'period_performance'),
# User/account references with performance
(r'@\w+.*?(portfolio|track\s*record|history|performance)', 'user_reference'), (r'@\w+.*?(portfolio|track\s*record|history|performance)', 'user_reference'),
(r'(this\s+(guy|trader|user|person|account))\s+.{0,30}(profit|win|return|made)', 'user_reference'), (r'(this\s+(guy|trader|user|person|account))\s+.{0,30}(profit|win|return|made)', 'user_reference'),
(r'trader\s+with\s+(\d+[\d.]*)\s*%\s+(accuracy|win\s+rate)', 'trader_stats'),
(r'(\d+[\d.]*)\s*(million|M|k|K)\s+follower\s+trader', 'influencer_trader'),
# Strategy claims
(r'(strategy|method|system)\s+that\s+.{0,30}(\d+[\d.]*)\s*%', 'strategy_claim'),
(r'using\s+.{0,30}\s+(\d+[\d.]*)\s*%\s+(success|win)', 'method_performance'),
(r'secret\s+(strategy|method|formula)', 'secret_method'),
# Options/derivatives
(r'(call|put)\s+option\s+.{0,30}\s*(\d+[\d.]*)\s*%', 'options_claim'),
(r'(\d+[\d.]*)\s*%\s+(otm|itm|out\s+of\s+the\s+money|in\s+the\s+money)', 'options_probability'),
(r'delta\s+(\d+[\d.]*)', 'options_delta'),
# Course/education monetization
(r'course\s+that\s+.{0,30}(\d+[\d.]*)\s*(k|K|dollar)', 'course_earning'),
(r'teaching\s+.{0,30}\s+(\d+[\d.]*)\s*(k|K)\s+student', 'teaching_claim'),
(r'(\d+[\d.]*)\s*%\s+of\s+students\s+(profit|succeed)', 'student_success'),
] ]
# Link domains that are investigatable # Link domains that are investigatable