Skill: liam8/crypto-market-data (version 1.0.2)

--- name: Crypto & Stock Market Data (Node.js) description: No API KEY needed for free tier. Professional-grade cryptocurrency and stock market data integration for real-time prices, company profiles, and global analytics. Powered by Node.js with zero external dependencies. --- # Crypto & Stock Market Data Skill (Node.js) A comprehensive suite of tools for retrieving real-time and historical cryptocurrency and stock market data. This skill interfaces with a dedicated market data server to provide high-performance, authenticated access to global financial statistics. Built entirely on Node.js standard libraries — no `npm install` required. ## Key Capabilities | Category | Description | | :--- | :--- | | **Real-time Prices** | Fetch current valuations, market caps, 24h volumes, and price changes for crypto & stocks. | | **Market Discovery** | Track trending assets and top-performing coins by market capitalization. | | **Smart Search** | Quickly find coin IDs or stock tickers by searching names or symbols. | | **Deep Details** | Access exhaustive asset information, from community links to company profiles. | | **Precise Charts** | Retrieve OHLC candlestick data and historical price/volume time-series. | | **Global Metrics** | Monitor total market capitalization and public company treasury holdings. | ## Tool Reference | Script Name | Primary Function | Command Example | | :--- | :--- | :--- | | `get_crypto_price.js` | Multi-coin price fetch | `node scripts/get_crypto_price.js bitcoin` | | `get_stock_quote.js` | Real-time stock quotes | `node scripts/get_stock_quote.js AAPL MSFT` | | `get_company_profile.js` | Company overview | `node scripts/get_company_profile.js AAPL` | | `search_stocks.js` | Find stock tickers | `node scripts/search_stocks.js apple` | | `get_trending_coins.js` | 24h trending assets | `node scripts/get_trending_coins.js` | | `get_top_coins.js` | Market leaderboards | `node scripts/get_top_coins.js --per_page=20` | | `search_coins.js` | Asset discovery | `node scripts/search_coins.js solana` | | `get_coin_details.js` | Comprehensive metadata | `node scripts/get_coin_details.js ethereum` | | `get_coin_ohlc_chart.js` | Candlestick data | `node scripts/get_coin_ohlc_chart.js bitcoin` | | `get_coin_historical_chart.js` | Time-series data | `node scripts/get_coin_historical_chart.js bitcoin` | | `get_global_market_data.js` | Macro market stats | `node scripts/get_global_market_data.js` | | `get_public_companies_holdings.js` | Treasury holdings | `node scripts/get_public_companies_holdings.js bitcoin` | | `get_supported_currencies.js` | Valuation options | `node scripts/get_supported_currencies.js` | --- ## Usage Details ### 1. `get_crypto_price.js` Fetch real-time pricing and basic market metrics for one or more cryptocurrencies. **Syntax:** ```bash node scripts/get_crypto_price.js <coin_id_1> [coin_id_2] ... [--currency=usd] ``` **Parameters:** - `coin_id`: The unique identifier for the coin (e.g., `bitcoin`, `solana`). - `--currency`: The target currency for valuation (default: `usd`). **Example:** ```bash node scripts/get_crypto_price.js bitcoin ethereum cardano --currency=jpy ``` --- ### 2. `get_top_coins.js` Retrieve a list of the top cryptocurrencies ranked by market capitalization. **Syntax:** ```bash node scripts/get_top_coins.js [--currency=usd] [--per_page=10] [--page=1] [--order=market_cap_desc] ``` **Parameters:** - `--currency`: Valuation currency (default: `usd`). - `--per_page`: Number of results (1-250, default: `10`). - `--order`: Sorting logic (e.g., `market_cap_desc`, `volume_desc`). --- ### 3. `get_coin_ohlc_chart.js` Get Open, High, Low, Close (candlestick) data for technical analysis. **Syntax:** ```bash node scripts/get_coin_ohlc_chart.js <coin_id> [--currency=usd] [--days=7] ``` **Allowed `days` values:** `1, 7, 14, 30, 90, 180, 365`. | Range | Resolution | | :--- | :--- | | 1-2 Days | 30 Minute intervals | | 3-30 Days | 4 Hour intervals | | 31+ Days | 4 Day intervals | --- ### 4. `get_coin_historical_chart.js` Retrieve detailed historical time-series data for price, market cap, and volume. **Syntax:** ```bash node scripts/get_coin_historical_chart.js <coin_id> [--currency=usd] [--days=30] ``` --- ### 5. `get_stock_quote.js` Fetch real-time stock prices for one or more ticker symbols. **Syntax:** ```bash node scripts/get_stock_quote.js <SYMBOL_1> [SYMBOL_2] ... ``` --- ### 6. `get_company_profile.js` Get a comprehensive company profile, including description, industry, and CEO. **Syntax:** ```bash node scripts/get_company_profile.js <SYMBOL> ``` --- ## Important Guidelines ### Cryptos: Use IDs | Stocks: Use Tickers - **Cryptocurrencies**: Always use **Coin IDs** (slugs) instead of ticker symbols (e.g., `bitcoin`, `BTC`). - **Stocks**: Always use **Ticker Symbols** (e.g., `AAPL`, `Apple`). Use `search_coins.js` if you are unsure of the correct ID. ### Authentication Authentication is handled **automatically** by the internal `api_client.js`. Here is how it works simply: - **Endpoint**: `GET https://api.igent.net/api/token` - **Mechanism**: 1. **Automatic Retrieval**: The first time you use a tool, it asks the server for a temporary session token. 2. **Local Storage**: This token is stored in a hidden `.token` file locally so it can be reused for subsequent requests. 3. **Automatic Headers**: The client automatically includes this token in every request to prove you are authorized. 4. **Auto-Refresh**: If a token expires, the client automatically fetches a new one without you needing to do anything. No manual API keys or configuration are required. ### Rate Limiting While the system is robust, please avoid burst requests (more than 30 per minute) to maintain service stability for all users. ### Agent Integration This skill is fully compatible with OpenClaw and other agents using the **AgentSkills** standard. Execute scripts directly from the `scripts/` directory.

