레시피: 거래소의 일별 스테이블코인 흐름 추적하기

레시피 · 한 거래소의 90일 시계열을 읽어 순유입과 잔액 변화를 보고하고, 가장 많이 움직인 자체 지갑을 나열하는 일일 작업

  • API 가이드
  • 8분
  • 고급
  • 개발자
  • entities → daily → addresses

ACHIVX Academy 원문 (영어) ↗

하루에 작업 하나, 조회 세 번입니다. 네 가지 기간, 네트워크별 잔액, as_of_day를 위한 /v1/entities/{slug}. 시계열을 위한 /v1/entities/{slug}/daily — 프로그램은 여기서 최근 7일의 순유입을 출력하고, 90일 기준 표준편차 2배를 벗어난 날을 표시합니다. 30일 회전량 순으로 정렬된 자체 지갑을 위한 /v1/entities/{slug}/addresses?chain=&group=own. 모든 수치는 마감된 UTC 날짜(as_of_day)에서 읽으므로, 작업은 야간 빌드(built_at, 약 08:00 UTC) 후 한 번 실행하며, 보고하는 모든 기간 옆에 complete 플래그를 유지합니다.

curl

#!/bin/sh
# flows.sh <slug>  — last 7 days of net flow and the 30d balance change
B=https://stablescan.achivx.com; SLUG=$1
curl -sf "$B/v1/entities/$SLUG" | jq -r '
  "\(.name) as of \(.as_of_day) (built \(.built_at))",
  (.flows[] | "\(.window): in $\(.inflow_usd6/1e9|round)B out $\(.outflow_usd6/1e9|round)B net $\(.netflow_usd6/1e9*10|round/10)B \(if .complete then "" else "(\(.days_measured)/\(.window_days) days)" end)"),
  (.networks[] | "  \(.network): balance $\(.balance_usd6/1e9|round)B (prev $\(.prev_balance_usd6/1e9|round)B on \(.prev_as_of_day))")'
curl -sf "$B/v1/entities/$SLUG/daily" | jq -r '.daily[-7:][] | "\(.day) net $\((.inflow_usd6 - .outflow_usd6)/1e6|round)"'

Python

import json, statistics, sys, urllib.parse, urllib.request

BASE = "https://stablescan.achivx.com"

def scan(path, **params):
    url = BASE + path + ("?" + urllib.parse.urlencode(params) if params else "")
    with urllib.request.urlopen(urllib.request.Request(url, headers={"Accept": "application/json", "User-Agent": "flows/1.0"}), timeout=30) as r:
        return json.load(r)

def report(slug):
    ex = scan(f"/v1/entities/{slug}")
    print(f'{ex["name"]} — as of {ex["as_of_day"]}, built {ex["built_at"]}, {ex["addresses"]["total"]:,} addresses ({ex["addresses"]["own"]} own)')
    for f in ex["flows"]:
        note = "" if f["complete"] else f' ({f["days_measured"]} of {f["window_days"]} days measured)'
        print(f'  {f["window"]:>3}: in ${f["inflow_usd6"]/1e9:7.1f}B  out ${f["outflow_usd6"]/1e9:7.1f}B  net ${f["netflow_usd6"]/1e9:+6.1f}B{note}')
    for n in ex["networks"]:
        delta = (n["balance_usd6"] - n["prev_balance_usd6"]) / 1e6
        print(f'  {n["network"]:10} balance ${n["balance_usd6"]/1e9:6.1f}B ({delta:+,.0f} vs {n["prev_as_of_day"]}; {n["balances_read"]}/{n["balances_total"]} wallets read)')
    daily = scan(f"/v1/entities/{slug}/daily")["daily"]
    nets = [(d["day"], (d["inflow_usd6"] - d["outflow_usd6"]) / 1e6) for d in daily]
    mu, sd = statistics.mean(v for _, v in nets), statistics.pstdev(v for _, v in nets)
    print(f"  daily net flow over {len(nets)} measured days: mean ${mu:,.0f}, sd ${sd:,.0f}")
    for day, v in nets[-7:]:
        flag = "  <-- outside 2 sd" if sd and abs(v - mu) > 2 * sd else ""
        print(f"    {day} ${v:+,.0f}{flag}")
    own = scan(f"/v1/entities/{slug}/addresses", chain=1, group="own", limit=5)["items"]
    print(f'  busiest own wallets on Ethereum (of {scan(f"/v1/entities/{slug}/addresses", chain=1, group="own", limit=1)["total"]}):')
    for w in own:
        print(f'    {w["address"]} in ${w["in_30d_usd6"]/1e6:,.0f} out ${w["out_30d_usd6"]/1e6:,.0f} balance ${(w["balance_usd6"] or 0)/1e6:,.0f}')

