Skip to content

Universes ​

How do you trade "the top 50 coins" without picking today's top 50 with hindsight? Ask request.universe for the list. On every bar it returns the coins as CoinMarketCap ranked them on the last date known by that bar's close, so a coin that later collapsed is in the list on the days it ranked, and one that rose later isn't there yet.

ameno
indicator("Coins over $1B")
// Up to 100 coins worth $1B or more, best rank first, stablecoins left
// out, as CoinMarketCap ranked them on the last date this bar can know
array<string> members = request.universe(100,
  exclude_tags = "stablecoin", min_mcap = 1e9)
plot(array.size(members), "Coins over $1B")

Universes are crypto only: they list CoinMarketCap's coins. Run a script that reads one on a daily chart of a coin that trades every day, such as CRYPTO:BTCUSD. A stock's chart has no weekend bars, so the script would decide nothing on weekends.

What it returns ​

request.universe returns an array<string> of symbols, best rank first. Each symbol is a CMC: series you can read like any other: pass it to request.security, or hand the whole array to request.each to run one expression per member.

ParameterDefaultKeeps a coin when
top50It is among the first top that pass the other filters, in rank order.
exclude_tagsnoneIt has none of these tags, comma-separated.
include_tagsnoneIt has at least one of these tags.
min_mcapnoneIts market cap that day, in US dollars, is at least this.
min_volumenoneIts 24-hour volume that day, in US dollars, is at least this.
min_age_daysnoneCoinMarketCap listed it at least this many days before.
hindsightfalseRefused. See Tags.

Rank is CoinMarketCap's own rank, not a sort by market cap. Each filter reads that date's own values. Before the first date with a ranking, the array is empty.

Pass literals or inputs. The chart fetches every member's prices before the first run, using the arguments it can read in your text. An argument worked out on each bar can't be read there, so the chart learns its members only from a first run, fetches their prices, and runs the script again.

The date rule ​

A date's ranking is the market at that date's close, and it is known when the date ends, at 00:00 UTC the next day. So a daily bar of date D reads D's ranking, since the bar closes at the same moment. An hourly bar during D + 1 reads D's ranking too, because D + 1's isn't known until it ends.

The same rule holds for every CMC: series: each daily row is known at the end of its date.

Reading a member ​

Each member is a CMC: symbol, and a few suffixes read other columns of the same coin:

SymbolReads
CMC:bitcoinThe coin's daily price bars.
CMC:bitcoin:MCAPIts market cap.
CMC:bitcoin:VOLIts 24-hour volume.
CMC:bitcoin:RANKIts rank.
CMC:TOTALThe whole market's cap.
CMC:BTC.D, CMC:ETH.DBitcoin's and Ether's share of it, in percent.
pine
//@version=6
indicator("Bitcoin's rank and share")
// gaps_on: a day with no row reads na instead of the day before
float rank = request.security("CMC:bitcoin:RANK", "1D", close,
  gaps = barmerge.gaps_on)
float share = request.security("CMC:BTC.D", "1D", close)
plot(rank, "Rank")
plot(share, "Dominance (%)")

Reading a CMC: series is plain Pine, so the script above runs in either dialect; only request.universe is AmenoScript. A CMC: series is daily at any chart timeframe. Some coins have only a daily close and no high or low, so their bars have no wicks. A coin outside the top 1,000 has no rank that day, so read :RANK with gaps = barmerge.gaps_on.

Slugs ​

A member's symbol is built from CoinMarketCap's slug, the name in its web address, such as bitcoin. CoinMarketCap reuses slugs: aave belonged to LEND until AAVE took it in 2020. When a slug now names another coin, the older coin's symbol is CMC:id-<number> instead, so its rows never read the newer coin's prices. The array always holds the right symbol, so read members through it rather than typing slugs.

Tags ​

exclude_tags and include_tags read each coin's tags as CoinMarketCap had them on that date.

Old dates have few tags

Before 2020-06-26 the only tag is mineable, and USDT is tagged stablecoin only from 2020-08-25. So exclude_tags = "stablecoin" keeps every stablecoin in the list before then. If your test reaches that far back, filter them out by symbol as well: array.filter(members, s => s != "CMC:tether").

hindsight = true would read each coin's newest tags on every date, which nobody had then, so it is always refused.

Thin and early data ​

CoinMarketCap recorded no volume through 2013-12-26, so min_volume empties the list on those days. Before about 2018, many ranked coins traded little, and a price printed on a few thousand dollars of volume is not a price you could have traded at. min_volume keeps only members that traded enough that day.

Wrapped, staked and unverified tokens are ranked past 200 from 2020-09-23, and sometimes left out, so they rarely appear in a top 50. That is CoinMarketCap's choice, and the list keeps it.

Holes in the list ​

CoinMarketCap's ranking leaves coins out on some dates. When a date is missing a coin the list would reach, the list keeps the newest complete list from before that date, and holds it until the ranking is whole again.

How long a member has been in ​

A coin usually joins the list on the day of the move that lifted its rank, so a rule that acts on new members acts right after that move. To act only on members that have stayed a while, count the bars each one has been in the list:

ameno
indicator("Members past a wait")
int wait = input.int(30, "Bars in the list before acting", minval = 1)
array<string> members = request.universe(20,
  exclude_tags = "stablecoin")
// Bars each member has been in the list without a break
var map<string, int> inList = map.new<string, int>()
map<string, int> now = map.new<string, int>()
for s in members
    now.put(s, nz(inList.get(s)) + 1)
inList := now
array<string> settled = array.filter(members, s => inList.get(s) >= wait)
plot(array.size(settled), "Settled members")
plot(array.size(members), "Members")

A member that leaves and comes back starts again at one. Use settled wherever the script would use members.

Traps ​

  • A list that isn't loaded is empty. When the membership can't be fetched, request.universe returns an empty array and the run warns. Guard with array.size(members) > 0 before you act on it.
  • Members leave. A coin that drops out of the list is still readable while it has rows, so a position you hold in it keeps its price.
  • A coin joins on its way up. See How long a member has been in.

See also ​

AmenoScript is compatible with Pine Script v6. Pine Script is a trademark of TradingView, Inc., which has no part in AmenoScript.