path: package.json
content: {
  "name": "crypto-market-data-skill-node",
  "version": "1.0.0",
  "description": "Crypto & Stock Market Data Skill (Node.js) — No external dependencies",
  "private": true,
  "scripts": {},
  "dependencies": {}
}


path: scripts/get_crypto_price.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getCryptoPrice(coinIds, currency = 'usd') {
  const params = {
    ids: coinIds.join(','),
    vs_currencies: currency,
    include_market_cap: 'true',
    include_24hr_vol: 'true',
    include_24hr_change: 'true',
    include_last_updated_at: 'true',
  };
  return apiClient.get('/crypto/prices', params);
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_crypto_price.js <coin_id_1> [coin_id_2] ... [--currency=usd]',
      example: 'node scripts/get_crypto_price.js bitcoin ethereum --currency=usd',
    }, null, 2));
    process.exit(1);
  }

  let currency = 'usd';
  const coinIds = [];
  for (const arg of args) {
    if (arg.startsWith('--currency=')) {
      currency = arg.split('=')[1];
    } else {
      coinIds.push(arg);
    }
  }
  if (coinIds.length === 0) coinIds.push('bitcoin');

  const result = await getCryptoPrice(coinIds, currency);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_coin_details.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getCoinDetails(coinId) {
  return apiClient.get(`/crypto/coins/${coinId}`);
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_coin_details.js <coin_id>',
      example: 'node scripts/get_coin_details.js bitcoin',
    }, null, 2));
    process.exit(1);
  }

  const result = await getCoinDetails(args[0]);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_global_market_data.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getGlobalMarketData() {
  return apiClient.get('/crypto/global');
}

async function main() {
  const result = await getGlobalMarketData();
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_public_companies_holdings.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getPublicCompaniesHoldings(coinId) {
  if (!['bitcoin', 'ethereum'].includes(coinId)) {
    return { error: "coin_id must be either 'bitcoin' or 'ethereum'" };
  }
  return apiClient.get('/crypto/companies/holdings', { coin_id: coinId });
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_public_companies_holdings.js <coin_id>',
      example: 'node scripts/get_public_companies_holdings.js bitcoin',
      note: "coin_id must be either 'bitcoin' or 'ethereum'",
    }, null, 2));
    process.exit(1);
  }

  const result = await getPublicCompaniesHoldings(args[0].toLowerCase());
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/api_client.js
content: #!/usr/bin/env node
const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');
const url = require('url');

