Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Cardano Pool Ranking in Daedalus

Cardano Pool Ranking in Daedalus

DISCLAIMER: The following example is based on the Chapter 4.3 and 5.6 in latest design spec and the Ranking Specification. But it seems that the ranking mechanism specified in the Design Specification, and explained below, is not fully implemented and/or enabled or I missed some of the details of the spec (more realistic assumption). Also, I have not checked what is the difference between the design doc and the current implementation of the haskell code to check if there is any difference, or I misinterpreted some of the both sources (design doc, haskell code). Anyway, I will check the code when I have some spare time.

UPDATED 09/23/2020

Layman explanation

The pool ranking in wallet is based on the stake the user intend to delegate and the expected non-myopic (member) reward after that delegated stake.

The steps for wallet ranking are the following:

  1. Initial (non-wallet) ranking - sort (rank) the pools by their expected (as if they would be in the 150 saturated pools) rewards called desirability (stake is not considered here at all).
  2. Pool Selection - favors the first 150 pools of this pool list that contains >1000 pools (stakes are only considered for these 150 pools).
  3. The non-myopic pool stake assignment - those who are in the lucky 150 will have at least a saturation level (z0) stake (or their live stake if they oversaturated) assigned, the others only their pledge.
  4. The non-myopic member reward calculation - and
  5. The wallet ranking - which is based on sorted non-myopic member reward based the stake the user wants to delegate (100K in this example).

Initial ranking

In the first stage the pools are ranked by the desirability function, which is simply based on the expected pool rewards as if the pools are fully saturated (what a user could expect from the pool when it is fully saturated).

Pool Rank Desirability Epoch Reward hit rate/performance pledge (ADA) cost (ADA) margin Stake
P2P 1 126,968.72 32,000,000 77.50% 7,000,000 340 0% 64,500,000
1PCT 328 125,573.28 32,000,000 77.50% 119,900 340 1% 190,330,000
BCSH 309 124,445.63 32,000,000 77.50% 7,900,000 340 2% 36,760,000
SAND 537 124,319.32 32,000,000 77.50% 918,900 340 2% 3,410,000
UNDR 820 123,064.90 32,000,000 77.50% 1,000,000 340 2.99% 23,800,000
DIGI 812 123,044.07 32,000,000 77.50% 545,700 340 3% 208,600,000
ZZZ 1,042 120,499.19 32,000,000 77.50% 95,400 340 5% 186,790,000
THREE 927 0.00 32,000,000 0.00% 33,333 343 3.33% 702,000

Use similar commands to gather the initial ranking from the ledger state

export LEDGER_STATE="ledger_state-$(date +%Y%m%d%H%M%S).txt"
cardano-cli shelley query ledger-state --mainnet > $LEDGER_STATE
jq -r '.esLState._delegationState._pstate._pParams | .[] | [ .publicKey, (32000000/(1+0.3) * (1/150 + .pledge/1000000/31000000000  * 0.3) - .cost/1000000)*(1 - .margin) ] | @tsv' $LEDGER_STATE |\
sort -rnk 2 | grep -n "."

Pool Selection

From the initial rank above (ordered list of pools based on the reward), the first k number (150 of the >1000) of pools are selected from these pools.

The non-myopic pool stake assignment

During the pool selection process, the The

  • selected ones can use their stake if they oversaturated (or the saturation level ~200m) as non-myopic stake, while
  • the rest only can use their pledge as non-myopic stake for further caclulation.

As you can see the table below that only P2P could keep his delegated stake as non-myopic pool stake the others can use their pledge only.

Pool Rank pledge (ADA) Stake NM Stake
P2P 1 7,000,000 64,500,000 206,666,667
1PCT 328 119,900 190,330,000 119,900
BCSH 309 7,900,000 36,760,000 7,900,000
SAND 537 918,900 3,410,000 918,900
UNDR 820 1,000,000 23,800,000 1,000,000
DIGI 812 545,700 208,600,000 545,700
ZZZ 1,042 95,400 186,790,000 95,400
THREE 927 33,333 702,000 33,333