if __name__ == "__main__":
    report(sys.argv[1] if len(sys.argv) > 1 else "binance")

Go

package main

import (
	"encoding/json"
	"fmt"
	"math"
	"net/http"
	"os"
)

const base = "https://stablescan.achivx.com"

type flow struct {
	Window       string `json:"window"`
	Complete     bool   `json:"complete"`
	DaysMeasured int    `json:"days_measured"`
	WindowDays   int    `json:"window_days"`
	InflowUSD6   int64  `json:"inflow_usd6"`
	OutflowUSD6  int64  `json:"outflow_usd6"`
	NetflowUSD6  int64  `json:"netflow_usd6"`
}

type entity struct {
	Name     string `json:"name"`
	AsOfDay  string `json:"as_of_day"`
	BuiltAt  string `json:"built_at"`
	Flows    []flow `json:"flows"`
	Networks []struct {
		Network        string `json:"network"`
		BalanceUSD6    int64  `json:"balance_usd6"`
		PrevBalance    int64  `json:"prev_balance_usd6"`
		PrevAsOfDay    string `json:"prev_as_of_day"`
	} `json:"networks"`
}

type daily struct {
	Daily []struct {
		Day         string `json:"day"`
		InflowUSD6  int64  `json:"inflow_usd6"`
		OutflowUSD6 int64  `json:"outflow_usd6"`
	} `json:"daily"`
}

func get(path string, out any) error {
	res, err := http.Get(base + path)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return fmt.Errorf("%s: HTTP %d", path, res.StatusCode)
	}
	return json.NewDecoder(res.Body).Decode(out)
}

func main() {
	slug := "binance"
	if len(os.Args) > 1 {
		slug = os.Args[1]
	}
	var ex entity
	if err := get("/v1/entities/"+slug, &ex); err != nil {
		panic(err)
	}
	fmt.Printf("%s — as of %s, built %s\n", ex.Name, ex.AsOfDay, ex.BuiltAt)
	for _, f := range ex.Flows {
		note := ""
		if !f.Complete {
			note = fmt.Sprintf(" (%d of %d days measured)", f.DaysMeasured, f.WindowDays)
		}
		fmt.Printf("  %3s: in $%.1fB out $%.1fB net $%+.1fB%s\n", f.Window, float64(f.InflowUSD6)/1e15, float64(f.OutflowUSD6)/1e15, float64(f.NetflowUSD6)/1e15, note)
	}
	for _, n := range ex.Networks {
		fmt.Printf("  %-10s balance $%.1fB (%+.0f vs %s)\n", n.Network, float64(n.BalanceUSD6)/1e15, float64(n.BalanceUSD6-n.PrevBalance)/1e6, n.PrevAsOfDay)
	}
	var d daily
	if err := get("/v1/entities/"+slug+"/daily", &d); err != nil {
		panic(err)
	}
	nets := make([]float64, len(d.Daily))
	var sum float64
	for i, row := range d.Daily {
		nets[i] = float64(row.InflowUSD6-row.OutflowUSD6) / 1e6
		sum += nets[i]
	}
	mean := sum / float64(len(nets))
	var sq float64
	for _, v := range nets {
		sq += (v - mean) * (v - mean)
	}
	sd := math.Sqrt(sq / float64(len(nets)))
	for i := max(0, len(nets)-7); i < len(nets); i++ {
		flag := ""
		if sd > 0 && math.Abs(nets[i]-mean) > 2*sd {
			flag = "  <-- outside 2 sd"
		}
		fmt.Printf("    %s $%+.0f%s\n", d.Daily[i].Day, nets[i], flag)
	}
}

/1e15는 usd6를 한 번에 10억 단위로 바꿉니다(달러로 10^6, 10억 단위로 10^9).

