From e9597b57fa03c419842f2caa8bddc38527c90c8e Mon Sep 17 00:00:00 2001 From: Ricardo Delfino Date: Wed, 22 Apr 2026 15:02:39 -0300 Subject: [PATCH] chore: remove kyc_required, cmc_rank, supported_fiats, stablecoins, withdrawal_usdt from all exchanges --- data/exchanges.json | 102 +----------------- docs/MAINTENANCE.md | 202 ++++++++++++++++++++++++++++++++++++ schema/exchange.schema.json | 50 +-------- scripts/check-fees.js | 120 ++++++++++----------- 4 files changed, 258 insertions(+), 216 deletions(-) create mode 100644 docs/MAINTENANCE.md diff --git a/data/exchanges.json b/data/exchanges.json index 2595cdf..40af485 100644 --- a/data/exchanges.json +++ b/data/exchanges.json @@ -13,14 +13,9 @@ "maker": 0.001, "taker": 0.001, "fee_url": "https://www.binance.com/en/fee/schedule", - "withdrawal_usdt": 1.0, "note": "VIP tiers reduce fees. BNB discount available (25% off)." }, - "supported_fiats": ["USD", "EUR", "BRL", "GBP", "AUD"], - "stablecoins": ["USDT", "USDC", "BUSD", "TUSD", "FDUSD"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": 1, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -37,14 +32,9 @@ "maker": 0.0008, "taker": 0.001, "fee_url": "https://www.okx.com/fees", - "withdrawal_usdt": 1.0, "note": "Tiered fee structure based on 30-day volume and OKB holdings." }, - "supported_fiats": ["USD", "EUR", "GBP", "AUD"], - "stablecoins": ["USDT", "USDC", "DAI"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": 4, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -61,14 +51,9 @@ "maker": 0.001, "taker": 0.001, "fee_url": "https://www.bybit.com/en/help-center/article/Trading-Fee-Structure", - "withdrawal_usdt": 1.0, "note": "Spot maker/taker both 0.1%. Futures fees differ." }, - "supported_fiats": ["USD", "EUR", "GBP"], - "stablecoins": ["USDT", "USDC", "DAI"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": 3, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -85,14 +70,9 @@ "maker": 0.001, "taker": 0.001, "fee_url": "https://www.bitget.com/en/rate/fee", - "withdrawal_usdt": 1.0, "note": "BGB token holders receive fee discounts." }, - "supported_fiats": ["USD", "EUR"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": 5, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -109,14 +89,9 @@ "maker": 0.001, "taker": 0.001, "fee_url": "https://www.kucoin.com/vip/privilege", - "withdrawal_usdt": 1.0, "note": "KCS holders receive up to 20% fee discount." }, - "supported_fiats": ["USD", "EUR"], - "stablecoins": ["USDT", "USDC", "DAI", "TUSD"], - "kyc_required": false, "monitored_by_dolarmap": true, - "cmc_rank": 9, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -133,14 +108,9 @@ "maker": 0.0, "taker": 0.0, "fee_url": "https://www.mexc.com/fee", - "withdrawal_usdt": 1.0, "note": "Spot maker/taker are currently 0%. Futures fees apply separately." }, - "supported_fiats": ["USD", "EUR"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": false, "monitored_by_dolarmap": true, - "cmc_rank": 10, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -157,14 +127,9 @@ "maker": 0.003, "taker": 0.005, "fee_url": "https://foxbit.com.br/taxas", - "withdrawal_usdt": 2.0, "note": "Fees vary by trading volume. Pix deposits free; withdrawals may have small BRL fee." }, - "supported_fiats": ["BRL"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -181,14 +146,9 @@ "maker": 0.003, "taker": 0.005, "fee_url": "https://www.novadax.com.br/taxas-e-limites", - "withdrawal_usdt": 2.0, "note": "ONG token holders receive fee discounts. Pix available for BRL operations." }, - "supported_fiats": ["BRL"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -205,14 +165,9 @@ "maker": 0.005, "taker": 0.005, "fee_url": "https://brasilbitcoin.com.br/taxas", - "withdrawal_usdt": 2.5, "note": "Flat 0.5% trading fee. Pix available for BRL deposits and withdrawals." }, - "supported_fiats": ["BRL"], - "stablecoins": ["USDT"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -229,14 +184,9 @@ "maker": 0.003, "taker": 0.005, "fee_url": "https://coinext.com.br/taxas", - "withdrawal_usdt": 2.0, "note": "Pix deposits and withdrawals available for BRL." }, - "supported_fiats": ["BRL"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -253,14 +203,9 @@ "maker": 0.005, "taker": 0.005, "fee_url": "https://bitso.com/fees", - "withdrawal_usdt": 2.0, "note": "Tiered maker/taker based on 30-day volume. Pix available in Brazil." }, - "supported_fiats": ["BRL", "MXN", "ARS"], - "stablecoins": ["USDT", "USDC", "DAI"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -277,14 +222,9 @@ "maker": 0.003, "taker": 0.007, "fee_url": "https://www.mercadobitcoin.com.br/taxas", - "withdrawal_usdt": 2.0, "note": "Largest Brazilian exchange by volume. Pix fully supported. Volume-based tiers." }, - "supported_fiats": ["BRL"], - "stablecoins": ["USDT", "USDC", "BRZ"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -301,14 +241,9 @@ "maker": 0.003, "taker": 0.005, "fee_url": "https://bitpreco.com/taxas", - "withdrawal_usdt": 2.0, "note": "Brazilian exchange focused on retail. Pix available for BRL." }, - "supported_fiats": ["BRL"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": true, "monitored_by_dolarmap": true, - "cmc_rank": null, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -325,14 +260,9 @@ "maker": 0.004, "taker": 0.006, "fee_url": "https://help.coinbase.com/en/coinbase/trading-and-funding/pricing-and-fees", - "withdrawal_usdt": 0.0, - "note": "Advanced Trade fees. Simple Buy/Sell fees are higher. USDT withdrawals to external wallet may vary." + "note": "Advanced Trade fees. Simple Buy/Sell fees are higher." }, - "supported_fiats": ["USD", "EUR", "GBP"], - "stablecoins": ["USDT", "USDC", "DAI"], - "kyc_required": true, "monitored_by_dolarmap": false, - "cmc_rank": 2, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -349,14 +279,9 @@ "maker": 0.0016, "taker": 0.0026, "fee_url": "https://www.kraken.com/features/fee-schedule", - "withdrawal_usdt": 2.5, "note": "Tiered maker/taker. Pro fees lower than retail." }, - "supported_fiats": ["USD", "EUR", "GBP", "CAD", "AUD", "CHF"], - "stablecoins": ["USDT", "USDC", "DAI"], - "kyc_required": true, "monitored_by_dolarmap": false, - "cmc_rank": 7, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -373,14 +298,9 @@ "maker": 0.002, "taker": 0.002, "fee_url": "https://www.gate.io/fee", - "withdrawal_usdt": 1.0, "note": "GT token holders receive fee discounts." }, - "supported_fiats": ["USD", "EUR"], - "stablecoins": ["USDT", "USDC", "DAI"], - "kyc_required": false, "monitored_by_dolarmap": false, - "cmc_rank": 8, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -397,14 +317,9 @@ "maker": 0.002, "taker": 0.002, "fee_url": "https://www.htx.com/fee/", - "withdrawal_usdt": 1.0, "note": "Formerly Huobi. HT token holders receive discounts." }, - "supported_fiats": ["USD", "EUR"], - "stablecoins": ["USDT", "USDC", "HUSD"], - "kyc_required": true, "monitored_by_dolarmap": false, - "cmc_rank": 12, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -421,14 +336,9 @@ "maker": 0.004, "taker": 0.004, "fee_url": "https://crypto.com/exchange/fees-and-limits", - "withdrawal_usdt": 0.8, "note": "CRO staking reduces fees significantly. Exchange and App are separate platforms." }, - "supported_fiats": ["USD", "EUR", "GBP", "AUD"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": true, "monitored_by_dolarmap": false, - "cmc_rank": 6, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -445,14 +355,9 @@ "maker": 0.001, "taker": 0.001, "fee_url": "https://bingx.com/en-us/support/articles/fees", - "withdrawal_usdt": 1.0, "note": "Copy trading fees may differ from standard spot fees." }, - "supported_fiats": ["USD", "EUR"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": false, "monitored_by_dolarmap": false, - "cmc_rank": 11, "updated_at": "2026-04-01T00:00:00Z" }, { @@ -469,14 +374,9 @@ "maker": 0.001, "taker": 0.001, "fee_url": "https://www.bitmart.com/fee/en", - "withdrawal_usdt": 1.5, "note": "BMX token holders receive fee discounts." }, - "supported_fiats": ["USD"], - "stablecoins": ["USDT", "USDC"], - "kyc_required": false, "monitored_by_dolarmap": false, - "cmc_rank": 13, "updated_at": "2026-04-01T00:00:00Z" } ] diff --git a/docs/MAINTENANCE.md b/docs/MAINTENANCE.md new file mode 100644 index 0000000..d91cdb6 --- /dev/null +++ b/docs/MAINTENANCE.md @@ -0,0 +1,202 @@ +# 📅 Guia de Manutenção Mensal — BitsARK Exchanges API + +> **Tempo estimado:** 10–20 minutos por mês +> **Ferramenta usada:** ChatGPT (plano Premium com navegação web ativada) +> **Quando fazer:** Sempre que receber um PR automático com o assunto `⚠️ Revisão Mensal de Taxas`, ou uma vez por mês de qualquer forma. + +--- + +## 📋 Campos que o ChatGPT deve verificar + +Para cada corretora, o ChatGPT deve buscar e retornar **somente estes campos**: + +| Campo | O que é | Exemplo | +|---|---|---| +| `fees.maker` | Taxa maker em decimal | `0.001` = 0,1% | +| `fees.taker` | Taxa taker em decimal | `0.001` = 0,1% | +| `fees.note` | Observação sobre descontos ou condições | `"BNB discount available (25% off)."` | +| `fees.fee_url` | URL oficial da página de taxas | `"https://binance.com/en/fee/schedule"` | + +> ⚠️ **Não pedir** ao ChatGPT: withdrawal, fiats, stablecoins, KYC, ranking CMC. Esses campos foram removidos do projeto. + +--- + +## 🤖 Passo 1 — Prompt para o ChatGPT + +Abra o ChatGPT (com navegação web ativada) e cole o prompt abaixo **exatamente como está**, substituindo apenas a lista de corretoras se necessário: + +--- + +``` +Você é um assistente de coleta de dados financeiros. +Preciso que você acesse a página oficial de taxas de cada corretora abaixo e me retorne os dados no formato JSON especificado. + +Para cada corretora, acesse a URL indicada e extraia: +- maker: taxa maker em decimal (ex: 0.1% = 0.001) +- taker: taxa taker em decimal +- note: uma frase curta em inglês sobre descontos ou condições especiais (máx 100 caracteres) +- fee_url: a URL exata que você acessou + +Retorne um array JSON com este formato exato, sem nenhum texto antes ou depois: +[ + { + "id": "binance", + "fees": { + "maker": 0.001, + "taker": 0.001, + "note": "BNB discount available (25% off).", + "fee_url": "https://www.binance.com/en/fee/schedule" + } + } +] + +Corretoras para verificar: + +1. id: binance | URL: https://www.binance.com/en/fee/schedule +2. id: okx | URL: https://www.okx.com/fees +3. id: bybit | URL: https://www.bybit.com/en/help-center/article/Trading-Fee-Structure +4. id: bitget | URL: https://www.bitget.com/en/rate/fee +5. id: kucoin | URL: https://www.kucoin.com/vip/privilege +6. id: mexc | URL: https://www.mexc.com/fee +7. id: foxbit | URL: https://foxbit.com.br/taxas +8. id: novadax | URL: https://www.novadax.com.br/taxas-e-limites +9. id: brasil-bitcoin | URL: https://brasilbitcoin.com.br/taxas +10. id: coinext | URL: https://coinext.com.br/taxas +11. id: bitso | URL: https://bitso.com/fees +12. id: mercado-bitcoin | URL: https://www.mercadobitcoin.com.br/taxas +13. id: bitpreco | URL: https://bitpreco.com/taxas +14. id: coinbase | URL: https://help.coinbase.com/en/coinbase/trading-and-funding/pricing-and-fees +15. id: kraken | URL: https://www.kraken.com/features/fee-schedule +16. id: gate-io | URL: https://www.gate.io/fee +17. id: htx | URL: https://www.htx.com/fee/ +18. id: crypto-com | URL: https://crypto.com/exchange/fees-and-limits +19. id: bingx | URL: https://bingx.com/en-us/support/articles/fees +20. id: bitmart | URL: https://www.bitmart.com/fee/en + +Importante: +- Use sempre decimal, não percentual (0.001, não 0.1 nem "0.1%") +- Se maker e taker forem iguais, repita o valor nos dois campos +- Se não conseguir acessar uma URL, inclua o id no JSON com os valores null e adicione uma nota "Could not access page." +- Retorne SOMENTE o array JSON, sem texto adicional +``` + +--- + +## ✅ Passo 2 — Validar o retorno do ChatGPT + +1. O ChatGPT vai retornar um array JSON. **Verifique se:** + - Tem exatamente 20 itens + - Nenhum valor está `null` (se tiver, acesse a URL manualmente) + - Os valores `maker` e `taker` são decimais (entre 0 e 0.1, tipicamente) + +2. **Se algum valor parecer estranho** (ex: `maker: 1.0`), acesse a URL da corretora e confirme. + +--- + +## ✏️ Passo 3 — Atualizar o `exchanges.json` + +1. Abra o arquivo `data/exchanges.json` no VS Code +2. Para cada corretora que teve mudança, atualize os campos `fees.maker`, `fees.taker`, `fees.note` e `fees.fee_url` +3. **Sempre atualize também o campo `updated_at`** com a data de hoje no formato ISO: + ``` + "updated_at": "2026-05-01T00:00:00Z" + ``` +4. Salve o arquivo + +--- + +## 🔍 Passo 4 — Validar o schema localmente + +No terminal do VS Code, rode: + +```bash +npm run validate +``` + +Se aparecer `✓ Validation passed` — está tudo certo. +Se aparecer erro — o ChatGPT provavelmente retornou um formato inválido. Leia a mensagem de erro e corrija o campo indicado. + +--- + +## 🚀 Passo 5 — Fazer o commit e abrir o PR + +### Opção A: Pelo VS Code (recomendado) + +1. Abra o painel **Source Control** (ícone de ramificação na barra lateral esquerda, ou `Ctrl+Shift+G`) +2. Você verá o arquivo `data/exchanges.json` listado em **Changes** +3. Clique no **+** ao lado do arquivo para adicionar ao commit (stage) +4. No campo **Message**, escreva: + ``` + chore: atualiza taxas das corretoras - revisão mensal YYYY-MM + ``` + Substitua `YYYY-MM` pelo ano e mês atual (ex: `2026-05`) +5. Clique em **Commit** (✓) +6. Clique em **Publish Branch** ou **Push** para enviar ao GitHub + +### Opção B: Pelo terminal + +```bash +git add data/exchanges.json +git commit -m "chore: atualiza taxas das corretoras - revisão mensal 2026-05" +git push +``` + +--- + +## 🔀 Passo 6 — Aceitar o PR no GitHub (Merge) + +> Este passo é necessário quando você faz a atualização em um branch separado ou quando o GitHub Actions abriu um PR automático. + +1. Acesse o repositório no GitHub: https://github.com/bitsARK-Labs/exchanges-api +2. Clique na aba **Pull requests** (menu superior) +3. Clique no PR com o título da sua atualização +4. Role a página até o final +5. Verifique se os checks (✓) passaram — especialmente o `validate-schema` +6. Clique no botão verde **Merge pull request** +7. Clique em **Confirm merge** +8. Pronto! O PR foi mergeado na `main`. + +> 💡 **Dica:** Após o merge, você pode clicar em **Delete branch** para manter o repositório limpo. + +--- + +## 🗂️ Lista de todas as corretoras monitoradas + +| # | ID | Nome | País | Pix | Página de Taxas | +|---|---|---|---|---|---| +| 1 | `binance` | Binance | Global | ❌ | [link](https://www.binance.com/en/fee/schedule) | +| 2 | `okx` | OKX | Global | ❌ | [link](https://www.okx.com/fees) | +| 3 | `bybit` | Bybit | Global | ❌ | [link](https://www.bybit.com/en/help-center/article/Trading-Fee-Structure) | +| 4 | `bitget` | Bitget | Global | ❌ | [link](https://www.bitget.com/en/rate/fee) | +| 5 | `kucoin` | KuCoin | Global | ❌ | [link](https://www.kucoin.com/vip/privilege) | +| 6 | `mexc` | MEXC | Global | ❌ | [link](https://www.mexc.com/fee) | +| 7 | `foxbit` | Foxbit | 🇧🇷 Brasil | ✅ | [link](https://foxbit.com.br/taxas) | +| 8 | `novadax` | NovaDAX | 🇧🇷 Brasil | ✅ | [link](https://www.novadax.com.br/taxas-e-limites) | +| 9 | `brasil-bitcoin` | Brasil Bitcoin | 🇧🇷 Brasil | ✅ | [link](https://brasilbitcoin.com.br/taxas) | +| 10 | `coinext` | Coinext | 🇧🇷 Brasil | ✅ | [link](https://coinext.com.br/taxas) | +| 11 | `bitso` | Bitso | 🇧🇷 Brasil | ✅ | [link](https://bitso.com/fees) | +| 12 | `mercado-bitcoin` | Mercado Bitcoin | 🇧🇷 Brasil | ✅ | [link](https://www.mercadobitcoin.com.br/taxas) | +| 13 | `bitpreco` | BitPreço | 🇧🇷 Brasil | ✅ | [link](https://bitpreco.com/taxas) | +| 14 | `coinbase` | Coinbase | Global | ❌ | [link](https://help.coinbase.com/en/coinbase/trading-and-funding/pricing-and-fees) | +| 15 | `kraken` | Kraken | Global | ❌ | [link](https://www.kraken.com/features/fee-schedule) | +| 16 | `gate-io` | Gate.io | Global | ❌ | [link](https://www.gate.io/fee) | +| 17 | `htx` | HTX | Global | ❌ | [link](https://www.htx.com/fee/) | +| 18 | `crypto-com` | Crypto.com | Global | ❌ | [link](https://crypto.com/exchange/fees-and-limits) | +| 19 | `bingx` | BingX | Global | ❌ | [link](https://bingx.com/en-us/support/articles/fees) | +| 20 | `bitmart` | BitMart | Global | ❌ | [link](https://www.bitmart.com/fee/en) | + +--- + +## ❓ Dúvidas frequentes + +**O ChatGPT retornou um valor diferente do que está no JSON — o que faço?** +Se a diferença for pequena (ex: `0.001` vs `0.0010`), é o mesmo valor. Se for realmente diferente, confira a URL manualmente antes de aceitar. + +**Posso pedir para a IA atualizar diretamente o arquivo no GitHub?** +Sim! Você pode pedir para o Perplexity (ou qualquer IA com acesso ao GitHub MCP) fazer isso. Basta dizer: _"Atualize as taxas do exchanges.json com base nestes dados e abra um PR"_ e colar o JSON retornado pelo ChatGPT. + +**O workflow `validate-schema` falhou no PR — o que faço?** +Clique em **Details** ao lado do check vermelho para ver o erro. Geralmente é um campo com tipo errado (string onde deveria ser number). Corrija no `exchanges.json` e faça um novo commit no mesmo branch — o PR atualiza automaticamente. + +**Preciso atualizar todas as 20 corretoras todo mês?** +Não. Só atualize as que tiveram mudança de taxa. Mas **sempre atualize o `updated_at`** de todas as que você verificou, mesmo que os valores não tenham mudado — isso reseta o contador de staleness. diff --git a/schema/exchange.schema.json b/schema/exchange.schema.json index 4a21276..1806ee8 100644 --- a/schema/exchange.schema.json +++ b/schema/exchange.schema.json @@ -16,11 +16,7 @@ "bcb_licensed", "accepts_pix", "fees", - "supported_fiats", - "stablecoins", - "kyc_required", "monitored_by_dolarmap", - "cmc_rank", "updated_at" ], "properties": { @@ -81,13 +77,12 @@ }, "fees": { "type": "object", - "description": "Trading and withdrawal fee information.", + "description": "Trading fee information.", "additionalProperties": false, "required": [ "maker", "taker", "fee_url", - "withdrawal_usdt", "note" ], "properties": { @@ -108,11 +103,6 @@ "description": "Direct URL to the exchange's official fee schedule page.", "format": "uri" }, - "withdrawal_usdt": { - "type": "number", - "description": "Standard withdrawal fee for USDT via TRC-20 network, in USDT.", - "minimum": 0 - }, "note": { "type": "string", "description": "Human-readable note about fees, discounts, or special conditions.", @@ -120,48 +110,10 @@ } } }, - "supported_fiats": { - "type": "array", - "description": "List of supported fiat currencies as ISO 4217 codes.", - "items": { - "type": "string", - "pattern": "^[A-Z]{3}$" - }, - "uniqueItems": true, - "minItems": 1 - }, - "stablecoins": { - "type": "array", - "description": "List of supported stablecoin ticker symbols.", - "items": { - "type": "string", - "minLength": 2, - "maxLength": 16 - }, - "uniqueItems": true, - "minItems": 0 - }, - "kyc_required": { - "type": "boolean", - "description": "Whether KYC (Know Your Customer) identity verification is required to trade." - }, "monitored_by_dolarmap": { "type": "boolean", "description": "Whether this exchange is actively monitored by the DolarMap service." }, - "cmc_rank": { - "oneOf": [ - { - "type": "integer", - "description": "CoinMarketCap exchange rank.", - "minimum": 1 - }, - { - "type": "null", - "description": "Null when the exchange does not appear in CMC rankings." - } - ] - }, "updated_at": { "type": "string", "description": "ISO 8601 datetime when this entry was last manually verified.", diff --git a/scripts/check-fees.js b/scripts/check-fees.js index 8ae7094..5867c1e 100644 --- a/scripts/check-fees.js +++ b/scripts/check-fees.js @@ -1,129 +1,117 @@ #!/usr/bin/env node /** * scripts/check-fees.js - * Flags exchanges where updated_at is older than 7 days. - * Prints name, last updated date, fee_url, and current maker/taker for each. - * Writes a .fee-check-summary.txt for use as a GitHub Actions PR body. + * + * Verifica quais corretoras estão com dados de taxas desatualizados. + * Considera stale qualquer registro com updated_at > 30 dias atrás. + * + * Uso: node scripts/check-fees.js + * + * Saída: + * - Console com lista de corretoras que precisam revisão + * - .fee-check-summary.txt com o relatório formatado (usado pelo GitHub Actions) + * + * O script nunca falha com exit(1) — staleness não é erro crítico. + * O GitHub Actions lê o summary para decidir se abre um PR de alerta. */ "use strict"; -const fs = require("fs"); +const fs = require("fs"); const path = require("path"); -const ROOT = path.resolve(__dirname, ".."); -const DATA_PATH = path.join(ROOT, "data", "exchanges.json"); +const ROOT = path.resolve(__dirname, ".."); +const DATA_PATH = path.join(ROOT, "data", "exchanges.json"); const SUMMARY_PATH = path.join(ROOT, ".fee-check-summary.txt"); +const STALE_DAYS = 30; +const MS_PER_DAY = 1000 * 60 * 60 * 24; -const STALE_DAYS = 7; -const MS_PER_DAY = 1000 * 60 * 60 * 24; - -// ─── Load data ───────────────────────────────────────────────────────────────── +// ─── Carrega dados ───────────────────────────────────────────────────────────── let exchanges; try { exchanges = JSON.parse(fs.readFileSync(DATA_PATH, "utf8")); } catch (err) { - console.error(`✗ Failed to load data/exchanges.json: ${err.message}`); + console.error(`✗ Falha ao carregar data/exchanges.json: ${err.message}`); process.exit(1); } if (!Array.isArray(exchanges)) { - console.error("✗ data/exchanges.json must be a JSON array."); + console.error("✗ data/exchanges.json deve ser um array JSON."); process.exit(1); } -// ─── Check staleness ─────────────────────────────────────────────────────────── -const now = Date.now(); -const staleExchanges = []; +// ─── Verifica staleness ──────────────────────────────────────────────────────── +const now = Date.now(); +const stale = []; -for (const exchange of exchanges) { - if (!exchange.updated_at) { - staleExchanges.push({ ...exchange, daysOld: Infinity, reason: "missing updated_at" }); +for (const ex of exchanges) { + if (!ex.updated_at) { + stale.push({ ...ex, daysOld: Infinity, reason: "updated_at ausente" }); continue; } - - const updatedAt = new Date(exchange.updated_at); + const updatedAt = new Date(ex.updated_at); if (isNaN(updatedAt.getTime())) { - staleExchanges.push({ ...exchange, daysOld: Infinity, reason: "invalid updated_at" }); + stale.push({ ...ex, daysOld: Infinity, reason: "updated_at inválido" }); continue; } - const daysOld = (now - updatedAt.getTime()) / MS_PER_DAY; if (daysOld > STALE_DAYS) { - staleExchanges.push({ ...exchange, daysOld: Math.round(daysOld) }); + stale.push({ ...ex, daysOld: Math.round(daysOld) }); } } // ─── Console output ──────────────────────────────────────────────────────────── -if (staleExchanges.length === 0) { - console.log( - `✓ All ${exchanges.length} exchange(s) have been updated within the last ${STALE_DAYS} days.` - ); +if (stale.length === 0) { + console.log(`✓ Todas as ${exchanges.length} corretoras foram atualizadas nos últimos ${STALE_DAYS} dias.`); } else { - console.log( - `⚠ ${staleExchanges.length} exchange(s) have stale fee data (older than ${STALE_DAYS} days):\n` - ); - for (const ex of staleExchanges) { - const age = - ex.daysOld === Infinity - ? `(${ex.reason})` - : `${ex.daysOld} day(s) old`; + console.log(`⚠ ${stale.length} corretora(s) com dados desatualizados (mais de ${STALE_DAYS} dias):\n`); + for (const ex of stale) { + const age = ex.daysOld === Infinity ? `(${ex.reason})` : `${ex.daysOld} dias`; console.log(` • ${ex.name}`); - console.log(` Last updated : ${ex.updated_at ?? "N/A"} — ${age}`); - console.log(` Maker / Taker: ${ex.fees?.maker ?? "?"} / ${ex.fees?.taker ?? "?"}`); - console.log(` Fee URL : ${ex.fees?.fee_url ?? "N/A"}`); + console.log(` Última atualização : ${ex.updated_at ?? "N/A"} — ${age}`); + console.log(` Maker / Taker : ${ex.fees?.maker ?? "?"} / ${ex.fees?.taker ?? "?"}`); + console.log(` Página de taxas : ${ex.fees?.fee_url ?? "N/A"}`); console.log(); } } -// ─── Write .fee-check-summary.txt ───────────────────────────────────────────── +// ─── Gera .fee-check-summary.txt ────────────────────────────────────────────── const lines = []; -lines.push("## ⚠️ Weekly Fee Check — Manual Review Required"); +lines.push("## ⚠️ Revisão Mensal de Taxas — Ação Necessária"); lines.push(""); -lines.push( - `> Generated on ${new Date().toISOString()} by \`scripts/check-fees.js\`.` -); +lines.push(`> Gerado em ${new Date().toISOString()} por \`scripts/check-fees.js\`.`); lines.push(""); -if (staleExchanges.length === 0) { - lines.push( - `✅ All ${exchanges.length} exchange(s) have been updated within the last ${STALE_DAYS} days. No action needed.` - ); +if (stale.length === 0) { + lines.push(`✅ Todas as ${exchanges.length} corretoras foram verificadas nos últimos ${STALE_DAYS} dias. Nenhuma ação necessária.`); } else { - lines.push( - `**${staleExchanges.length} exchange(s) have fee data older than ${STALE_DAYS} days.** ` + - `Please verify the fees at each exchange's official fee page and update \`data/exchanges.json\` accordingly.` - ); + lines.push(`**${stale.length} corretora(s) precisam de revisão** (dados com mais de ${STALE_DAYS} dias).`); + lines.push(""); + lines.push("Siga o passo a passo em [docs/MAINTENANCE.md](docs/MAINTENANCE.md) para atualizar via ChatGPT."); lines.push(""); - lines.push("| Exchange | Last Updated | Days Old | Maker | Taker | Fee URL |"); + lines.push("| Corretora | Última Atualização | Dias | Maker | Taker | Página de Taxas |"); lines.push("| --- | --- | --- | --- | --- | --- |"); - for (const ex of staleExchanges) { - const age = ex.daysOld === Infinity ? `∞ (${ex.reason})` : `${ex.daysOld}`; + for (const ex of stale) { + const age = ex.daysOld === Infinity ? `∞ (${ex.reason})` : `${ex.daysOld}`; const maker = ex.fees?.maker !== undefined ? (ex.fees.maker * 100).toFixed(3) + "%" : "—"; const taker = ex.fees?.taker !== undefined ? (ex.fees.taker * 100).toFixed(3) + "%" : "—"; - const feeUrl = ex.fees?.fee_url ?? "—"; - lines.push( - `| ${ex.name} | ${ex.updated_at ?? "N/A"} | ${age} | ${maker} | ${taker} | [link](${feeUrl}) |` - ); + lines.push(`| ${ex.name} | ${ex.updated_at ?? "N/A"} | ${age} | ${maker} | ${taker} | [link](${ex.fees?.fee_url ?? "#"}) |`); } lines.push(""); lines.push("### Checklist"); lines.push(""); - for (const ex of staleExchanges) { - lines.push(`- [ ] Verify and update **${ex.name}** — [Fee page](${ex.fees?.fee_url ?? "#"})`); + for (const ex of stale) { + lines.push(`- [ ] **${ex.name}** — [Abrir página de taxas](${ex.fees?.fee_url ?? "#"})`); } } lines.push(""); lines.push("---"); -lines.push("_This PR was created automatically by the weekly fee-check workflow._"); +lines.push("_Gerado automaticamente pelo workflow mensal. Siga [docs/MAINTENANCE.md](docs/MAINTENANCE.md) para completar a revisão._"); -const summary = lines.join("\n"); -fs.writeFileSync(SUMMARY_PATH, summary, "utf8"); -console.log(`✓ Summary written to ${SUMMARY_PATH}`); +fs.writeFileSync(SUMMARY_PATH, lines.join("\n"), "utf8"); +console.log(`✓ Relatório salvo em ${SUMMARY_PATH}`); -// Exit 0 always — GitHub Action reads .fee-check-summary.txt to decide -// whether to open a PR; stale fees are not a hard failure. process.exit(0);