The non-myopic member reward calculation

The non-myopic member reward calculation uses the normal reward calculation /w the pool parameters, apparent performance (assuming 100% now, the hit rate estimates only used in the desirability function) and non-myopic pool stake. As you can see only the BCSH and P2P are which have any non-myopic member reward and therefore be favored in the wallet raking (see details below), meaning the others would inevitabely die out (my pool is included).

Pool Wallet Rank NM Stake NM pool reward NM memeber Reward NM Mem Pool Pledge Pool Stake Epoch Reward
P2P 92 206,000,000 212645.16 103.06 7.52% 7,000,000 64,500,000 32,000,000
BCSH 151 7,900,000 8154.84 97.93 7.15% 7,900,000 36,760,000 32,000,000
UNDR 133 1,000,000 1032.26 67.84 4.95% 1,000,000 23,800,000 32,000,000
SAND 206 918,900 948.54 64.90 4.74% 918,900 3,410,000 32,000,000
DIGI 31 545,700 563.30 39.69 2.90% 545,700 208,600,000 32,000,000
1PCT 9 119,900 123.77 0 0.00% 119,900 190,330,000 32,000,000
ZZZ 58 95,400 98.48 0 0.00% 95,400 186,790,000 32,000,000
THREE 209 33,333 34.41 0 0.00% 33,333 789,000 32,000,000

The Wallet Ranking

The wallet rank, besed on the desig nspecification, should be based on the expected non-myopic pool member reward calculated for the stake she wants/intent to delegate. Therefore, every rank could slightly differ in every runs.

But, something does not hold in the wallet ranking, either probably I did some miscaclulation or the specification in the design doc has not fully implemented in the haskell code. As the current wallet rank (2nd and 3rd columns in the table below) differs from the expected rank (4th column). I think it can be related the lack of the proper value of the apparent performance i.e. I have not checked how to gather from the ledger states (if there are such values in it) for that parameter.

Pool name Wallet Rank Relative rank Expected relative rank NM Stake NM pool reward NM mem Reward NM ROI
1PCT 9 1 6 119,900.00 123.77 0.00 0.00%
DIGI 31 2 5 545,700.00 563.30 39.70 2.90%
ZZZ 58 3 7 95,400.00 98.48 0.00 0.00%
P2P 92 4 1 206,000,000.00 212,645.16 103.06 7.52%
UNDR 133 5 3 1,000,000.00 1,032.26 67.84 4.95%
BCSH 151 6 2 7,900,000.00 8,154.84 97.93 7.15%
SAND 206 7 4 918,900.00 948.54 64.90 4.74%
THREE 209 8 8 33,333.00 34.41 0.00 0.00%

As an example below querying the protocol, for wallet pool ranking, using cardano-wallet listening on localhost:8090.

# 100000 ADA delegatable stake in loveloce

# if you left empty it will show all ranked pools
# e.g. POOL_ID=""

curl -s "http://localhost:8090/v2/stake-pools?stake=$STAKE" | jq -r  '.[] | [ .id, .metrics.non_myopic_member_rewards.quantity ] | @csv' POOL100K | grep -n "${POOL_ID}.*,"

Brief explanation

The ranking in Daedalus is simply based on a sorted list of the pools, which comes from the underlying backend node that Deadalus uses.

The pool list is updated each time when Daedalus (cardano-wallet to be more precise) queries the node (using the node-to-client mini-porotocol) with the user's specified delegatable stake.

The rank order is based on the pools' calculated non-myopic pool member rewards that are calculated both, the user specified delegatable stake of the query (slider in Daedalus) and the pools characteristics (perfomance an pool parameters).

It can be thought as some dynamic ranking, when a user could always get different ranks based on the stake she or he wants to delegate when the protocol calculates the non-myopic member reward, and therefore the rank, based on that value the user specified in the GUI.

