Skill: iamcooper2026/crypto-safe-portfolio-analyzer-pro (version 1.0.1)

--- name: crypto-safe-portfolio-analyzer-pro description: "Safe cryptocurrency portfolio tracking and P&L analysis. Monitors portfolio value, calculates profit/loss with custom cost basis, tracks live prices via CoinGecko, and generates detailed reports. Use for portfolio performance tracking, P&L calculation, position monitoring, allocation analysis, and market overviews." --- # Crypto SAFE Portfolio Analyzer Pro Track cryptocurrency portfolios, calculate P&L with custom cost basis, and generate comprehensive reports with live market data. ## Overview This skill provides professional-grade portfolio tracking for cryptocurrency investors. It fetches live prices from CoinGecko, calculates accurate P&L based on your cost basis, and generates detailed reports showing position breakdown, allocations, and performance metrics. **Key Features:** - Real-time price tracking for 20+ cryptocurrencies - Custom cost basis for accurate P&L calculation - Portfolio allocation analysis with target vs. actual comparisons - Market sentiment analysis across watchlists - Multiple output formats (text reports, JSON data) - Integration-ready for Discord bots, automated alerts, and cron jobs ## Quick Start ### 1. Analyze Portfolio Holdings For a quick portfolio analysis, provide holdings as a JSON object: ```bash python3 scripts/portfolio-analyzer.py portfolio --holdings '{"BTC": 1.5, "ETH": 10.2, "SOL": 50, "DOGE": 10000}' ``` **Expected Output:** ``` 📊 CRYPTO PORTFOLIO REPORT ================================================== 💰 Total Value: $125,750.50 🟢 Total P&L: $15,750.50 (+14.32%) 📈 POSITION BREAKDOWN: BTC: 💵 Value: $89,237.50 (71.0% of portfolio) 🟢 P&L: $8,987.50 (+11.20%) 🔴 24h: -3.92% ``` ### 2. Market Overview Get sentiment and price overview for your watchlist: ```bash python3 scripts/portfolio-analyzer.py market ``` ### 3. Set Up Configuration For advanced features like cost basis tracking, create `portfolio-config.json`: ```json { "cost_basis": { "BTC": 45000, "ETH": 2800, "SOL": 85 }, "watchlist": ["BTC", "ETH", "SOL", "DOGE", "XRP"], "target_allocation": { "BTC": 40, "ETH": 30, "SOL": 20, "Other": 10 } } ``` ## Core Capabilities ### 1. Portfolio Value Calculation Calculates total portfolio value with position-level breakdown: - **Live pricing** from CoinGecko API (no API key required) - **Cost basis tracking** for accurate P&L calculation - **24-hour change** for each position - **Portfolio allocation** percentages - **Total portfolio P&L** with percentage gains/losses **Supported Coins:** BTC, ETH, SOL, DOGE, XRP, ADA, AVAX, LINK, DOT, UNI, NEAR, ATOM, and more. ### 2. Market Analysis Provides market sentiment analysis across your watchlist: - **Market sentiment** classification (Bullish/Bearish/Neutral) - **Average 24-hour change** across all tracked coins - **Individual coin performance** with prices and changes - **Market cap data** for supported cryptocurrencies ### 3. Report Generation Multiple output formats for different use cases: **Text Format:** Human-readable reports with emoji indicators and formatted numbers ```bash # Generate text report python3 scripts/portfolio-analyzer.py portfolio --holdings holdings.json ``` **JSON Format:** Structured data for automation and integrations ```bash # Generate JSON data for automation python3 scripts/portfolio-analyzer.py portfolio --holdings holdings.json --format json ``` ### 4. Integration Ready Designed for automation and integration: - **Discord bot integration** - Post daily portfolio updates - **Cron job automation** - Schedule regular portfolio checks - **Alert systems** - Monitor portfolio value thresholds - **API consumption** - JSON output ready for webhooks and APIs ## Common Use Cases ### Daily Portfolio Monitoring Set up automated daily reports: ```bash # Add to cron or OpenClaw cron jobs python3 scripts/portfolio-analyzer.py portfolio --holdings ~/.crypto/holdings.json > daily-report.txt ``` ### Rebalancing Analysis Compare current vs. target allocations: 1. Set target allocations in `portfolio-config.json` 2. Run portfolio analysis to see current allocations 3. Identify positions that need rebalancing ### Performance Tracking Monitor P&L over time: ```bash # Log daily values for trend analysis echo "$(date): $(python3 scripts/portfolio-analyzer.py portfolio --holdings holdings.json --format json | jq '.total_value')" >> portfolio-history.log ``` ### Tax Reporting Generate P&L data for tax calculations: ```bash # Export detailed P&L breakdown python3 scripts/portfolio-analyzer.py portfolio --holdings holdings.json --format json | jq '.breakdown' ``` ## Configuration Options ### Cost Basis Setup Define your average purchase prices for accurate P&L: ```json { "cost_basis": { "BTC": 45000, // Your average BTC purchase price "ETH": 2800, // Your average ETH purchase price "SOL": 85 // Your average SOL purchase price } } ``` Without cost basis, P&L calculations use current price as basis (showing $0 P&L). ### Watchlist Customization Customize which coins to track in market overviews: ```json { "watchlist": ["BTC", "ETH", "SOL", "DOGE", "XRP", "ADA", "AVAX", "LINK"] } ``` ### Target Allocation Set portfolio allocation targets for rebalancing analysis: ```json { "target_allocation": { "BTC": 40, // Target 40% Bitcoin "ETH": 30, // Target 30% Ethereum "SOL": 20, // Target 20% Solana "Other": 10 // Target 10% other assets } } ``` ## Error Handling The skill includes robust error handling: - **API failures**: Graceful fallback when CoinGecko is unavailable - **Invalid holdings**: Clear error messages for malformed JSON - **Missing config**: Uses sensible defaults when config files are missing - **Network timeouts**: 10-second timeout with error reporting ## Integration Examples ### Discord Bot Integration ```javascript const { exec } = require('child_process'); async function postPortfolioUpdate() { const command = 'python3 scripts/portfolio-analyzer.py portfolio --holdings holdings.json --format json'; exec(command, (error, stdout) => { if (!error) { const portfolio = JSON.parse(stdout); const message = `📊 Portfolio: $${portfolio.total_value.toLocaleString()} | P&L: ${portfolio.total_pnl >= 0 ? '🟢' : '🔴'} ${portfolio.total_pnl_pct.toFixed(2)}%`; // Send to Discord channel } }); } ``` ### OpenClaw Automation ```javascript // In OpenClaw skills or cron jobs const portfolioData = await exec('python3 ~/.openclaw/workspace/skills/crypto-portfolio-tracker/scripts/portfolio-analyzer.py portfolio --holdings holdings.json --format json'); const portfolio = JSON.parse(portfolioData); if (portfolio.total_value > 100000) { await notify('Portfolio over $100k!'); } ``` ## Resources ### scripts/ - **portfolio-analyzer.py**: Main portfolio tracking and analysis script - **portfolio-tracker.js**: Node.js integration wrapper (adapted from crypto-alerts) ### references/ - **usage-examples.md**: Detailed examples, configurations, and integration patterns - **config-example.json**: Sample configuration file with all available options For detailed examples and integration patterns, see `references/usage-examples.md`.

