"""Which coins to buy inside a basket BUY, v3 (4 Oct 2026). Tested in 3 sessions on 66, 102 and 210 basket buys plus 85 on
never-used baskets: no pick rule beats random coins of the same basket; the edge is the basket timing. What does help:
own several coins in equal amounts (smallest losses), skip coins up 30%+ on the buy day (beat the basket only 19 to 32%),
skip the 7 day leader (a lottery). So: up to 50 liquid coins of the category, drop those two, take the 5 biggest."""
import json, datetime as dt, urllib.request, time
H_URL = "https://api.coinmarketcap.com/data-api/v3/cryptocurrency/listings/historical?date={}&limit=5000&start={}&convertId=2781"
UA = {"User-Agent": "Mozilla/5.0"}
BAD = ("stablecoin", "wrapped", "liquid-staking", "tokenized-gold", "bridged", "tokenized-stock", "tokenized", "rehypothecated",
       "xstocks", "ondo-stocks")
POOL_N, MIN_VOL, MIN_AGE, N_PICK = 50, 100000, 8, 5
_CACHE = {}

def _get(url):
    for t in range(3):
        try: return json.load(urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=90))["data"]
        except Exception as e:
            err = e; time.sleep(3 * (t + 1))
    raise err

def rows(day):
    """compact CoinMarketCap listing for a UTC day: id, sym, name, chain, address, rank, price, vol, bad, tags, dateAdded"""
    if day in _CACHE: return _CACHE[day]
    data = _get(H_URL.format(day, 1))
    if len(data) >= 5000: data += _get(H_URL.format(day, 5001))
    out = []
    for c in data:
        p = c.get("platform") or {}; tg = c.get("tags") or []; q = c["quotes"][0]
        out.append(dict(id=c["id"], sym=c["symbol"], name=c["name"], chain=p.get("name"), addr=p.get("token_address"), rank=c.get("cmcRank") or 99999,
                        price=q.get("price") or 0, vol=q.get("volume24h") or 0, bad=any(any(b in x for b in BAD) for x in tg), tags=tg,
                        added=(c.get("dateAdded") or "")[:10]))
    _CACHE[day] = out
    return out

def _rank(vals):
    known = sorted(v for v in vals if v is not None); n = len(known)
    return [(known.index(v) / (n - 1) if n > 1 else .5) if v is not None else .5 for v in vals]

def pick(basket, day, exclude=()):
    """basket: (key, chain, name, rule(tags), what) from baskets_def.CMC_BASKETS. day: the BUY close (UTC label day)."""
    key, chain, _, rule, _ = basket
    now = rows(day)
    lim = (dt.date.fromisoformat(day) - dt.timedelta(MIN_AGE)).isoformat()
    pool = [r for r in now if r["chain"] == chain and not r["bad"] and r["sym"] not in exclude and rule(r["tags"]) and r["vol"] >= MIN_VOL
            and r["price"] > 0 and r["added"] and r["added"] <= lim]
    pool.sort(key=lambda r: r["rank"]); pool = pool[:POOL_N]
    if len(pool) < 3: return []
    D = dt.date.fromisoformat(day)
    p1 = {x["id"]: x["price"] for x in rows((D - dt.timedelta(1)).isoformat()) if x["price"]}
    p7 = {x["id"]: x["price"] for x in rows((D - dt.timedelta(7)).isoformat()) if x["price"]}
    r1 = {r["id"]: (r["price"] / p1[r["id"]] - 1) if p1.get(r["id"]) else None for r in pool}
    r7 = {r["id"]: (r["price"] / p7[r["id"]] - 1) if p7.get(r["id"]) else None for r in pool}
    lead = max((r for r in pool if r7[r["id"]] is not None), key=lambda r: r7[r["id"]], default=None)
    keep = [r for r in pool if not (r1[r["id"]] is not None and r1[r["id"]] >= 0.30) and r is not lead]
    best = keep[:N_PICK]
    return [dict(sym=r["sym"], name=r["name"], addr=r["addr"], id=r["id"], r1=None if r1[r["id"]] is None else round(r1[r["id"]] * 100),
                 r7=None if r7[r["id"]] is None else round(r7[r["id"]] * 100), pool=len(pool), skipped=[x["sym"] for x in pool[:N_PICK + 3] if x not in keep])
            for r in best]