const BASE_URL = process.env.API_BASE_URL || 'https://api.igent.net/api';
const TOKEN_FILE = path.join(__dirname, '.token');
const USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36';

function _loadToken() {
  if (!fs.existsSync(TOKEN_FILE)) return null;

  try {
    const data = JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf-8'));
    const expiresAtStr = data.expires_at;
    if (!expiresAtStr) return null;

    const expiresAt = new Date(expiresAtStr);
    if (Date.now() >= expiresAt.getTime()) return null;

    return data.token || null;
  } catch (e) {
    console.error(`Warning: Failed to load token file: ${e.message}`);
    return null;
  }
}

function _httpRequest(reqUrl, headers = {}) {
  return new Promise((resolve, reject) => {
    const parsed = new URL(reqUrl);
    const mod = parsed.protocol === 'https:' ? https : http;
    const options = {
      hostname: parsed.hostname,
      port: parsed.port,
      path: parsed.pathname + parsed.search,
      method: 'GET',
      headers: { 'User-Agent': USER_AGENT, ...headers },
    };

    const req = mod.request(options, (res) => {
      let body = '';
      res.on('data', (chunk) => (body += chunk));
      res.on('end', () => resolve({ status: res.statusCode, body }));
    });
    req.on('error', reject);
    req.end();
  });
}

async function _fetchNewToken() {
  const reqUrl = `${BASE_URL}/token`;
  const { status, body } = await _httpRequest(reqUrl);

  if (status === 200) {
    const data = JSON.parse(body);
    fs.writeFileSync(TOKEN_FILE, JSON.stringify(data));
    return data.token;
  }
  throw new Error(`Failed to fetch token. Status: ${status}`);
}

async function getToken() {
  const [REDACTED_SECRET]);
  if (token) return token;
  return _fetchNewToken();
}

async function get(endpoint, params) {
  let token;
  try {
    token = await getToken();
  } catch (e) {
    return { error: e.message };
  }

  const parsed = new URL(`${BASE_URL}${endpoint}`);
  if (params) {
    for (const [key, value] of Object.entries(params)) {
      parsed.searchParams.set(key, value);
    }
  }

  const headers = {
    accept: 'application/json',
    'X-API-Token': token,
  };

  try {
    const { status, body } = await _httpRequest(parsed.toString(), headers);
    if (status === 200) {
      return JSON.parse(body);
    }
    try {
      const errorJson = JSON.parse(body);
      return { error: `HTTP Error ${status}: ${errorJson.error || 'Unknown'}` };
    } catch {
      return { error: `HTTP Error ${status}` };
    }
  } catch (e) {
    return { error: `Request error: ${e.message}` };
  }
}

module.exports = { get, getToken };


path: scripts/search_coins.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function searchCoins(query) {
  return apiClient.get('/crypto/search', { query });
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/search_coins.js <query>',
      example: 'node scripts/search_coins.js bitcoin',
    }, null, 2));
    process.exit(1);
  }

  const query = args.join(' ');
  const result = await searchCoins(query);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_top_coins.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getTopCoins(vsCurrency = 'usd', perPage = 10, page = 1, order = 'market_cap_desc') {
  const params = {
    vs_currency: vsCurrency,
    per_page: String(perPage),
    page: String(page),
    order,
  };
  return apiClient.get('/crypto/coins/markets', params);
}