path: references/config-example.json
content: {
  "discord_channel_id": "1483536881814868071",
  "discord_channel_name": "crypto",
  "guild_id": "1477442892879626260",
  "exchange": "coinbase",
  "risk_tolerance": "aggressive",
  "monthly_target": 1000,
  "watchlist": [
    "BTC", "ETH", "SOL", "XRP", "DOGE", "AVAX", "LINK", "ADA",
    "DOT", "MATIC", "ATOM", "UNI", "NEAR", "APT", "SUI", "ARB",
    "OP", "INJ", "PEPE", "SHIB", "RENDER", "FET", "ONDO", "WIF"
  ],
  "alerts": {
    "funding_scan_interval_hours": 4,
    "risk_radar_interval_hours": 12,
    "momentum_threshold_pct": 5,
    "price_check_interval_hours": 2
  },
  "strategy": {
    "funding_arb": {
      "enabled": true,
      "min_rate": -0.0005,
      "max_leverage": 5,
      "notes": "Aggressive: collect funding while holding direction"
    },
    "momentum": {
      "enabled": true,
      "entry_on_dip_pct": -8,
      "entry_on_breakout_pct": 5,
      "stop_loss_pct": 10,
      "take_profit_pct": 15
    },
    "risk_regime": {
      "enabled": true,
      "buy_on_green_after_red": true,
      "reduce_on_red": true
    }
  }
}


