@clawhub-iamcooper2026-6b199451c4
Safe cryptocurrency portfolio tracking and P&L analysis. Monitors portfolio value, calculates profit/loss with custom cost basis, tracks live prices via Coin...
---
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_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`.
FILE:references/config-example.json
{
"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
}
}
}
FILE:references/usage-examples.md
# Crypto Portfolio Tracker - Usage Examples
## Configuration File Format
Create a `portfolio-config.json` file with your portfolio settings:
```json
{
"wallets": {
"Main Wallet": {
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0eB1E",
"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()
'🔴' 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
FILE:scripts/portfolio-analyzer.py
#!/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: ,.2f")
lines.append(f"{pnl_emoji} Total P&L: ,.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: ,.2f ({allocation:.1f}% of portfolio)")
lines.append(f" {pnl_emoji} P&L: ,.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}: ,.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()
FILE:scripts/portfolio-tracker.js
#!/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.volumeM`);
}
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);