diff --git a/Readme.md b/Readme.md index 44d393c..6286e41 100644 --- a/Readme.md +++ b/Readme.md @@ -461,6 +461,13 @@ gmgn-cli market trending \ --order-by volume --limit 20 \ --filter not_risk --filter not_honeypot +# Trending with numeric range filters (min_*/max_* are forwarded as query params) +gmgn-cli market trending \ + --chain sol --interval 1h \ + --min-liquidity 10000 --max-liquidity 1000000 \ + --max-created 30m --min-smart-degen-count 1 \ + --order-by volume --limit 30 + gmgn-cli market trenches \ --chain sol \ --type new_creation --type near_completion --type completed \ diff --git a/Readme.zh.md b/Readme.zh.md index ef65031..7a8a107 100644 --- a/Readme.zh.md +++ b/Readme.zh.md @@ -491,6 +491,13 @@ gmgn-cli market trending \ --order-by volume --limit 20 \ --filter not_risk --filter not_honeypot +# 热门榜 + 数值范围过滤(min_*/max_* 以查询参数透传) +gmgn-cli market trending \ + --chain sol --interval 1h \ + --min-liquidity 10000 --max-liquidity 1000000 \ + --max-created 30m --min-smart-degen-count 1 \ + --order-by volume --limit 30 + # 战壕新币列表 gmgn-cli market trenches \ --chain sol \ diff --git a/docs/cli-usage.md b/docs/cli-usage.md index f180fb6..e56ded8 100644 --- a/docs/cli-usage.md +++ b/docs/cli-usage.md @@ -120,18 +120,21 @@ npx gmgn-cli market trending \ [--direction asc|desc] \ [--filter ] \ [--platform ] \ + [--min- ] [--max- ] \ [--raw] ``` | Option | Required | Description | |--------|----------|-------------| -| `--chain` | Yes | `sol` / `bsc` / `base` | -| `--interval` | Yes | `1h` / `3h` / `6h` / `24h` | +| `--chain` | Yes | `sol` / `bsc` / `base` / `eth` | +| `--interval` | Yes | `1m` / `5m` / `1h` / `6h` / `24h` | | `--limit` | No | Number of results (default 100, max 100) | | `--order-by` | No | Sort field: `volume` / `swaps` / `liquidity` / `marketcap` / `holders` / `price` / `change` / `change1m` / `change5m` / `change1h` / `renowned_count` / `smart_degen_count` / `bluechip_owner_percentage` / `rank` / `creation_timestamp` / `square_mentions` / `history_highest_market_cap` / `gas_fee` | | `--direction` | No | Sort direction: `asc` / `desc` (default `desc`) | -| `--filter` | No | Filter tag (repeatable): `has_social` / `not_risk` / `not_honeypot` / `verified` / `locked` / `renounced` / `distributed` / `frozen` / `burn` / `token_burnt` / `creator_hold` / `creator_close` / `creator_add_liquidity` / `creator_remove_liquidity` / `creator_sell` / `creator_buy` / `not_wash_trading` / `not_social_dup` / `not_image_dup` / `is_internal_market` / `is_out_market` | +| `--filter` | No | Filter tag (repeatable): `has_social` / `not_risk` / `not_honeypot` / `verified` / `locked` / `renounced` / `distributed` / `frozen` / `burn` / `token_burnt` / `creator_hold` / `creator_close` / `creator_add_liquidity` / `creator_remove_liquidity` / `creator_sell` / `creator_buy` / `not_wash_trading` / `not_social_dup` / `not_image_dup` / `is_internal_market` / `is_out_market`. The gmgn web client also sends aliases `social_not_duplicate` / `img_not_duplicate` / `is_burnt` / `launching` / `migrated`, which are accepted but only the canonical tags change behavior. | | `--platform` | No | Platform filter (repeatable). Omit (or pass an empty list) to include **all** platforms. Available values depend on chain — see below. | +| `--min-` / `--max-` | No | Numeric range filters (inclusive). Supported metrics: `volume` / `liquidity` / `marketcap` / `history-highest-marketcap` / `swaps` / `holder-count` / `gas-fee` / `renowned-count` / `smart-degen-count` / `bot-degen-count` / `visiting-count` / `price-change-percent` / `insider-rate` / `bundler-rate` / `entrapment-ratio` / `top10-holder-rate` / `top70-sniper-hold-rate` / `dev-team-hold-rate`. Unknown metrics are ignored by the service. | +| `--min-created` / `--max-created` | No | Token-age window, duration string (`1m` / `6h` / `7d`). `--min-created` is a minimum age (excludes younger tokens); `--max-created` a maximum age (excludes older tokens). | **`sol` platforms:** `Pump.fun` / `pump_mayhem` / `pump_mayhem_agent` / `pump_agent` / `letsbonk` / `bonkers` / `bags` / `memoo` / `liquid` / `bankr` / `zora` / `surge` / `anoncoin` / `moonshot_app` / `wendotdev` / `heaven` / `sugar` / `token_mill` / `believe` / `trendsfun` / `trends_fun` / `jup_studio` / `Moonshot` / `boop` / `xstocks` / `ray_launchpad` / `meteora_virtual_curve` / `pool_ray` / `pool_meteora` / `pool_pump_amm` / `pool_orca` diff --git a/skills/gmgn-market/SKILL.md b/skills/gmgn-market/SKILL.md index a3c496f..acb0b94 100644 --- a/skills/gmgn-market/SKILL.md +++ b/skills/gmgn-market/SKILL.md @@ -146,6 +146,32 @@ The response is an object with a `list` array. Each element in `list` is one can | `--filter ` | Repeatable filter tags (chain-specific). **⚠️ SOL defaults: `renounced frozen`; BSC/Base/ETH defaults: `not_honeypot verified renounced`.** Omitting `--filter` is NOT "no filter" — chain defaults always apply. **sol** tags: `renounced` / `frozen` / `burn` / `token_burnt` / `has_social` / `not_social_dup` / `not_image_dup` / `dexscr_update_link` / `not_wash_trading` / `is_internal_market` / `is_out_market`. **evm** tags: `not_honeypot` / `verified` / `renounced` / `locked` / `token_burnt` / `has_social` / `not_social_dup` / `not_image_dup` / `dexscr_update_link` / `is_internal_market` / `is_out_market` | | `--platform ` | Repeatable platform filter (chain-specific). **sol**: `Pump.fun` / `pump_mayhem` / `pump_mayhem_agent` / `pump_agent` / `letsbonk` / `bonkers` / `bags` / `memoo` / `liquid` / `bankr` / `zora` / `surge` / `anoncoin` / `moonshot_app` / `wendotdev` / `heaven` / `sugar` / `token_mill` / `believe` / `trendsfun` / `trends_fun` / `jup_studio` / `Moonshot` / `boop` / `xstocks` / `ray_launchpad` / `meteora_virtual_curve` / `pool_ray` / `pool_meteora` / `pool_pump_amm` / `pool_orca`. **bsc**: `fourmeme` / `fourmeme_agent` / `bn_fourmeme` / `four_xmode_agent` / `cubepeg` / `likwid` / `goplus_creator` / `goplus_skills` / `openfour` / `flap` / `flap_stocks` / `flap_aioracle` / `clanker` / `lunafun` / `pool_uniswap` / `pool_pancake`. **base**: `clanker` / `bankr` / `flaunch` / `zora` / `zora_creator` / `baseapp` / `basememe` / `virtuals_v2` / `klik`. **eth**: `trench` / `clanker` / `klik` / `livo` / `stroid` / `pool_uniswap_v2` / `pool_uniswap_v3` / `printr` | +### `market trending` Range Filters + +Optional `--min-*` / `--max-*` flags apply server-side numeric range filtering (inclusive). Unknown metrics are ignored by the service. + +| Option | Description | +|--------|-------------| +| `--min-volume` / `--max-volume` | Trading volume (USD) | +| `--min-liquidity` / `--max-liquidity` | Liquidity (USD) | +| `--min-marketcap` / `--max-marketcap` | Market cap (USD) | +| `--min-history-highest-marketcap` / `--max-history-highest-marketcap` | Historical highest market cap (USD) | +| `--min-swaps` / `--max-swaps` | Swap count | +| `--min-holder-count` / `--max-holder-count` | Holder count | +| `--min-gas-fee` / `--max-gas-fee` | Gas fee | +| `--min-renowned-count` / `--max-renowned-count` | KOL / renowned wallet count | +| `--min-smart-degen-count` / `--max-smart-degen-count` | Smart-money holder count | +| `--min-bot-degen-count` / `--max-bot-degen-count` | Bot-degen wallet count | +| `--min-visiting-count` / `--max-visiting-count` | Visitor count | +| `--min-price-change-percent` / `--max-price-change-percent` | Price change ratio over the interval | +| `--min-insider-rate` / `--max-insider-rate` | Insider trading ratio (0–1); tokens lacking this field are excluded | +| `--min-bundler-rate` / `--max-bundler-rate` | Bundle-bot trading ratio (0–1); tokens lacking this field are excluded | +| `--min-entrapment-ratio` / `--max-entrapment-ratio` | Entrapment trading ratio (0–1); tokens lacking this field are excluded | +| `--min-top10-holder-rate` / `--max-top10-holder-rate` | Top-10 holder concentration (0–1) | +| `--min-top70-sniper-hold-rate` / `--max-top70-sniper-hold-rate` | Top-70 sniper holding ratio (0–1) | +| `--min-dev-team-hold-rate` / `--max-dev-team-hold-rate` | Dev-team holding ratio (0–1); `--min-dev-team-hold-rate` also excludes creator-close tokens | +| `--min-created` / `--max-created` | Token age window, duration string `1m` / `6h` / `7d`. `--min-created` is a minimum age (excludes younger tokens); `--max-created` a maximum age (excludes older tokens) | + ## Usage Examples ### Kline @@ -277,6 +303,28 @@ gmgn-cli market trending \ --order-by smart_degen_count --limit 20 --raw ``` +### Trending — Numeric Range Filters + +```bash +# SOL 1h trending — liquidity 10k–1M, market cap above 50k, sorted by volume +gmgn-cli market trending \ + --chain sol --interval 1h \ + --min-liquidity 10000 --max-liquidity 1000000 --min-marketcap 50000 \ + --order-by volume --limit 30 --raw + +# SOL 5m hottest — fresh tokens (under 30 min old) with smart money interest +gmgn-cli market trending \ + --chain sol --interval 5m \ + --max-created 30m --min-smart-degen-count 1 \ + --order-by volume --limit 50 --raw + +# SOL 1h trending — exclude high-insider / high-bundler tokens +gmgn-cli market trending \ + --chain sol --interval 1h \ + --max-insider-rate 0.3 --max-bundler-rate 0.3 \ + --order-by volume --limit 20 --raw +``` + ### Trending — Base by Launchpad Platform ```bash diff --git a/src/commands/market.ts b/src/commands/market.ts index 32cabbb..db05106 100644 --- a/src/commands/market.ts +++ b/src/commands/market.ts @@ -48,7 +48,7 @@ export function registerMarketCommands(program: Command): void { printResult(data, opts.raw); }); - market + const trendingCmd = market .command("trending") .description("Get trending token swap data") .requiredOption("--chain ", "Chain: sol / bsc / base / eth") @@ -58,20 +58,40 @@ export function registerMarketCommands(program: Command): void { .option("--direction ", "Sort direction: asc / desc") .option("--filter ", "Filter tags, repeatable. sol: renounced / frozen / has_social / not_wash_trading / ... evm: not_honeypot / verified / renounced / locked / ... (see docs for full list)") .option("--platform ", "Platform filter, repeatable. sol: Pump.fun / letsbonk / moonshot_app / ... bsc: fourmeme / four_xmode_agent / cubepeg / likwid / goplus_creator / goplus_skills / openfour / flap / flap_stocks / flap_aioracle / clanker / ... base: clanker / flaunch / zora / ... eth: trench / clanker / klik / livo / stroid / pool_uniswap_v2 / pool_uniswap_v3 / printr (see docs for full list)") - .option("--raw", "Output raw JSON") - .action(async (opts) => { - validateChain(opts.chain); - const extra: Record = {}; - if (opts.limit != null) extra["limit"] = opts.limit; - if (opts.orderBy) extra["order_by"] = opts.orderBy; - if (opts.direction) extra["direction"] = opts.direction; - if (opts.filter?.length) extra["filters"] = opts.filter; - if (opts.platform?.length) extra["platforms"] = opts.platform; + .option("--raw", "Output raw JSON"); - const client = new OpenApiClient(getConfig()); - const data = await client.getTrendingSwaps(opts.chain, opts.interval, extra).catch(exitOnError); - printResult(data, opts.raw); - }); + // Dynamically register all server-side min_*/max_* range filter flags + for (const def of RANK_RANGE_FIELDS) { + const flag = def.api.replace(/_/g, "-"); + if (def.type === "int") { + trendingCmd.option(`--${flag} <${def.type}>`, def.desc, parseInt); + } else if (def.type === "float") { + trendingCmd.option(`--${flag} <${def.type}>`, def.desc, parseFloat); + } else { + trendingCmd.option(`--${flag} `, def.desc); + } + } + + trendingCmd.action(async (opts) => { + validateChain(opts.chain); + const extra: Record = {}; + if (opts.limit != null) extra["limit"] = opts.limit; + if (opts.orderBy) extra["order_by"] = opts.orderBy; + if (opts.direction) extra["direction"] = opts.direction; + if (opts.filter?.length) extra["filters"] = opts.filter; + if (opts.platform?.length) extra["platforms"] = opts.platform; + + // Apply server-side min_*/max_* range filters + const optsMap = opts as Record; + for (const def of RANK_RANGE_FIELDS) { + const val = optsMap[apiFieldToCliKey(def.api)]; + if (val != null) extra[def.api] = val as string | number; + } + + const client = new OpenApiClient(getConfig()); + const data = await client.getTrendingSwaps(opts.chain, opts.interval, extra).catch(exitOnError); + printResult(data, opts.raw); + }); const trenchesCmd = market .command("trenches") @@ -270,6 +290,52 @@ const TRENCHES_FILTER_FIELDS: TrenchesFilterField[] = [ { api: "max_tg_call_count", type: "int", desc: "Max Telegram call count" }, ]; +// Server-side numeric range filters for `market trending` (/v1/market/rank). +// Passed through as min_/max_ query params; the service applies the +// metrics it understands and ignores the rest. min_created/max_created are token-age +// windows expressed as duration strings (e.g. 1m / 6h / 7d) — note these use m/h/d, +// NOT the s/m form used by trenches; min_created is a minimum age, max_created a maximum. +const RANK_RANGE_FIELDS: TrenchesFilterField[] = [ + { api: "min_volume", type: "float", desc: "Min trading volume (USD)" }, + { api: "max_volume", type: "float", desc: "Max trading volume (USD)" }, + { api: "min_liquidity", type: "float", desc: "Min liquidity (USD)" }, + { api: "max_liquidity", type: "float", desc: "Max liquidity (USD)" }, + { api: "min_marketcap", type: "float", desc: "Min market cap (USD)" }, + { api: "max_marketcap", type: "float", desc: "Max market cap (USD)" }, + { api: "min_history_highest_marketcap", type: "float", desc: "Min historical highest market cap (USD)" }, + { api: "max_history_highest_marketcap", type: "float", desc: "Max historical highest market cap (USD)" }, + { api: "min_swaps", type: "int", desc: "Min swap count" }, + { api: "max_swaps", type: "int", desc: "Max swap count" }, + { api: "min_holder_count", type: "int", desc: "Min holder count" }, + { api: "max_holder_count", type: "int", desc: "Max holder count" }, + { api: "min_gas_fee", type: "float", desc: "Min gas fee" }, + { api: "max_gas_fee", type: "float", desc: "Max gas fee" }, + { api: "min_renowned_count", type: "int", desc: "Min KOL / renowned wallet count" }, + { api: "max_renowned_count", type: "int", desc: "Max KOL / renowned wallet count" }, + { api: "min_smart_degen_count", type: "int", desc: "Min smart-money holder count" }, + { api: "max_smart_degen_count", type: "int", desc: "Max smart-money holder count" }, + { api: "min_bot_degen_count", type: "int", desc: "Min bot-degen wallet count" }, + { api: "max_bot_degen_count", type: "int", desc: "Max bot-degen wallet count" }, + { api: "min_visiting_count", type: "int", desc: "Min visitor count" }, + { api: "max_visiting_count", type: "int", desc: "Max visitor count" }, + { api: "min_price_change_percent", type: "float", desc: "Min price change ratio over the interval" }, + { api: "max_price_change_percent", type: "float", desc: "Max price change ratio over the interval" }, + { api: "min_insider_rate", type: "float", desc: "Min insider trading ratio (0–1); tokens lacking this field are excluded" }, + { api: "max_insider_rate", type: "float", desc: "Max insider trading ratio (0–1); tokens lacking this field are excluded" }, + { api: "min_bundler_rate", type: "float", desc: "Min bundle-bot trading ratio (0–1); tokens lacking this field are excluded" }, + { api: "max_bundler_rate", type: "float", desc: "Max bundle-bot trading ratio (0–1); tokens lacking this field are excluded" }, + { api: "min_entrapment_ratio", type: "float", desc: "Min entrapment trading ratio (0–1); tokens lacking this field are excluded" }, + { api: "max_entrapment_ratio", type: "float", desc: "Max entrapment trading ratio (0–1); tokens lacking this field are excluded" }, + { api: "min_top10_holder_rate", type: "float", desc: "Min top-10 holder concentration (0–1)" }, + { api: "max_top10_holder_rate", type: "float", desc: "Max top-10 holder concentration (0–1)" }, + { api: "min_top70_sniper_hold_rate", type: "float", desc: "Min top-70 sniper holding ratio (0–1)" }, + { api: "max_top70_sniper_hold_rate", type: "float", desc: "Max top-70 sniper holding ratio (0–1)" }, + { api: "min_dev_team_hold_rate", type: "float", desc: "Min dev-team holding ratio (0–1); also excludes creator-close tokens" }, + { api: "max_dev_team_hold_rate", type: "float", desc: "Max dev-team holding ratio (0–1)" }, + { api: "min_created", type: "string", desc: "Min token age (minimum age), duration string e.g. 1m / 6h / 7d" }, + { api: "max_created", type: "string", desc: "Max token age (maximum age), duration string e.g. 600m / 24h" }, +]; + // Named filter presets using actual server-side API field names const TRENCHES_FILTER_PRESETS: Record> = { safe: {