path: references/usage-examples.md
content: # Crypto Portfolio Tracker - Usage Examples

## Configuration File Format

Create a `portfolio-config.json` file with your portfolio settings:

```json
{
  "wallets": {
    "Main Wallet": {
      "address": [REDACTED_SECRET],
      "added_date": "2026-03-19"
    }
  },
  "watchlist": ["BTC", "ETH", "SOL", "DOGE", "XRP", "ADA", "AVAX"],
  "cost_basis": {
    "BTC": 45000,
    "ETH": 2800,
    "SOL": 85,
    "DOGE": 0.08
  },
  "target_allocation": {
    "BTC": 40,
    "ETH": 30,
    "SOL": 15,
    "Other": 15
  }
}
```

## Example Commands

### 1. Analyze Portfolio Holdings

```bash
# From JSON string
python3 scripts/portfolio-analyzer.py portfolio --holdings '{"BTC": 1.5, "ETH": 10.2, "SOL": 50}'

# From file
python3 scripts/portfolio-analyzer.py portfolio --holdings ./my-holdings.json
```

### 2. Market Overview

```bash
# Text format
python3 scripts/portfolio-analyzer.py market

# JSON format for automation
python3 scripts/portfolio-analyzer.py market --format json
```

### 3. Add Wallet for Tracking

```bash
python3 scripts/portfolio-analyzer.py wallet 0x742d35Cc6634C0532925a3b844Bc9e7595f0eB1E --name "Main Wallet"
```

## Sample Holdings File Format

Create `holdings.json`:

```json
{
  "BTC": 1.25,
  "ETH": 8.5,
  "SOL": 75,
  "DOGE": 10000,
  "XRP": 2500,
  "ADA": 1000
}
```

## Expected Outputs

### Portfolio Report (Text Format)

```
📊 CRYPTO PORTFOLIO REPORT
==================================================
Generated: 2026-03-19 15:30:45

💰 Total Value: $125,750.50
🟢 Total P&L: $15,750.50 (+14.32%)

📈 POSITION BREAKDOWN:
--------------------------------------------------
BTC:
  💵 Value: $89,237.50 (71.0% of portfolio)
  🟢 P&L: $8,987.50 (+11.20%)
  🔴 24h: -3.92%

ETH:
  💵 Value: $18,642.38 (14.8% of portfolio)
  🟢 P&L: $4,842.38 (+35.10%)
  🔴 24h: -5.84%

SOL:
  💵 Value: $7,177.50 (5.7% of portfolio)
  🔴 P&L: -$1,452.50 (-16.84%)
  🔴 24h: -4.21%
```

### Market Overview

```
🌍 CRYPTO MARKET OVERVIEW
==============================
Sentiment: Bearish
Avg 24h Change: -4.52%

Top Coins:
  🔴 BTC: $71,390.00 (-3.92%)
  🔴 ETH: $2,193.28 (-5.84%)
  🔴 SOL: $95.70 (-4.21%)
  🔴 DOGE: $0.094 (-5.99%)
```

## Integration Examples

### Use in OpenClaw Skills

```javascript
// In another skill or automation
const { exec } = require('child_process');

// Get portfolio value
exec('python3 ~/.openclaw/workspace/skills/crypto-portfolio-tracker/scripts/portfolio-analyzer.py portfolio --holdings ./holdings.json --format json', 
  (error, stdout, stderr) => {
    if (!error) {
      const portfolio = JSON.parse(stdout);
      console.log(`Portfolio worth: $${portfolio.total_value.toLocaleString()}`);
    }
  });
```

### Discord Bot Integration

```javascript
// Post daily portfolio updates to Discord
const portfolioData = await runPortfolioAnalyzer();
const message = `📊 **Daily Portfolio Update**
💰 Total: $${portfolioData.total_value.toLocaleString()}
${portfolioData.total_pnl >= 0 ? '🟢' : '🔴'} P&L: $${portfolioData.total_pnl.toLocaleString()} (${portfolioData.total_pnl_pct.toFixed(2)}%)`;

await sendToDiscord(message);
```

## Cost Basis Setup

Update your cost basis in the config file as you make purchases:

```json
{
  "cost_basis": {
    "BTC": 45000,    // Average buy price
    "ETH": 2800,     // Average buy price
    "SOL": 85,       // Average buy price
    "DOGE": 0.08     // Average buy price
  }
}
```

This enables accurate P&L calculation based on your actual entry prices.

## Automation Ideas

1. **Daily Portfolio Reports**: Set up a cron job to email you daily portfolio summaries
2. **Price Alerts**: Monitor when your portfolio hits certain value thresholds
3. **Rebalancing Alerts**: Get notified when allocations drift from targets
4. **Tax Reporting**: Export P&L data for tax preparation
5. **Performance Tracking**: Log daily portfolio values to track performance over time

path: scripts/portfolio-analyzer.py
content: #!/usr/bin/env python3
"""
Crypto Portfolio Analyzer
Tracks multiple wallets, calculates P&L, and generates portfolio reports
"""

import json
import sys
import os
import requests
from datetime import datetime, timedelta
from typing import Dict, List, Optional

class PortfolioTracker:
    def __init__(self, config_file: str = None):
        self.config = self.load_config(config_file)
        self.wallets = self.config.get('wallets', {})
        self.watchlist = self.config.get('watchlist', ['BTC', 'ETH', 'SOL'])
        self.cost_basis = self.config.get('cost_basis', {})
        
    def load_config(self, config_file: str) -> Dict:
        """Load configuration from JSON file"""
        if not config_file:
            # Look for config in current directory or skill directory
            possible_paths = [
                './portfolio-config.json',
                '../references/config-example.json',
                os.path.expanduser('~/.openclaw/workspace/crypto-portfolio-config.json')
            ]
            for path in possible_paths:
                if os.path.exists(path):
                    config_file = path
                    break
        
        if config_file and os.path.exists(config_file):
            with open(config_file, 'r') as f:
                return json.load(f)
        
        # Default config
        return {
            'wallets': {},
            'watchlist': ['BTC', 'ETH', 'SOL', 'DOGE', 'XRP'],
            'cost_basis': {},
            'target_allocation': {}
        }
    
    def get_prices(self, coins: List[str] = None) -> Dict[str, float]:
        """Get current prices from CoinGecko"""
        if not coins:
            coins = self.watchlist
            
        # CoinGecko ID mapping
        coin_ids = {
            'BTC': 'bitcoin', 'ETH': 'ethereum', 'SOL': 'solana', 
            'DOGE': 'dogecoin', 'XRP': 'ripple', 'ADA': 'cardano',
            'AVAX': 'avalanche-2', 'LINK': 'chainlink', 'DOT': 'polkadot',
            'UNI': 'uniswap', 'NEAR': 'near', 'ATOM': 'cosmos'
        }
        
        ids = [coin_ids.get(coin, coin.lower()) for coin in coins if coin in coin_ids]
        
        if not ids:
            return {}
            
        try:
            url = f"https://api.coingecko.com/api/v3/simple/price?ids={','.join(ids)}&vs_currencies=usd&include_24hr_change=true&include_market_cap=true"
            response = requests.get(url, timeout=10)
            data = response.json()
            
            # Map back to symbols
            prices = {}
            for coin in coins:
                coin_id = coin_ids.get(coin)
                if coin_id and coin_id in data:
                    prices[coin] = {
                        'price': data[coin_id]['usd'],
                        'change_24h': data[coin_id].get('usd_24h_change', 0),
                        'market_cap': data[coin_id].get('usd_market_cap', 0)
                    }
            return prices
        except Exception as e:
            print(f"Error fetching prices: {e}", file=sys.stderr)
            return {}
    
    def calculate_portfolio_value(self, holdings: Dict[str, float]) -> Dict:
        """Calculate total portfolio value and breakdown"""
        if not holdings:
            return {'total_value': 0, 'breakdown': {}, 'total_pnl': 0, 'total_pnl_pct': 0}
            
        coins = list(holdings.keys())
        prices = self.get_prices(coins)
        
        breakdown = {}
        total_value = 0
        total_cost = 0
        
        for coin, amount in holdings.items():
            if coin in prices:
                current_price = prices[coin]['price']
                position_value = amount * current_price
                cost_basis = self.cost_basis.get(coin, current_price) * amount
                
                breakdown[coin] = {
                    'amount': amount,
                    'price': current_price,
                    'value': position_value,
                    'cost_basis': cost_basis,
                    'pnl': position_value - cost_basis,
                    'pnl_pct': ((position_value - cost_basis) / cost_basis * 100) if cost_basis > 0 else 0,
                    'change_24h': prices[coin].get('change_24h', 0),
                    'allocation_pct': 0  # Will calculate after total
                }
                
                total_value += position_value
                total_cost += cost_basis
        
        # Calculate allocations
        for coin in breakdown:
            breakdown[coin]['allocation_pct'] = (breakdown[coin]['value'] / total_value * 100) if total_value > 0 else 0
        
        return {
            'total_value': total_value,
            'total_cost': total_cost,
            'total_pnl': total_value - total_cost,
            'total_pnl_pct': ((total_value - total_cost) / total_cost * 100) if total_cost > 0 else 0,
            'breakdown': breakdown,
            'timestamp': datetime.now().isoformat()
        }
    
    def generate_report(self, portfolio_data: Dict, format: str = 'text') -> str:
        """Generate portfolio report"""
        if format == 'json':
            return json.dumps(portfolio_data, indent=2, default=str)
        
        # Text format
        lines = []
        lines.append("📊 CRYPTO PORTFOLIO REPORT")
        lines.append("=" * 50)
        lines.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        lines.append("")
        
        # Summary
        total_value = portfolio_data['total_value']
        total_pnl = portfolio_data['total_pnl']
        total_pnl_pct = portfolio_data['total_pnl_pct']
        
        pnl_emoji = "🟢" if total_pnl >= 0 else "🔴"
        lines.append(f"💰 Total Value: ${total_value:,.2f}")
        lines.append(f"{pnl_emoji} Total P&L: ${total_pnl:,.2f} ({total_pnl_pct:+.2f}%)")
        lines.append("")
        
        # Breakdown
        lines.append("📈 POSITION BREAKDOWN:")
        lines.append("-" * 50)
        
        breakdown = portfolio_data['breakdown']
        
        # Sort by value (largest first)
        sorted_positions = sorted(breakdown.items(), key=lambda x: x[1]['value'], reverse=True)
        
        for coin, data in sorted_positions:
            value = data['value']
            pnl = data['pnl']
            pnl_pct = data['pnl_pct']
            allocation = data['allocation_pct']
            change_24h = data['change_24h']
            
            pnl_emoji = "🟢" if pnl >= 0 else "🔴"
            change_emoji = "🟢" if change_24h >= 0 else "🔴"
            
            lines.append(f"{coin}:")
            lines.append(f"  💵 Value: ${value:,.2f} ({allocation:.1f}% of portfolio)")
            lines.append(f"  {pnl_emoji} P&L: ${pnl:,.2f} ({pnl_pct:+.2f}%)")
            lines.append(f"  {change_emoji} 24h: {change_24h:+.2f}%")
            lines.append("")
        
        return "\n".join(lines)
    
    def track_wallet(self, address: str, name: str = None):
        """Add wallet to tracking (placeholder - would integrate with blockchain APIs)"""
        if not name:
            name = f"Wallet {address[:8]}..."
        
        self.wallets[name] = {
            'address': address,
            'added_date': datetime.now().isoformat()
        }
        print(f"✅ Added wallet: {name} ({address})")
    
    def get_market_overview(self) -> Dict:
        """Get market overview for watchlist"""
        prices = self.get_prices(self.watchlist)
        
        if not prices:
            return {'error': 'Failed to fetch market data'}
        
        # Calculate market metrics
        total_mcap = sum(data.get('market_cap', 0) for data in prices.values())
        avg_change = sum(data.get('change_24h', 0) for data in prices.values()) / len(prices)
        
        return {
            'timestamp': datetime.now().isoformat(),
            'total_market_cap': total_mcap,
            'average_24h_change': avg_change,
            'coins': prices,
            'market_sentiment': 'Bullish' if avg_change > 2 else 'Bearish' if avg_change < -2 else 'Neutral'
        }