async function main() {
  let vsCurrency = 'usd';
  let perPage = 10;
  let page = 1;
  let order = 'market_cap_desc';

  for (const arg of process.argv.slice(2)) {
    if (arg.startsWith('--currency=')) vsCurrency = arg.split('=')[1];
    else if (arg.startsWith('--per_page=')) perPage = parseInt(arg.split('=')[1], 10);
    else if (arg.startsWith('--page=')) page = parseInt(arg.split('=')[1], 10);
    else if (arg.startsWith('--order=')) order = arg.split('=')[1];
  }

  const result = await getTopCoins(vsCurrency, perPage, page, order);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_stock_quote.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getStockQuote(symbols) {
  const symbolsStr = symbols.map((s) => s.toUpperCase()).join(',');
  return apiClient.get('/stock/quote', { symbols: symbolsStr });
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_stock_quote.js <SYMBOL_1> [SYMBOL_2] ...',
      example: 'node scripts/get_stock_quote.js AAPL MSFT GOOG',
    }, null, 2));
    process.exit(1);
  }

  const result = await getStockQuote(args);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/search_stocks.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function searchStocks(query, limit = 10) {
  return apiClient.get('/stock/search', { query, limit: String(limit) });
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/search_stocks.js <query> [--limit=10]',
      example: 'node scripts/search_stocks.js apple --limit=5',
    }, null, 2));
    process.exit(1);
  }

  let limit = 10;
  const queryParts = [];

  for (const arg of args) {
    if (arg.startsWith('--limit=')) {
      const val = parseInt(arg.split('=')[1], 10);
      if (!isNaN(val)) limit = val;
    } else {
      queryParts.push(arg);
    }
  }

  const result = await searchStocks(queryParts.join(' '), limit);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_coin_historical_chart.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getCoinHistoricalChart(coinId, vsCurrency = 'usd', days = '7', precision = null) {
  const params = { vs_currency: vsCurrency, days, interval: 'daily' };
  if (precision) params.precision = precision;

  return apiClient.get(`/crypto/coins/${coinId}/market_chart`, params);
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_coin_historical_chart.js <coin_id> [--currency=usd] [--days=7] [--precision=2]',
      example: 'node scripts/get_coin_historical_chart.js bitcoin --currency=usd --days=30',
    }, null, 2));
    process.exit(1);
  }

  const coinId = args[0];
  let vsCurrency = 'usd';
  let days = '7';
  let precision = null;

  for (const arg of args.slice(1)) {
    if (arg.startsWith('--currency=')) vsCurrency = arg.split('=')[1];
    else if (arg.startsWith('--days=')) days = arg.split('=')[1];
    else if (arg.startsWith('--precision=')) precision = arg.split('=')[1];
  }

  const result = await getCoinHistoricalChart(coinId, vsCurrency, days, precision);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_coin_ohlc_chart.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

const VALID_DAYS = ['1', '7', '14', '30', '90', '180', '365'];

async function getCoinOhlcChart(coinId, vsCurrency = 'usd', days = '7', precision = null) {
  if (!VALID_DAYS.includes(days)) {
    return { error: `days must be one of: ${VALID_DAYS.join(', ')}` };
  }

  const params = { vs_currency: vsCurrency, days };
  if (precision) params.precision = precision;

  return apiClient.get(`/crypto/coins/${coinId}/ohlc`, params);
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_coin_ohlc_chart.js <coin_id> [--currency=usd] [--days=7] [--precision=2]',
      example: 'node scripts/get_coin_ohlc_chart.js bitcoin --currency=usd --days=30',
      valid_days: VALID_DAYS,
    }, null, 2));
    process.exit(1);
  }

  const coinId = args[0];
  let vsCurrency = 'usd';
  let days = '7';
  let precision = null;

  for (const arg of args.slice(1)) {
    if (arg.startsWith('--currency=')) vsCurrency = arg.split('=')[1];
    else if (arg.startsWith('--days=')) days = arg.split('=')[1];
    else if (arg.startsWith('--precision=')) precision = arg.split('=')[1];
  }

  const result = await getCoinOhlcChart(coinId, vsCurrency, days, precision);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_trending_coins.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getTrendingCoins() {
  return apiClient.get('/crypto/search/trending');
}

async function main() {
  const result = await getTrendingCoins();
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_company_profile.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getCompanyProfile(symbol) {
  return apiClient.get('/stock/profile', { symbol: symbol.toUpperCase() });
}

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) {
    console.log(JSON.stringify({
      usage: 'node scripts/get_company_profile.js <SYMBOL>',
      example: 'node scripts/get_company_profile.js AAPL',
    }, null, 2));
    process.exit(1);
  }

  const result = await getCompanyProfile(args[0]);
  console.log(JSON.stringify(result, null, 2));
}

main();


path: scripts/get_supported_currencies.js
content: #!/usr/bin/env node
const apiClient = require('./api_client');

async function getSupportedCurrencies() {
  return apiClient.get('/crypto/supported-currencies');
}

async function main() {
  const result = await getSupportedCurrencies();
  console.log(JSON.stringify(result, null, 2));
}

main();


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