commit 1e3b5bcace6b667846aeba04792f493de4aee740 Author: Zhongwei Li Date: Sat Nov 29 18:53:19 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..ee38cfd --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,16 @@ +{ + "name": "market-movers-scanner", + "description": "Scan for top market movers - gainers, losers, volume spikes, and unusual activity", + "version": "1.0.0", + "author": { + "name": "Intent Solutions IO", + "email": "jeremy@intentsolutions.ai", + "url": "https://intentsolutions.ai" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..78a873b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# market-movers-scanner + +Scan for top market movers - gainers, losers, volume spikes, and unusual activity diff --git a/commands/scan-movers.md b/commands/scan-movers.md new file mode 100644 index 0000000..3136dcb --- /dev/null +++ b/commands/scan-movers.md @@ -0,0 +1,900 @@ +--- +description: Scan for top market movers across crypto, stocks, and forex with real-time updates +shortcut: sm +--- + +# Scan Market Movers + +Comprehensive market scanner identifying top gainers, losers, volume leaders, and unusual activity across multiple asset classes. + +## Usage + +When the user wants to scan for market movers, implement a real-time scanning system with these capabilities: + +### Scan Parameters +- **Markets**: crypto, stocks, forex, all +- **Timeframe**: 1h, 4h, 24h, 7d, 30d +- **Categories**: gainers, losers, volume, volatility, unusual +- **Limit**: Top N results (default: 20) +- **Filters**: Market cap, volume thresholds, price ranges +- **Sort**: By percentage, volume, market cap, volatility + +## Implementation + +### 1. Market Scanner Engine + +```javascript +class MarketMoversScanner { + constructor() { + this.dataSources = { + crypto: { + coingecko: 'https://api.coingecko.com/api/v3', + coinmarketcap: process.env.CMC_API_KEY, + messari: 'https://data.messari.io/api/v1', + binance: 'https://api.binance.com/api/v3' + }, + stocks: { + yahoo: 'https://query1.finance.yahoo.com/v1', + alphavantage: process.env.ALPHA_VANTAGE_API, + iex: 'https://api.iextrading.com/1.0', + polygon: process.env.POLYGON_API_KEY + }, + forex: { + oanda: process.env.OANDA_API_KEY, + fixer: process.env.FIXER_API_KEY, + currencylayer: process.env.CURRENCY_LAYER_API + } + }; + + this.scanCache = new Map(); + this.updateInterval = 60000; // 1 minute + this.lastUpdate = {}; + } + + async scanMarkets(params) { + const { + markets = 'all', + timeframe = '24h', + categories = ['gainers', 'losers', 'volume'], + limit = 20, + filters = {}, + sortBy = 'percentage' + } = params; + + // Initialize scan results + const results = { + timestamp: Date.now(), + timeframe, + markets: markets === 'all' ? ['crypto', 'stocks', 'forex'] : [markets], + data: { + gainers: [], + losers: [], + volumeLeaders: [], + volatilityLeaders: [], + unusual: [], + breakouts: [], + newHighs: [], + newLows: [] + }, + statistics: {}, + alerts: [] + }; + + // Scan each market + for (const market of results.markets) { + const marketData = await this.scanMarket(market, timeframe, filters); + results.data = this.mergeMarketData(results.data, marketData); + } + + // Process categories + for (const category of categories) { + results.data[category] = await this.processCategory( + category, + results.data, + limit, + sortBy + ); + } + + // Calculate statistics + results.statistics = this.calculateStatistics(results.data); + + // Identify alerts + results.alerts = this.identifyAlerts(results.data); + + return results; + } + + async scanMarket(market, timeframe, filters) { + const cacheKey = `${market}_${timeframe}`; + + // Check cache + if (this.scanCache.has(cacheKey)) { + const cached = this.scanCache.get(cacheKey); + if (Date.now() - cached.timestamp < this.updateInterval) { + return cached.data; + } + } + + // Fetch fresh data + let marketData; + switch (market) { + case 'crypto': + marketData = await this.scanCrypto(timeframe, filters); + break; + case 'stocks': + marketData = await this.scanStocks(timeframe, filters); + break; + case 'forex': + marketData = await this.scanForex(timeframe, filters); + break; + default: + throw new Error(`Unsupported market: ${market}`); + } + + // Update cache + this.scanCache.set(cacheKey, { + timestamp: Date.now(), + data: marketData + }); + + return marketData; + } + + async scanCrypto(timeframe, filters) { + const assets = []; + + try { + // Fetch from CoinGecko + const cgData = await this.fetchCoinGeckoData(timeframe); + + for (const coin of cgData) { + if (this.applyFilters(coin, filters)) { + assets.push({ + symbol: coin.symbol.toUpperCase(), + name: coin.name, + market: 'crypto', + price: coin.current_price, + change: this.getChangeForTimeframe(coin, timeframe), + volume: coin.total_volume, + marketCap: coin.market_cap, + high24h: coin.high_24h, + low24h: coin.low_24h, + ath: coin.ath, + athDate: coin.ath_date, + circulatingSupply: coin.circulating_supply, + rank: coin.market_cap_rank, + sparkline: coin.sparkline_in_7d?.price || [], + metrics: { + volatility: this.calculateVolatility(coin), + momentum: this.calculateMomentum(coin), + relativeVolume: coin.total_volume / coin.market_cap, + priceScore: this.calculatePriceScore(coin) + } + }); + } + } + + // Fetch from Binance for real-time data + const binanceData = await this.fetchBinanceData(); + this.enrichWithBinanceData(assets, binanceData); + + } catch (error) { + console.error('Error scanning crypto:', error); + } + + return assets; + } + + async fetchCoinGeckoData(timeframe) { + const periods = { + '1h': '1h', + '24h': '24h', + '7d': '7d', + '30d': '30d' + }; + + const response = await fetch( + `${this.dataSources.crypto.coingecko}/coins/markets?` + + `vs_currency=usd&order=market_cap_desc&per_page=500&` + + `price_change_percentage=${periods[timeframe] || '24h'},7d,30d&` + + `sparkline=true` + ); + + return response.json(); + } + + async fetchBinanceData() { + const response = await fetch( + `${this.dataSources.crypto.binance}/ticker/24hr` + ); + const data = await response.json(); + + const processed = {}; + for (const ticker of data) { + if (ticker.symbol.endsWith('USDT')) { + const symbol = ticker.symbol.replace('USDT', ''); + processed[symbol] = { + price: parseFloat(ticker.lastPrice), + change24h: parseFloat(ticker.priceChangePercent), + volume: parseFloat(ticker.volume), + quoteVolume: parseFloat(ticker.quoteVolume), + count: parseInt(ticker.count), + weightedAvgPrice: parseFloat(ticker.weightedAvgPrice) + }; + } + } + + return processed; + } + + getChangeForTimeframe(coin, timeframe) { + const changeMap = { + '1h': coin.price_change_percentage_1h_in_currency, + '24h': coin.price_change_percentage_24h, + '7d': coin.price_change_percentage_7d_in_currency, + '30d': coin.price_change_percentage_30d_in_currency + }; + + return changeMap[timeframe] || coin.price_change_percentage_24h || 0; + } + + calculateVolatility(asset) { + if (!asset.sparkline_in_7d?.price || asset.sparkline_in_7d.price.length < 2) { + return 0; + } + + const prices = asset.sparkline_in_7d.price; + const returns = []; + + for (let i = 1; i < prices.length; i++) { + returns.push((prices[i] - prices[i-1]) / prices[i-1]); + } + + const mean = returns.reduce((a, b) => a + b) / returns.length; + const variance = returns.reduce((sum, r) => sum + Math.pow(r - mean, 2), 0) / returns.length; + + return Math.sqrt(variance) * 100; // Percentage volatility + } + + calculateMomentum(asset) { + const weights = { + '24h': 0.4, + '7d': 0.3, + '30d': 0.3 + }; + + const momentum = + (asset.price_change_percentage_24h || 0) * weights['24h'] + + (asset.price_change_percentage_7d_in_currency || 0) * weights['7d'] + + (asset.price_change_percentage_30d_in_currency || 0) * weights['30d']; + + return momentum; + } + + calculatePriceScore(asset) { + // Score based on price position relative to range + const range = asset.high_24h - asset.low_24h; + if (range === 0) return 50; + + const position = (asset.current_price - asset.low_24h) / range; + return position * 100; + } + + async scanStocks(timeframe, filters) { + const assets = []; + + try { + // Fetch major indices components + const indices = ['SPY', 'QQQ', 'DIA']; // S&P 500, NASDAQ, Dow ETFs + const stockList = await this.fetchStockList(indices); + + for (const symbol of stockList) { + const stockData = await this.fetchStockData(symbol, timeframe); + + if (stockData && this.applyFilters(stockData, filters)) { + assets.push({ + symbol: stockData.symbol, + name: stockData.name, + market: 'stocks', + price: stockData.price, + change: stockData.changePercent, + volume: stockData.volume, + marketCap: stockData.marketCap, + high52w: stockData.week52High, + low52w: stockData.week52Low, + pe: stockData.peRatio, + eps: stockData.eps, + dividend: stockData.dividendYield, + beta: stockData.beta, + metrics: { + rsi: stockData.rsi, + volumeRatio: stockData.volume / stockData.avgVolume, + priceToHigh: stockData.price / stockData.week52High, + earningsGrowth: stockData.earningsGrowth + } + }); + } + } + } catch (error) { + console.error('Error scanning stocks:', error); + } + + return assets; + } + + async processCategory(category, data, limit, sortBy) { + let categoryData = []; + + switch (category) { + case 'gainers': + categoryData = this.findTopGainers(data, limit); + break; + case 'losers': + categoryData = this.findTopLosers(data, limit); + break; + case 'volume': + categoryData = this.findVolumeLeaders(data, limit); + break; + case 'volatility': + categoryData = this.findVolatilityLeaders(data, limit); + break; + case 'unusual': + categoryData = this.findUnusualActivity(data, limit); + break; + case 'breakouts': + categoryData = this.findBreakouts(data, limit); + break; + case 'momentum': + categoryData = this.findMomentumPlays(data, limit); + break; + } + + return this.sortResults(categoryData, sortBy); + } + + findTopGainers(data, limit) { + const allAssets = [...data.gainers, ...data.losers, ...data.volumeLeaders]; + const uniqueAssets = this.removeDuplicates(allAssets); + + return uniqueAssets + .filter(asset => asset.change > 0) + .sort((a, b) => b.change - a.change) + .slice(0, limit) + .map(asset => ({ + ...asset, + category: 'GAINER', + signal: this.generateSignal(asset, 'GAINER') + })); + } + + findTopLosers(data, limit) { + const allAssets = [...data.gainers, ...data.losers, ...data.volumeLeaders]; + const uniqueAssets = this.removeDuplicates(allAssets); + + return uniqueAssets + .filter(asset => asset.change < 0) + .sort((a, b) => a.change - b.change) + .slice(0, limit) + .map(asset => ({ + ...asset, + category: 'LOSER', + signal: this.generateSignal(asset, 'LOSER') + })); + } + + findVolumeLeaders(data, limit) { + const allAssets = this.getAllAssets(data); + + return allAssets + .filter(asset => asset.metrics?.volumeRatio > 2) + .sort((a, b) => b.metrics.volumeRatio - a.metrics.volumeRatio) + .slice(0, limit) + .map(asset => ({ + ...asset, + category: 'VOLUME_LEADER', + volumeMultiple: asset.metrics.volumeRatio.toFixed(2) + 'x', + signal: this.generateSignal(asset, 'VOLUME') + })); + } + + findUnusualActivity(data, limit) { + const allAssets = this.getAllAssets(data); + const unusual = []; + + for (const asset of allAssets) { + const signals = []; + + // Check for unusual volume + if (asset.metrics?.volumeRatio > 5) { + signals.push('EXTREME_VOLUME'); + } + + // Check for large price movement + if (Math.abs(asset.change) > 20) { + signals.push('LARGE_MOVE'); + } + + // Check for volatility spike + if (asset.metrics?.volatility > 10) { + signals.push('HIGH_VOLATILITY'); + } + + // Check for new highs/lows + if (asset.price >= asset.high52w * 0.95) { + signals.push('NEAR_52W_HIGH'); + } + if (asset.price <= asset.low52w * 1.05) { + signals.push('NEAR_52W_LOW'); + } + + if (signals.length > 0) { + unusual.push({ + ...asset, + category: 'UNUSUAL', + signals, + unusualScore: signals.length * 25 + }); + } + } + + return unusual + .sort((a, b) => b.unusualScore - a.unusualScore) + .slice(0, limit); + } + + findBreakouts(data, limit) { + const allAssets = this.getAllAssets(data); + const breakouts = []; + + for (const asset of allAssets) { + const breakoutSignals = []; + + // Volume breakout + if (asset.metrics?.volumeRatio > 3 && asset.change > 5) { + breakoutSignals.push({ + type: 'VOLUME_BREAKOUT', + strength: 'HIGH' + }); + } + + // Price breakout (near 52-week high) + if (asset.high52w && asset.price > asset.high52w * 0.98) { + breakoutSignals.push({ + type: 'PRICE_BREAKOUT', + strength: 'VERY_HIGH', + target: asset.high52w * 1.1 + }); + } + + // Momentum breakout + if (asset.metrics?.momentum > 15) { + breakoutSignals.push({ + type: 'MOMENTUM_BREAKOUT', + strength: 'MEDIUM' + }); + } + + if (breakoutSignals.length > 0) { + breakouts.push({ + ...asset, + category: 'BREAKOUT', + breakoutSignals, + breakoutScore: this.calculateBreakoutScore(breakoutSignals) + }); + } + } + + return breakouts + .sort((a, b) => b.breakoutScore - a.breakoutScore) + .slice(0, limit); + } + + generateSignal(asset, category) { + const signals = { + strength: 'MEDIUM', + action: 'WATCH', + confidence: 50, + reasons: [] + }; + + // Analyze based on category + if (category === 'GAINER') { + if (asset.change > 20) { + signals.strength = 'STRONG'; + signals.action = 'MOMENTUM_PLAY'; + signals.confidence = 75; + signals.reasons.push('Strong upward momentum'); + } + if (asset.metrics?.volumeRatio > 3) { + signals.confidence += 10; + signals.reasons.push('High volume confirmation'); + } + } else if (category === 'LOSER') { + if (asset.change < -20) { + signals.strength = 'STRONG'; + signals.action = 'OVERSOLD_BOUNCE'; + signals.confidence = 60; + signals.reasons.push('Potential oversold bounce'); + } + } else if (category === 'VOLUME') { + signals.strength = 'HIGH'; + signals.action = 'INVESTIGATE'; + signals.confidence = 70; + signals.reasons.push('Unusual volume activity'); + } + + return signals; + } + + identifyAlerts(data) { + const alerts = []; + + // Market-wide alerts + const gainersCount = data.gainers?.filter(a => a.change > 10).length || 0; + const losersCount = data.losers?.filter(a => a.change < -10).length || 0; + + if (gainersCount > 50) { + alerts.push({ + type: 'MARKET_RALLY', + message: `Strong market rally detected: ${gainersCount} assets up >10%`, + severity: 'INFO' + }); + } + + if (losersCount > 50) { + alerts.push({ + type: 'MARKET_SELLOFF', + message: `Market selloff detected: ${losersCount} assets down >10%`, + severity: 'WARNING' + }); + } + + // Individual asset alerts + for (const category of Object.values(data)) { + if (!Array.isArray(category)) continue; + + for (const asset of category) { + if (asset.change > 50) { + alerts.push({ + type: 'EXTREME_GAIN', + symbol: asset.symbol, + message: `${asset.symbol} up ${asset.change.toFixed(2)}% - extreme movement`, + severity: 'HIGH' + }); + } + + if (asset.change < -30) { + alerts.push({ + type: 'EXTREME_LOSS', + symbol: asset.symbol, + message: `${asset.symbol} down ${Math.abs(asset.change).toFixed(2)}% - potential crash`, + severity: 'CRITICAL' + }); + } + + if (asset.metrics?.volumeRatio > 10) { + alerts.push({ + type: 'VOLUME_EXPLOSION', + symbol: asset.symbol, + message: `${asset.symbol} volume ${asset.metrics.volumeRatio.toFixed(1)}x average`, + severity: 'HIGH' + }); + } + } + } + + return alerts; + } +} +``` + +### 2. Display Interface + +```javascript +class MoversDisplay { + displayResults(results) { + const output = ` +╔════════════════════════════════════════════════════════════════╗ +║ MARKET MOVERS SCANNER ║ +╠════════════════════════════════════════════════════════════════╣ +║ Timeframe: ${results.timeframe.padEnd(48)} ║ +║ Markets: ${results.markets.join(', ').padEnd(48)} ║ +║ Last Update: ${new Date(results.timestamp).toLocaleString().padEnd(48)} ║ +╠════════════════════════════════════════════════════════════════╣ +║ TOP GAINERS ║ +╠════════════════════════════════════════════════════════════════╣ +${this.formatMovers(results.data.gainers, 'gain')} +╠════════════════════════════════════════════════════════════════╣ +║ TOP LOSERS ║ +╠════════════════════════════════════════════════════════════════╣ +${this.formatMovers(results.data.losers, 'loss')} +╠════════════════════════════════════════════════════════════════╣ +║ VOLUME LEADERS ║ +╠════════════════════════════════════════════════════════════════╣ +${this.formatVolumeLeaders(results.data.volumeLeaders)} +╠════════════════════════════════════════════════════════════════╣ +║ UNUSUAL ACTIVITY ║ +╠════════════════════════════════════════════════════════════════╣ +${this.formatUnusual(results.data.unusual)} +╠════════════════════════════════════════════════════════════════╣ +║ ALERTS ║ +╠════════════════════════════════════════════════════════════════╣ +${this.formatAlerts(results.alerts)} +╚════════════════════════════════════════════════════════════════╝ +`; + return output; + } + + formatMovers(movers, type) { + if (!movers || movers.length === 0) { + return '║ No significant movers found ║'; + } + + const lines = []; + for (const mover of movers.slice(0, 5)) { + const changeStr = type === 'gain' + ? `+${mover.change.toFixed(2)}%` + : `${mover.change.toFixed(2)}%`; + + const emoji = type === 'gain' ? '' : ''; + + lines.push( + `║ ${emoji} ${mover.symbol.padEnd(8)} ${changeStr.padEnd(10)} ` + + `$${this.formatPrice(mover.price).padEnd(12)} ${this.formatVolume(mover.volume).padEnd(12)} ║` + ); + } + + return lines.join('\n'); + } + + formatVolumeLeaders(leaders) { + if (!leaders || leaders.length === 0) { + return '║ No volume leaders found ║'; + } + + const lines = []; + for (const leader of leaders.slice(0, 5)) { + lines.push( + `║ ${leader.symbol.padEnd(8)} ${leader.volumeMultiple.padEnd(6)} ` + + `${this.formatChange(leader.change).padEnd(10)} Signal: ${leader.signal.action.padEnd(15)} ║` + ); + } + + return lines.join('\n'); + } + + formatUnusual(unusual) { + if (!unusual || unusual.length === 0) { + return '║ No unusual activity detected ║'; + } + + const lines = []; + for (const item of unusual.slice(0, 3)) { + const signals = item.signals.join(', '); + lines.push( + `║ ️ ${item.symbol.padEnd(8)} ${signals.padEnd(45)} ║` + ); + } + + return lines.join('\n'); + } + + formatAlerts(alerts) { + if (!alerts || alerts.length === 0) { + return '║ No alerts at this time ║'; + } + + const lines = []; + const severityEmoji = { + 'INFO': 'ℹ️', + 'WARNING': '️', + 'HIGH': '', + 'CRITICAL': '' + }; + + for (const alert of alerts.slice(0, 3)) { + const emoji = severityEmoji[alert.severity] || 'ℹ️'; + lines.push( + `║ ${emoji} ${alert.message.padEnd(55)} ║` + ); + } + + return lines.join('\n'); + } + + formatPrice(price) { + if (price > 10000) return price.toFixed(0); + if (price > 100) return price.toFixed(2); + if (price > 1) return price.toFixed(4); + return price.toFixed(8); + } + + formatVolume(volume) { + if (volume > 1e9) return `${(volume / 1e9).toFixed(2)}B`; + if (volume > 1e6) return `${(volume / 1e6).toFixed(2)}M`; + if (volume > 1e3) return `${(volume / 1e3).toFixed(2)}K`; + return volume.toFixed(0); + } + + formatChange(change) { + const formatted = change.toFixed(2); + if (change > 0) return `+${formatted}%`; + return `${formatted}%`; + } +} +``` + +### 3. Real-Time Updates + +```javascript +class RealTimeScanner { + constructor() { + this.scanner = new MarketMoversScanner(); + this.display = new MoversDisplay(); + this.updateInterval = 30000; // 30 seconds + this.isRunning = false; + } + + async start(params) { + this.isRunning = true; + console.log('Starting real-time market scanner...'); + + while (this.isRunning) { + try { + // Scan markets + const results = await this.scanner.scanMarkets(params); + + // Clear console and display + console.clear(); + console.log(this.display.displayResults(results)); + + // Check for critical alerts + this.checkCriticalAlerts(results.alerts); + + // Wait for next update + await this.sleep(this.updateInterval); + + } catch (error) { + console.error('Scanner error:', error); + await this.sleep(5000); // Retry after 5 seconds + } + } + } + + checkCriticalAlerts(alerts) { + const critical = alerts.filter(a => a.severity === 'CRITICAL' || a.severity === 'HIGH'); + + if (critical.length > 0) { + console.log('\n CRITICAL ALERTS:'); + for (const alert of critical) { + console.log(`- ${alert.message}`); + } + // Could trigger notifications here + } + } + + stop() { + this.isRunning = false; + console.log('Scanner stopped.'); + } + + sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} +``` + +### 4. Advanced Filtering + +```javascript +class ScannerFilters { + applyFilters(asset, filters) { + // Market cap filter + if (filters.minMarketCap && asset.marketCap < filters.minMarketCap) { + return false; + } + if (filters.maxMarketCap && asset.marketCap > filters.maxMarketCap) { + return false; + } + + // Volume filter + if (filters.minVolume && asset.volume < filters.minVolume) { + return false; + } + + // Price filter + if (filters.minPrice && asset.price < filters.minPrice) { + return false; + } + if (filters.maxPrice && asset.price > filters.maxPrice) { + return false; + } + + // Change filter + if (filters.minChange && asset.change < filters.minChange) { + return false; + } + if (filters.maxChange && asset.change > filters.maxChange) { + return false; + } + + // Custom filters + if (filters.excludeStablecoins && this.isStablecoin(asset.symbol)) { + return false; + } + + if (filters.onlyTop100 && asset.rank > 100) { + return false; + } + + return true; + } + + isStablecoin(symbol) { + const stablecoins = ['USDT', 'USDC', 'BUSD', 'DAI', 'TUSD', 'USDP', 'USDD']; + return stablecoins.includes(symbol.toUpperCase()); + } + + createSmartFilters(scanType) { + const filters = { + dayTrading: { + minVolume: 10000000, + minPrice: 0.01, + maxPrice: 100000, + minChange: 2, + excludeStablecoins: true + }, + swingTrading: { + minMarketCap: 100000000, + minVolume: 5000000, + minChange: 5, + onlyTop100: true + }, + pennyStocks: { + maxPrice: 5, + minVolume: 1000000, + minChange: 10 + }, + blueChips: { + minMarketCap: 10000000000, + minVolume: 100000000 + } + }; + + return filters[scanType] || {}; + } +} +``` + +## Error Handling + +```javascript +try { + const scanner = new RealTimeScanner(); + + await scanner.start({ + markets: 'all', + timeframe: '24h', + categories: ['gainers', 'losers', 'volume', 'unusual'], + limit: 20, + filters: { + minVolume: 1000000, + excludeStablecoins: true + }, + sortBy: 'percentage' + }); + + // Graceful shutdown + process.on('SIGINT', () => { + scanner.stop(); + process.exit(0); + }); + +} catch (error) { + console.error('Failed to start scanner:', error); + process.exit(1); +} +``` + +This command provides comprehensive market scanning with real-time updates, advanced filtering, and alert detection across multiple asset classes. \ No newline at end of file diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..07dd66e --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,85 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:jeremylongshore/claude-code-plugins-plus:plugins/crypto/market-movers-scanner", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "814f8034a2dccd664d5aa222ec5d2332a047eceb", + "treeHash": "a5164506e2680193c85244d6fea07667ca7673949ca47d82074344b297662ff9", + "generatedAt": "2025-11-28T10:18:33.223596Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "market-movers-scanner", + "description": "Scan for top market movers - gainers, losers, volume spikes, and unusual activity", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "e93a83e44812b0136c8d16d735637b7b1bd62528de7e29bbcab7a66afc7cb6c8" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "bda9b847b7bebba99374bf52f2d578ff52e8f3f4daeef6829fbdfe2a696cee9f" + }, + { + "path": "commands/scan-movers.md", + "sha256": "a4961527b08bcd4c2fe0103c0f233bef3ff0fdd79742aa36ce8f67e48cb15e54" + }, + { + "path": "skills/skill-adapter/references/examples.md", + "sha256": "922bbc3c4ebf38b76f515b5c1998ebde6bf902233e00e2c5a0e9176f975a7572" + }, + { + "path": "skills/skill-adapter/references/best-practices.md", + "sha256": "c8f32b3566252f50daacd346d7045a1060c718ef5cfb07c55a0f2dec5f1fb39e" + }, + { + "path": "skills/skill-adapter/references/README.md", + "sha256": "25b8ed650dc743ee9fa66a86b867cd2a10c53811d2237c4226080c4b6e98add9" + }, + { + "path": "skills/skill-adapter/scripts/helper-template.sh", + "sha256": "0881d5660a8a7045550d09ae0acc15642c24b70de6f08808120f47f86ccdf077" + }, + { + "path": "skills/skill-adapter/scripts/validation.sh", + "sha256": "92551a29a7f512d2036e4f1fb46c2a3dc6bff0f7dde4a9f699533e446db48502" + }, + { + "path": "skills/skill-adapter/scripts/README.md", + "sha256": "1d488cc92b8d86764ce598c4f70814f840c905310738e50d96b63da942aa298c" + }, + { + "path": "skills/skill-adapter/assets/test-data.json", + "sha256": "ac17dca3d6e253a5f39f2a2f1b388e5146043756b05d9ce7ac53a0042eee139d" + }, + { + "path": "skills/skill-adapter/assets/README.md", + "sha256": "d3c725146e5bc2372dba3e3cf2c957157ca66821f1a881626ebc608a3783da9e" + }, + { + "path": "skills/skill-adapter/assets/skill-schema.json", + "sha256": "f5639ba823a24c9ac4fb21444c0717b7aefde1a4993682897f5bf544f863c2cd" + }, + { + "path": "skills/skill-adapter/assets/config-template.json", + "sha256": "0c2ba33d2d3c5ccb266c0848fc43caa68a2aa6a80ff315d4b378352711f83e1c" + } + ], + "dirSha256": "a5164506e2680193c85244d6fea07667ca7673949ca47d82074344b297662ff9" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/skill-adapter/assets/README.md b/skills/skill-adapter/assets/README.md new file mode 100644 index 0000000..38f6135 --- /dev/null +++ b/skills/skill-adapter/assets/README.md @@ -0,0 +1,7 @@ +# Assets + +Bundled resources for market-movers-scanner skill + +- [ ] alert_templates/: Templates for alert notifications, including email, SMS, and push notifications. +- [ ] configuration_examples/: Example configuration files for different use cases and market conditions. +- [ ] sample_data/: Sample market data for testing and demonstration purposes. diff --git a/skills/skill-adapter/assets/config-template.json b/skills/skill-adapter/assets/config-template.json new file mode 100644 index 0000000..16f1712 --- /dev/null +++ b/skills/skill-adapter/assets/config-template.json @@ -0,0 +1,32 @@ +{ + "skill": { + "name": "skill-name", + "version": "1.0.0", + "enabled": true, + "settings": { + "verbose": false, + "autoActivate": true, + "toolRestrictions": true + } + }, + "triggers": { + "keywords": [ + "example-trigger-1", + "example-trigger-2" + ], + "patterns": [] + }, + "tools": { + "allowed": [ + "Read", + "Grep", + "Bash" + ], + "restricted": [] + }, + "metadata": { + "author": "Plugin Author", + "category": "general", + "tags": [] + } +} diff --git a/skills/skill-adapter/assets/skill-schema.json b/skills/skill-adapter/assets/skill-schema.json new file mode 100644 index 0000000..8dc154c --- /dev/null +++ b/skills/skill-adapter/assets/skill-schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Claude Skill Configuration", + "type": "object", + "required": ["name", "description"], + "properties": { + "name": { + "type": "string", + "pattern": "^[a-z0-9-]+$", + "maxLength": 64, + "description": "Skill identifier (lowercase, hyphens only)" + }, + "description": { + "type": "string", + "maxLength": 1024, + "description": "What the skill does and when to use it" + }, + "allowed-tools": { + "type": "string", + "description": "Comma-separated list of allowed tools" + }, + "version": { + "type": "string", + "pattern": "^\\d+\\.\\d+\\.\\d+$", + "description": "Semantic version (x.y.z)" + } + } +} diff --git a/skills/skill-adapter/assets/test-data.json b/skills/skill-adapter/assets/test-data.json new file mode 100644 index 0000000..f0cd871 --- /dev/null +++ b/skills/skill-adapter/assets/test-data.json @@ -0,0 +1,27 @@ +{ + "testCases": [ + { + "name": "Basic activation test", + "input": "trigger phrase example", + "expected": { + "activated": true, + "toolsUsed": ["Read", "Grep"], + "success": true + } + }, + { + "name": "Complex workflow test", + "input": "multi-step trigger example", + "expected": { + "activated": true, + "steps": 3, + "toolsUsed": ["Read", "Write", "Bash"], + "success": true + } + } + ], + "fixtures": { + "sampleInput": "example data", + "expectedOutput": "processed result" + } +} diff --git a/skills/skill-adapter/references/README.md b/skills/skill-adapter/references/README.md new file mode 100644 index 0000000..4b6f154 --- /dev/null +++ b/skills/skill-adapter/references/README.md @@ -0,0 +1,8 @@ +# References + +Bundled resources for market-movers-scanner skill + +- [ ] market_data_api.md: Detailed documentation of the market data APIs used by the plugin, including endpoints, parameters, and response formats. +- [ ] alert_configuration.md: Comprehensive guide to configuring alerts, including available options, thresholds, and notification methods. +- [ ] supported_exchanges.md: List of supported cryptocurrency exchanges, stock exchanges, and forex brokers. +- [ ] data_aggregation_methods.md: Explanation of the data aggregation methods used by the plugin to ensure data reliability. diff --git a/skills/skill-adapter/references/best-practices.md b/skills/skill-adapter/references/best-practices.md new file mode 100644 index 0000000..3505048 --- /dev/null +++ b/skills/skill-adapter/references/best-practices.md @@ -0,0 +1,69 @@ +# Skill Best Practices + +Guidelines for optimal skill usage and development. + +## For Users + +### Activation Best Practices + +1. **Use Clear Trigger Phrases** + - Match phrases from skill description + - Be specific about intent + - Provide necessary context + +2. **Provide Sufficient Context** + - Include relevant file paths + - Specify scope of analysis + - Mention any constraints + +3. **Understand Tool Permissions** + - Check allowed-tools in frontmatter + - Know what the skill can/cannot do + - Request appropriate actions + +### Workflow Optimization + +- Start with simple requests +- Build up to complex workflows +- Verify each step before proceeding +- Use skill consistently for related tasks + +## For Developers + +### Skill Development Guidelines + +1. **Clear Descriptions** + - Include explicit trigger phrases + - Document all capabilities + - Specify limitations + +2. **Proper Tool Permissions** + - Use minimal necessary tools + - Document security implications + - Test with restricted tools + +3. **Comprehensive Documentation** + - Provide usage examples + - Document common pitfalls + - Include troubleshooting guide + +### Maintenance + +- Keep version updated +- Test after tool updates +- Monitor user feedback +- Iterate on descriptions + +## Performance Tips + +- Scope skills to specific domains +- Avoid overlapping trigger phrases +- Keep descriptions under 1024 chars +- Test activation reliability + +## Security Considerations + +- Never include secrets in skill files +- Validate all inputs +- Use read-only tools when possible +- Document security requirements diff --git a/skills/skill-adapter/references/examples.md b/skills/skill-adapter/references/examples.md new file mode 100644 index 0000000..b1d8bd2 --- /dev/null +++ b/skills/skill-adapter/references/examples.md @@ -0,0 +1,70 @@ +# Skill Usage Examples + +This document provides practical examples of how to use this skill effectively. + +## Basic Usage + +### Example 1: Simple Activation + +**User Request:** +``` +[Describe trigger phrase here] +``` + +**Skill Response:** +1. Analyzes the request +2. Performs the required action +3. Returns results + +### Example 2: Complex Workflow + +**User Request:** +``` +[Describe complex scenario] +``` + +**Workflow:** +1. Step 1: Initial analysis +2. Step 2: Data processing +3. Step 3: Result generation +4. Step 4: Validation + +## Advanced Patterns + +### Pattern 1: Chaining Operations + +Combine this skill with other tools: +``` +Step 1: Use this skill for [purpose] +Step 2: Chain with [other tool] +Step 3: Finalize with [action] +``` + +### Pattern 2: Error Handling + +If issues occur: +- Check trigger phrase matches +- Verify context is available +- Review allowed-tools permissions + +## Tips & Best Practices + +- ✅ Be specific with trigger phrases +- ✅ Provide necessary context +- ✅ Check tool permissions match needs +- ❌ Avoid vague requests +- ❌ Don't mix unrelated tasks + +## Common Issues + +**Issue:** Skill doesn't activate +**Solution:** Use exact trigger phrases from description + +**Issue:** Unexpected results +**Solution:** Check input format and context + +## See Also + +- Main SKILL.md for full documentation +- scripts/ for automation helpers +- assets/ for configuration examples diff --git a/skills/skill-adapter/scripts/README.md b/skills/skill-adapter/scripts/README.md new file mode 100644 index 0000000..a2cf24c --- /dev/null +++ b/skills/skill-adapter/scripts/README.md @@ -0,0 +1,8 @@ +# Scripts + +Bundled resources for market-movers-scanner skill + +- [ ] alert_manager.py: Manages and triggers alerts based on market movements, allowing users to configure thresholds and notification methods. +- [ ] data_aggregator.py: Aggregates data from multiple sources to ensure data reliability and accuracy. +- [ ] cache_optimizer.py: Optimizes data caching to improve performance and reduce latency. +- [ ] websocket_manager.py: Manages WebSocket connections for real-time data updates. diff --git a/skills/skill-adapter/scripts/helper-template.sh b/skills/skill-adapter/scripts/helper-template.sh new file mode 100755 index 0000000..c4aae90 --- /dev/null +++ b/skills/skill-adapter/scripts/helper-template.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Helper script template for skill automation +# Customize this for your skill's specific needs + +set -e + +function show_usage() { + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -v, --verbose Enable verbose output" + echo "" +} + +# Parse arguments +VERBOSE=false + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_usage + exit 0 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + *) + echo "Unknown option: $1" + show_usage + exit 1 + ;; + esac +done + +# Your skill logic here +if [ "$VERBOSE" = true ]; then + echo "Running skill automation..." +fi + +echo "✅ Complete" diff --git a/skills/skill-adapter/scripts/validation.sh b/skills/skill-adapter/scripts/validation.sh new file mode 100755 index 0000000..590af58 --- /dev/null +++ b/skills/skill-adapter/scripts/validation.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Skill validation helper +# Validates skill activation and functionality + +set -e + +echo "🔍 Validating skill..." + +# Check if SKILL.md exists +if [ ! -f "../SKILL.md" ]; then + echo "❌ Error: SKILL.md not found" + exit 1 +fi + +# Validate frontmatter +if ! grep -q "^---$" "../SKILL.md"; then + echo "❌ Error: No frontmatter found" + exit 1 +fi + +# Check required fields +if ! grep -q "^name:" "../SKILL.md"; then + echo "❌ Error: Missing 'name' field" + exit 1 +fi + +if ! grep -q "^description:" "../SKILL.md"; then + echo "❌ Error: Missing 'description' field" + exit 1 +fi + +echo "✅ Skill validation passed"