def main():
    import argparse
    
    parser = argparse.ArgumentParser(description='Crypto Portfolio Tracker')
    parser.add_argument('--config', '-c', help='Config file path')
    parser.add_argument('--format', '-f', choices=['text', 'json'], default='text', help='Output format')
    
    subparsers = parser.add_subparsers(dest='command', help='Commands')
    
    # Portfolio command
    portfolio_parser = subparsers.add_parser('portfolio', help='Analyze portfolio')
    portfolio_parser.add_argument('--holdings', help='Holdings as JSON string or file')
    
    # Market command
    market_parser = subparsers.add_parser('market', help='Market overview')
    
    # Wallet command
    wallet_parser = subparsers.add_parser('wallet', help='Add wallet to track')
    wallet_parser.add_argument('address', help='Wallet address')
    wallet_parser.add_argument('--name', help='Wallet name')
    
    args = parser.parse_args()
    
    tracker = PortfolioTracker(args.config)
    
    if args.command == 'portfolio':
        if args.holdings:
            try:
                if args.holdings.startswith('{'):
                    holdings = json.loads(args.holdings)
                else:
                    with open(args.holdings, 'r') as f:
                        holdings = json.load(f)
                
                portfolio_data = tracker.calculate_portfolio_value(holdings)
                print(tracker.generate_report(portfolio_data, args.format))
                
            except Exception as e:
                print(f"Error: {e}", file=sys.stderr)
                sys.exit(1)
        else:
            print("Error: --holdings required for portfolio command", file=sys.stderr)
            sys.exit(1)
            
    elif args.command == 'market':
        market_data = tracker.get_market_overview()
        if args.format == 'json':
            print(json.dumps(market_data, indent=2, default=str))
        else:
            print("🌍 CRYPTO MARKET OVERVIEW")
            print("=" * 30)
            print(f"Sentiment: {market_data.get('market_sentiment', 'Unknown')}")
            print(f"Avg 24h Change: {market_data.get('average_24h_change', 0):+.2f}%")
            print("\nTop Coins:")
            for coin, data in market_data.get('coins', {}).items():
                emoji = "🟢" if data.get('change_24h', 0) >= 0 else "🔴"
                print(f"  {emoji} {coin}: ${data['price']:,.2f} ({data.get('change_24h', 0):+.2f}%)")
                
    elif args.command == 'wallet':
        tracker.track_wallet(args.address, args.name)
        
    else:
        parser.print_help()

if __name__ == '__main__':
    main()

path: scripts/portfolio-tracker.js
content: #!/usr/bin/env node
/**
 * Crypto Opportunity Scanner (US-friendly)
 * Uses CoinGecko (free, no key) + Coinbase public API
 * Outputs structured alerts for Discord #crypto channel
 */

const https = require('https');
const fs = require('fs');
const path = require('path');

const CONFIG = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'config.json'), 'utf-8'));

// ─── Helpers ───────────────────────────────────────────────

function httpsGet(url) {
  return new Promise((resolve) => {
    const req = https.get(url, {
      timeout: 15000,
      headers: { 'User-Agent': 'OpenClaw-CryptoScanner/1.0', 'Accept': 'application/json' }
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try { resolve(JSON.parse(data)); }
        catch { resolve(null); }
      });
    });
    req.on('error', () => resolve(null));
    req.on('timeout', () => { req.destroy(); resolve(null); });
  });
}

// ─── CoinGecko Market Data ─────────────────────────────────

const COINGECKO_IDS = {
  BTC: 'bitcoin', ETH: 'ethereum', SOL: 'solana', XRP: 'ripple',
  DOGE: 'dogecoin', AVAX: 'avalanche-2', LINK: 'chainlink', ADA: 'cardano',
  DOT: 'polkadot', MATIC: 'matic-network', ATOM: 'cosmos', UNI: 'uniswap',
  NEAR: 'near', APT: 'aptos', SUI: 'sui', ARB: 'arbitrum',
  OP: 'optimism', INJ: 'injective-protocol', PEPE: 'pepe', SHIB: 'shiba-inu',
  RENDER: 'render-token', FET: 'fetch-ai', ONDO: 'ondo-finance', WIF: 'dogwifcoin'
};

async function getMarketData() {
  const ids = CONFIG.watchlist.map(c => COINGECKO_IDS[c]).filter(Boolean).join(',');
  const url = `https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=${ids}&order=market_cap_desc&per_page=50&sparkline=false&price_change_percentage=1h,24h,7d`;
  const data = await httpsGet(url);
  if (!data || !Array.isArray(data)) return [];
  return data;
}

// ─── Fear & Greed ──────────────────────────────────────────