TypeScript

const BASE = "https://stablescan.achivx.com";
type Flow = { window: string; complete: boolean; days_measured: number; window_days: number; inflow_usd6: number; outflow_usd6: number; netflow_usd6: number };
type Entity = { name: string; as_of_day: string; built_at: string; addresses: { total: number; own: number }; flows: Flow[]; networks: { network: string; balance_usd6: number; prev_balance_usd6: number; prev_as_of_day: string }[] };
type Daily = { daily: { day: string; inflow_usd6: number; outflow_usd6: number }[] };

const scan = async <T>(path: string, params: Record<string, string | number> = {}): Promise<T> => {
  const q = new URLSearchParams(Object.entries(params).map(([k, v]) => [k, String(v)])).toString();
  const res = await fetch(`${BASE}${path}${q ? `?${q}` : ""}`, { headers: { accept: "application/json" } });
  if (!res.ok) throw new Error(`${path}: HTTP ${res.status}`);
  return (await res.json()) as T;
};
const B = (usd6: number) => `$${(usd6 / 1e15).toFixed(1)}B`;

export async function report(slug = "binance") {
  const ex = await scan<Entity>(`/v1/entities/${slug}`);
  console.log(`${ex.name} — as of ${ex.as_of_day}, built ${ex.built_at}, ${ex.addresses.total.toLocaleString()} addresses (${ex.addresses.own} own)`);
  for (const f of ex.flows) console.log(`  ${f.window.padStart(3)}: in ${B(f.inflow_usd6)} out ${B(f.outflow_usd6)} net ${B(f.netflow_usd6)}${f.complete ? "" : ` (${f.days_measured} of ${f.window_days} days measured)`}`);
  for (const n of ex.networks) console.log(`  ${n.network.padEnd(10)} balance ${B(n.balance_usd6)} (${((n.balance_usd6 - n.prev_balance_usd6) / 1e6).toLocaleString()} vs ${n.prev_as_of_day})`);
  const nets = (await scan<Daily>(`/v1/entities/${slug}/daily`)).daily.map((d) => [d.day, (d.inflow_usd6 - d.outflow_usd6) / 1e6] as const);
  const mean = nets.reduce((s, [, v]) => s + v, 0) / nets.length;
  const sd = Math.sqrt(nets.reduce((s, [, v]) => s + (v - mean) ** 2, 0) / nets.length);
  for (const [day, v] of nets.slice(-7)) console.log(`    ${day} $${v.toLocaleString(undefined, { maximumFractionDigits: 0 })}${sd && Math.abs(v - mean) > 2 * sd ? "  <-- outside 2 sd" : ""}`);
}

await report(process.argv[2]);

2026-10-24의 보고 내용

2026-10-23 기준 바이낸스(Binance): 24시간 유입 $2.7B, 유출 $2.8B, 순 −$0.05B. 7일 유입 $16.2B, 유출 $16.7B. 30일 유입 $65.9B, 유출 $67.6B (30일 중 28일 측정). 이더리움(Ethereum) 잔액 $33.7B, 당일 $74M 증가. 2026-09-26 이후 일별 행 28개, 표준편차 2배를 벗어난 날 없음. 30일 기간은 시계열이 보여 주는 이유로 불완전하다고 표시됩니다. 가장 최근 이틀이 아직 모든 네트워크에서 측정되지 않았기 때문이며, 프로그램은 그 표시를 반올림해 없애지 않고 그대로 전달합니다.

일정과 캐시

엔티티는 마감된 UTC 날짜를 바탕으로 하루에 한 번, 약 08:00 UTC(built_at)에 생성됩니다. Cache-Control: max-age=600. 빌드 후 작업을 한 번 실행하고, 실행 시각이 아니라 as_of_day로 날짜별 비교를 하십시오.

Stablecoin scanner ACHIVX에서

출처

더 알아보기

레시피 · entities → daily → addresses · 2026-10-24 작성, openapi.json, agents.md 및 당일 실제 호출 기반. 구조는 인용한 것이며, 수치는 날짜 기준이고 인덱스에 따라 바뀝니다.

이 페이지는 ACHIVX Academy 영어 원문을 한국어로 옮긴 것입니다. 번역과 원문이 다르면 영어 원문이 기준입니다.