When daedalus receives the non-myopic rewards ordered pool list (pool id and non-myopic reward pairs), it links the pools' details (semi-static data queried earlier) to the latest non-myopic (dynamically queried) rewards and shows it in GUI as ranked pools (highest the reward higest the rank e.g. highest reward then the pool ranked as 1st).

Note: the slider in Daedalus is for querying the protocol for the pools' list /w the updated non-myopic rewards based on the amount of ADA the users wants to delegate.

The non-myopic pool member rewards is caclulated by the Cardano protocol as the following:

  1. First, it ranks (it is different than the wallet ranks) the pools by desirability which is based on the pools apparent performance (UPDATE: the average apparent perfromance was considered, but afaik hit rate estimates is used) and on cost, declared pledge, and margin pool's parameters (the pool parameters are taken from the most recent pool registration certificate).
  2. then, the first nOpt ranked pools (currently 150) can have a full-saturated (or more if they are oversaturated) stake for further calculations, while the rest can keep only their pledge, this called non-myopic stake.
  3. After that, for the non-myopic reward calculations it uses the normal pool member reward function, but uses the non-myopic pool's stake instead of the current active stake of the relevant pool as the total delegated stakes (i.e. sigma) parameter in the function.

High level explanation

The pool ranking in Daedalus is based on the Cardano procotol's game theoretic model that assumes that the procolol will inevitably reach an ideailistic (non-myopic) state (Nash equilibirium) by some time, and the rank of the pools based (weighted would be more proper word) on their expected - non-myopic - reward.

And therefore in this context, non-myopic simply means, that ranking assumes the equilibirium in which a pool can be either part of only the nOpt number of almost fully the saturated pools (selected) or not.

It makes sense as saturation means: stakes delegated to pool; and if almost all nOpt nr. of pools would be fully saturated then, there would not be any room (i.e. no stakes left) for the rest of the pools.

Therefore, the game theoritic model assumes that the selected nOpt number (currently 150) of pools will have a high chance of being part of the equlibirium and the rest of them (the non-selected) would probably die out for long term.


This initial selection, based on the above ssumptions, of the pools are based on their expected reward as attractivenes as if they would be fully saturated, which called desirability.

It also means, that a simplified reward function is used to calculate the pools' reward.


Note: As you can see it is not simplified, but uses z0 as full saturation for the sigma, σ i.e. it assumes full saturation of the pool. Test, when σ = z0.

The pools desirability are calculated from the pools' individual apparent performance (has changed to hit rate estimates) and their monetary related pool's parameter such as cost, pledge and margin.


Note: if the caclulated non-myopic optimal reward is less then the cost then the desirability is 0.

The rationale of this assumption is that these parameters have some degree of impact on the pools' rewards (in Daedalus) that will drive the delegators toward to the pools that offer the highest return of their investment (rationale behaviour).

Non-Myopic Pool Stake

After the initial desirability based ranking, the system assign a non-myopic pool stake to each of the pools, based on the initial ranking selection.


As a result, all of the pools are - virtualy - split into two distinct groups by this non-myopic pool stake assignment.

The non-myopic stake simply means that the first nOpt umber of pools (currently 150) will be considered as it would have at least a saturated level of stakes for the later non-myopic reward calculations, while the rest only can have their currently delegated owner's stake (pledge).

Note: keep in mind that the pledge and the pool's stake are calculated from the current UtxO set (i.e. live stake) of the time of the query and not from the active stake.

The rationale of the non-myopic stake is that, those who won't be part of the equlibirium (the non-selected pools) will loose all their delegators by the time anyway, and therefore they would have stakes only from their owner(s), while the selected ones will be inevitably saturated i.e. assumes that the system will reach the Nash equilibirium.

In simple words, the system has ranked the pools by their desirability and selected the first nOpt number of them as the attractive ones that would be saturated by the time and the others won't be really counted as they would have been already died out by the time the system reaches equlibirium.

Non-myopic Pool Member Rewards

The ranking in wallet is based on the non-myopic pool member reward that is calculated for each pools based on the stake of the pool member wants to delegate, for example the slider in the Daedalus.