async function getFearGreed() {
  const data = await httpsGet('https://api.alternative.me/fng/?limit=1');
  if (!data || !data.data || !data.data[0]) return null;
  const fg = data.data[0];
  const v = parseInt(fg.value);
  return {
    value: v,
    label: fg.value_classification,
    emoji: v <= 25 ? '😱' : v <= 45 ? '😰' : v <= 55 ? '😐' : v <= 75 ? '😊' : '🤑'
  };
}

// ─── Coinbase BTC Price (backup) ───────────────────────────

async function getCoinbasePrice(pair = 'BTC-USD') {
  const data = await httpsGet(`https://api.coinbase.com/v2/prices/${pair}/spot`);
  if (!data || !data.data) return null;
  return parseFloat(data.data.amount);
}

// ─── Analysis Engine ───────────────────────────────────────

function analyzeOpportunities(markets, fg) {
  const opps = [];

  for (const coin of markets) {
    const symbol = Object.keys(COINGECKO_IDS).find(k => COINGECKO_IDS[k] === coin.id) || coin.symbol.toUpperCase();
    const change24h = coin.price_change_percentage_24h || 0;
    const change7d = coin.price_change_percentage_7d_in_currency || 0;
    const change1h = coin.price_change_percentage_1h_in_currency || 0;
    const price = coin.current_price;
    const volume = coin.total_volume;
    const mcap = coin.market_cap;
    const ath = coin.ath;
    const athDrop = ath ? ((price - ath) / ath * 100) : 0;

    // ─── DIP BUY: Down big in 24h, high volume ───
    if (change24h <= -8 && volume > 50000000) {
      opps.push({
        type: 'DIP_BUY', coin: symbol, price, emoji: '🟢',
        action: 'BUY THE DIP',
        reason: `Down ${change24h.toFixed(1)}% in 24h with $${(volume/1e6).toFixed(0)}M volume — oversold bounce setup`,
        risk: 'HIGH', potential: '10-20% recovery bounce',
        score: Math.abs(change24h) * 1.5
      });
    }

    // ─── BREAKOUT: Up big with momentum ───
    if (change24h >= 8 && change1h >= 1 && volume > 50000000) {
      opps.push({
        type: 'BREAKOUT', coin: symbol, price, emoji: '🚀',
        action: 'RIDE BREAKOUT',
        reason: `Up ${change24h.toFixed(1)}% and still pushing (+${change1h.toFixed(1)}% last hour)`,
        risk: 'HIGH', potential: 'Continuation to next resistance',
        score: change24h * 1.2
      });
    }

    // ─── REVERSAL SETUP: Down 7d but bouncing today ───
    if (change7d <= -15 && change24h >= 3) {
      opps.push({
        type: 'REVERSAL', coin: symbol, price, emoji: '🔄',
        action: 'REVERSAL PLAY',
        reason: `Down ${change7d.toFixed(1)}% this week but bouncing +${change24h.toFixed(1)}% today — possible trend reversal`,
        risk: 'HIGH', potential: '15-30% recovery if reversal confirms',
        score: Math.abs(change7d) * 0.8
      });
    }

    // ─── VALUE BUY: Way below ATH with signs of life ───
    if (athDrop <= -70 && change24h >= 2 && mcap > 500000000) {
      opps.push({
        type: 'VALUE', coin: symbol, price, emoji: '💎',
        action: 'VALUE ACCUMULATE',
        reason: `${athDrop.toFixed(0)}% below ATH ($${ath.toLocaleString()}) — deep value with bullish momentum today`,
        risk: 'MEDIUM', potential: 'Multi-month recovery play',
        score: Math.abs(athDrop) * 0.5
      });
    }

    // ─── DUMP WARNING: Crashing hard ───
    if (change24h <= -12) {
      opps.push({
        type: 'WARNING', coin: symbol, price, emoji: '🚨',
        action: 'CAUTION / WAIT',
        reason: `Crashing ${change24h.toFixed(1)}% — wait for stabilization before entry`,
        risk: 'EXTREME', potential: 'Could go lower. Patience.',
        score: Math.abs(change24h) * 2
      });
    }
  }

  // Extreme Fear = contrarian buy signal
  if (fg && fg.value <= 20) {
    opps.push({
      type: 'SENTIMENT', coin: 'MARKET', price: 0, emoji: '😱',
      action: 'EXTREME FEAR — BUY ZONE',
      reason: `Fear & Greed at ${fg.value} (${fg.label}). Historically, extreme fear = best buying opportunities`,
      risk: 'MEDIUM', potential: 'Broad market mean reversion',
      score: (25 - fg.value) * 3
    });
  }

  opps.sort((a, b) => b.score - a.score);
  return opps;
}

// ─── Format Big Movers ────────────────────────────────────

function getBigMovers(markets) {
  return markets
    .filter(c => Math.abs(c.price_change_percentage_24h || 0) >= 5)
    .sort((a, b) => Math.abs(b.price_change_percentage_24h) - Math.abs(a.price_change_percentage_24h))
    .slice(0, 8)
    .map(c => {
      const symbol = Object.keys(COINGECKO_IDS).find(k => COINGECKO_IDS[k] === c.id) || c.symbol.toUpperCase();
      const change = c.price_change_percentage_24h;
      const emoji = change >= 0 ? '🟢' : '🔴';
      return { symbol, price: c.current_price, change: change.toFixed(2), volume: (c.total_volume / 1e6).toFixed(0), emoji };
    });
}

// ─── Main Scanner ──────────────────────────────────────────

async function runScan() {
  const args = process.argv.slice(2);
  const jsonOutput = args.includes('--json');

  // Fetch all data
  const [markets, fg, btcCoinbase] = await Promise.all([
    getMarketData(),
    getFearGreed(),
    getCoinbasePrice('BTC-USD')
  ]);

  if (!markets || markets.length === 0) {
    console.log('**⚠️ Crypto Scanner:** API unavailable. Will retry next scan.');
    return;
  }

  const btc = markets.find(m => m.id === 'bitcoin');
  const eth = markets.find(m => m.id === 'ethereum');
  const opportunities = analyzeOpportunities(markets, fg);
  const bigMovers = getBigMovers(markets);

  if (jsonOutput) {
    console.log(JSON.stringify({ timestamp: new Date().toISOString(), btc, eth, fg, opportunities, bigMovers }, null, 2));
    return;
  }

  // Format for Discord
  const lines = [];
  const now = new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles', dateStyle: 'short', timeStyle: 'short' });

  lines.push(`**📊 Crypto Scanner** — ${now}`);
  lines.push('');

  // Market header
  if (btc) {
    const btcEmoji = btc.price_change_percentage_24h >= 0 ? '🟢' : '🔴';
    lines.push(`**BTC** ${btcEmoji} $${btc.current_price.toLocaleString()} (${btc.price_change_percentage_24h.toFixed(2)}%)`);
  }
  if (eth) {
    const ethEmoji = eth.price_change_percentage_24h >= 0 ? '🟢' : '🔴';
    lines.push(`**ETH** ${ethEmoji} $${eth.current_price.toLocaleString()} (${eth.price_change_percentage_24h.toFixed(2)}%)`);
  }
  if (fg) {
    lines.push(`**Fear & Greed:** ${fg.emoji} ${fg.value} — ${fg.label}`);
  }
  lines.push('');

  // Big movers
  if (bigMovers.length > 0) {
    lines.push('**⚡ Big Movers (24h):**');
    for (const m of bigMovers) {
      lines.push(`${m.emoji} **${m.symbol}** ${m.change}% | $${m.price.toLocaleString()} | Vol: $${m.volume}M`);
    }
    lines.push('');
  }

  // Opportunities
  if (opportunities.length > 0) {
    lines.push('**🎯 Opportunities:**');
    for (const o of opportunities.slice(0, 6)) {
      const riskTag = o.risk === 'EXTREME' ? '⛔' : o.risk === 'HIGH' ? '🔥' : o.risk === 'MEDIUM' ? '⚠️' : '✅';
      lines.push(`${o.emoji} **${o.coin}** — ${o.action}`);
      lines.push(`   ${o.reason}`);
      lines.push(`   ${riskTag} Risk: ${o.risk} | Target: ${o.potential}`);
    }
    lines.push('');
  }

  if (opportunities.length === 0 && bigMovers.length === 0) {
    lines.push('**⚪ Markets quiet.** No strong signals right now. Watching...');
    lines.push('');
  }

  lines.push('*Not financial advice. DYOR. Use stop-losses. 🐾*');

  console.log(lines.join('\n'));
}

runScan().catch(console.error);


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)