This non-myopic pool member reward is calculated by the the normal reward splitting formula of a pool, but uses the idealised non-myopic pool stake as the pool's total stake instead of the actual pool's total stake (**also this non-myopic stake is based on the current UtxO set of the pools (pool's live stake) at the time of the query and not based on the actual pool's total (active) stake).


The meaining of this above formula is that, the protocol calculates the pool member reward for each of the pools relative to the member stakes (t is the stake the user is about to delegate) to the corresponding non-myopic pool stake, by using the following formula:


In simple words this means:

For each of the pools, the pool (non-myopic) reward is calculated based on the pool's pledge, the non-myopic pool stake which is either of:

  • at least the full saturation or more if the pool is oversaturated (for the selected ones) or
  • the owner(s)' pledge (for the rest). and the apparent performance.

If the pool reward is less then the cost then it returns the reward as non-myopic pool member reward.

Otherwise, it calculates the non-myopic pool member reward for that pool as the non-myopic pool reward minus cost.

Which is multiplied by the reduced margin of the whole.

Then it is multiplied by member stake relative to the non-myopic pool stake.

See details below:

(non-myopic pool reward - cost) times (1 - margin) times member contributing stake relative to the non-myopic pool stake


Wallet Ranking

In the wallet, the pool ranking is based on the specified wallet's expected (non-myopic pool member) rewards for its delegated- or delegatable stake, from each of the pools.

Meaning, that a wallet would have a list of all pools that contains the calculated non-myopic pool member reward that the wallet should expect. And the wallet ranking is based on this list, meaning the pool which has higher expected non-myopic pool member reward, will be higher in the rank.

Though, the GUI is quite misleading, as it should behave per wallet basis, and not using some slides to fine tune to what the wallet owner wants to delegate, it should be wallet specific. For example some a drop down list of the wallets the Daedalus have and rank the pools based on the selected wallet's delegatable stakes.

This non-myopic pool member rewards uses the normal reward function, but the total stakes of the pool, the sigma depends on the inital ranking selection, which means that the selected ones (in some degree) would be much more attractive for the users, as their all delegated stake would be considered, while the rest only can use their owner(s)' stake (pledge) in the non-myopic reward caclulation.

In simple words, the wallet would sort/rank the pools based on the delegators non-myopic pool member reward for each of the pools. Meaning, the first in the rank would be the best choice for the delegator. This assumes rational behaviour from the delegators where they, in general, choose from the first few pools of the ranked list.



This comment has been minimized.

Copy link

@JamesRobertKelley JamesRobertKelley commented Sep 12, 2020

Typo in "underlying backend node that Deadalus uses"


This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Sep 14, 2020

There are a lot typos:) But thx anyway.
But, what's wrong with that statement? probably "Daedalus GUI frontend uses"


This comment has been minimized.

Copy link

@DamjanOstrelic DamjanOstrelic commented Sep 18, 2020

This is a great read Pal, thanks!


This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Sep 19, 2020

This is a great read Pal, thanks!

Thx, but I think there are lot of errors and typos, but thx anyway.


This comment has been minimized.

Copy link

@shawnim shawnim commented Sep 20, 2020

Excellent article! Thanks for writing this.


This comment has been minimized.

Copy link

@Colin-Edwards-IOHK Colin-Edwards-IOHK commented Sep 21, 2020

Superb post; I will work on getting this in front of the correct people.


This comment has been minimized.

Copy link

@csoroz csoroz commented Oct 5, 2020

Some errata, in the second r_member,nm equation: f if f <= c should be 0 if f <= c
and some parameters have different names: c ~ cost / m ~ margin


This comment has been minimized.

Copy link
Owner Author

@ilap ilap commented Oct 6, 2020

Some errata, in the second r_member,nm equation: f if f <= c should be 0 if f <= c

Yep, that was a typo and fixed it.

and some parameters have different names: c ~ cost / m ~ margin

I did it on purpose, but yeah it's a bit misleading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment