Trust-less liquidity pool

Really? I thought that too in the beginning, then @tomjoad sent me some material but I cannot see any of these auction models having the requirements (i.e. decreasing my bid will never lead to a higher price I have to pay, increasing it will never lead to a lower price I have to pay, assuming other’s don’t change their bid).

Right now I am approaching it more like an order book, with the interest rates being the price and the liquidity the order amount being available at every minute, which the server then “buys” for a certain price, where the price has to ensure above conditions. This view is also very close to the concrete implementation.

I really want to avoid clients to adjust their interest rate and want to make the promise that they always should specify the minimal interest rate they are willing to get, knowing that they will always get the maximal reward possible.

EDIT: I think it may be considered as multiple-item dutch auction, which looks like quite some confusing beast but I’ll look into it. Thanks for the heads up.

As I mentioned to @creon, traditional auction models rely on bids being placed for a single category of item using a single dominant variable. That could be a car, a piece of land, a painting, etc, usually bidded for in a local currency. The problem with the approach above is that it is trying to simultaneously optimize “auction” outcomes for two dominant variables: interest rate and liquidity provided.

The solution I think is to prioritize one variable over the other. In an order book for example, “Price” is the dominant variable, with “Amount” being a secondary variable. Bids on order books are first filled in accordance with the dominant variable.

Perhaps there is an easier solution to this problem using this approach. What if you simply fill orders up from the minimum interest rate offered?

Example: 20 NBT in liquidity is required by Creon’s pool

Alice offers 5 NBT at 1% interest

Bob offers 10 NBT at 1.5% interest

Carl offers 10 NBT at 3% interest

The pool would fill the entirety of Alice’s order of 5 NBT, the entirety of Bob’s order of 10 NBT, and only 5 NBT of Carl’s order, for a total of 20 NBT. The pool would fill the offers at the exact minimum interest rate specified by the pool participants.

Now, this will mean that the total interest paid by the pool is lower than 5%, and Creon will have made a healthy profit if his custodial proposal specified a 10% fee in total. This is in violation of the current rule of 5% being maximal interest.

I would argue this is ideal for shareholders. This scenario means that other pool operators will likely set up new operations if @Creon’s pool doesn’t lower its requested fee from 10%. Looking at basic economic theory, we see that in monopolistic competition (the state that would occur if Creon had the only pool), “supernormal” profits are possible. Supernormal profits encourage new entrants into the space, which would move the competitive landscape closer to perfect competition. In perfect competition, the total fees requested by pools will be lower.

I think this solution is much simpler, ensures the lowest possible interest rates for shareholder-funded liquidity, and better reflects market demand.

1 Like

Yes this is the approach I used as the very first implementation. Problem is that all of the participants in your example would be able to get a larger payout by increasing their interest rate.

So then its a real game the participants are playing against each other and every client should use some kind of interest rate strategy in order to maximize the own payout. When writing the trading bot for this, I noticed that this is a problem which surely has an equilibrium, and if bots slowly evaluate different interest rates and then adjust their own interest rate into the right direction, then after a while this equilibrium should always be found (assuming the market doesn’t move and no other participants join) and the whole trading strategy will be obsolete.

This is why I just want to calculate this equilibrium on the server side and directly pay the best possible scenario for all players.

It would only do so in the short-run. Game theory (and Dutch auction theory) would specify that participants lower their requested interest rate down to their lowest acceptable amount in the long-run.

You are correct that in the beginning, all users would select 10% as their minimum interest rate. If enough NBT liquidity could be offered by participants, that number would begin falling (one user would offer 9%, and then another would offer 8%) if the rates are desirable enough.

In the long-run, every user would arrive at their lowest possible interest rate, subject to the condition that total liquidity provided > 20 NBT. If 20 NBT liquidity or less is offered by participants, the participants deserve the full 10% reward, and the pool operator should request 15% next time. Shareholders ought to pay whatever is required.

However, if 50 NBT in liquidity is offered by participants, the average interest rate paid out by the pool will very likely be significantly less than 10%. At that point, potential pool operator entrants would weigh whether the supernormal profits earned by you (Creon) would justify creating a new pool to compete with yours.

I believe this approach allows liquidity to be priced perfectly by the market, rather than relying on complicated auction rules.

1 Like

Yes If nothing better comes to my mind then I will probably need to fall back to this approach. It of course requires a lot more interaction and market analysis by the participants, while the solution actually should be calculable on the server’s side as the minimal interest rates (i.e. maximal bids) and amounts are known and fixed (at this time step). These complicated auction rules would then apply on client side.

After studying those multiple-item dutch auction I think it is indeed exactly my model here. It is the same system eBay uses when you are not selling 1 copy of an item but 100 at once - and eBay ensures the rules I would like to see satisfied (i.e. you will never pay more on eBay just because you set a higher maximum bid initially). Here the pool is selling space (consisting of many 10^-8 NBT units) within the target for a price which is given by the interest rate.

In general you can say if you have increasing price levels p_1 … p_n, a target of T and a current liquidity of L then the pool has to pay (this forum needs a LaTeX plugin):

p_n * L for L <= T
p_n-1 * (L - T) + p_n * (2T - L)  for T < L <= 2T
p_n-2 * (L - 2T) + p_n-1 * (3T - L) for 2T < L <= 3T
p_n-i * (L - iT) + p_n-i-1 * ((i+1)T - L) for iT < L <= (i+1)T

At each price level q the corresponding payout then should be distributed according to the liquidity provided. So if there are M participants with liquidity u_1,…,u_M then we normalize the liquidity to r_m = u_m / z with z = \sum_i u_i such that they sum up to 1 and then credit the corresponding part of of the paid liquidity as payout:

(L - iT) * r_m * p_n-1 if q < p_n-1
((i+1)T - L) * r_m * p_n if p_n-1 <= q < p_n

Every unpaid residual is then shifted to the previous price level and will be compensated there if the minimal interest rate is small enough.


So here L = 25, T = 20 and the price levels are: p_1 = 3%, p_2 = 4%, and p_3 = 5% (pool max) with n = 3.

So T < L < 2T and i above is 1, and the total payout is:

4% * (25 - 20) + 5% * (40 - 25) = 0.95 NBT

The 15 NBT at price level 3 will be compensated at 5% according to the contribution w.r.t. the total liquidity, so A and B both will get

5% * (5 / 25) * (40 - 25) = 0.15 NBT

while C will get

5% * (15 / 25) * (40 - 25) = 0.45 NBT

Both A and B still have 5 - 15 * 5 / 25 = 2 NBT at price level 2 (4%) containing 5 NBT, which results in an additional payout for A and B of

 4% * (2 / 4) * (25 - 20) = 0.1 NBT

Case 1: A changes its minimal rate from 2 to 3%

No effect, neither price levels nor set of participants within a price level changed.

Case 2: A changes its minimal rate from 2 to 3.5%

New price levels: p1=3.5%, p2=4%, p3=5%. There is no change in the payout, since all coins will be compensated up to price level 2.

Case 3: A changes its minimal rate from 2 to 4.5%

New price levels: p1=4%, p2=4.5%, p3=5%. The 15 NBT in p3 will be compensated just as before, but now B and C are sharing level p2, giving B 0.05625 NBT and C 0.16875 NBT. So A will lose all payouts of the price levels skipped through the interest rate change.

Case 4: B changes its minimal rate from 3 to 4%

A is now alone on the 4% price level and will get the whole 5 NBT compensated at 4%:

4% * 5 NBT = 0.2 NBT

which accordingly gets subtracted from B’s payout.

Case 5: C changes its funds from 15 NBT to 11 NBT

L becomes 21 and only 1 NBT will be paid at 4%. C only reduces its own contribution to the last price level by changing the funds by this amount, therefore decreasing its own payout.

Case 6: C changes its minimal interest rate to 0.01%
New price levels: p1 = 2%, p2 = 3%, p3 = 5%. A and C are both now taking over coins from a higher price level to a lower price level. As before, C gets 15 * (15 / 25) = 9 NBT compensated at 5%, but now additionally gets a payout at previous price levels, therefore increasing its own payout. The one who is suffering from it is B, who loses the payouts at the second price level.

So an increase of the minimal interest rate can only remove yourself from previous price levels which in turn can only reduce your payout (or it stays the same)

A decrease of the minimal interest rate can only add additional price levels where you get compensated and therefore can only increase your payout (or it stays the same)

Increasing your funds will always increase your contribution in the top most price level therefore increasing your payout.

Decreasing your funds likewise will only decrease your contribution in the highest price level and therefore reduce your payout.


@creon, you are a beast. :leopard:


Its all the Dutch’s fault :wink:

I consider this method as safe if @cryptog (or anyone else) doesn’t again come up with some easy example ruining my whole work - but of course I am glad for bringing it up now instead of exploiting it later.

1 Like

To be honest, I did not dare to look into the details of what you described in your dutch auction algorithm, the beast is a monster :smile:

But this rule above is counterintuitive.
So if I put 0% as minimal interest, I should maximize my rewards?
In that case, I should put 0% all the time.

I have not quite understood the meaning of minimal interest.
My view:

  • If I put n% as minimum interest on X NBT, that means that I either get n% or more on X NBT or nothing.

This is correct (although 0 will be interpreted as pool max, so 0.000000001% is minimum). You will always maximize your reward by reducing your minimal interest rate, but it also means that you might provide a part of your liquidity for this interest rate.

So if you provide 100,000 NBT below the current top price level of 5% minimal interest rate then you might only get 1000 NBT compensated at this interest rate (50 NBT) and you should reduce your liquidity by 99,000 NBT, since it reduces your risk and doesn’t change the payout.

If you now decrease your minimal interest rate below an existing price level at 3% then you will get more of your liquidity compensated but the additional amount of liquidity you get compensated will be at a lower interest rate. So for example you would get your 1,000 NBT at 5% + 500 NBT at 3%, so your reward will be larger BUT its partly compensated at 3% only and in order to keep this payout you must not remove those 500 NBT from your liquidity, i.e. you can only reduce your funds by 98,500 NBT.

Note that the trading bot automatically performs these tier 1 -> tier 2 movements in order to remove as many funds as possible without losing any payout. So if you set 5% and enough people provide liquidity for less, then at some point your bot will remove all your funds from the order book. Its in your own responsibility however to move those funds then from the exchange into a secure wallet.

If you put n% as minimum interest rate on X NBT, that means that you get n% or more on Y NBT and 0% on Z NBT where X = Y + Z

1 Like

I implemented the new payout system, which turned out to be much less code both on client and on server side. Furthermore the client is much better able to adjust tier 1 -> tier 2 movements. The new features however required a slight change in the API communication of the client to the server.

Therefore I need all of you to update the client again to the (hopefully last) pre-release 0.32:

or update your repository. I also changed the port again to 3333, so please run your client like this:


I will shut the old server down in about 6 to 12 hours. Please update as soon as possible. I also edited the maximal interest to 1% and the target values to 50 NBT on each side and exchange.

Thank you!

1 Like

Here is a new version of the client. It is not mandatory but greatly improves the trading bot and also shows the console output in a more human readable form:

The new output looks like this:

INFO: poloniex - balance: 0.00039520 rate 0.33% efficiency: 100.00% rejects: 0 missing: 0 - btc - ask: 0.6278 x 0.66%, 1.4946 x 1.00%, 3.6106 x 0.00% - 85BQTQG1-N3D3PFKX-YUNIHIO1-XXXXXXX

Since there are always three payout levels in the new payout system, the output always shows the corresponding amount x the interest rate at this level.

It also fixes an issue on BTER where orders of other coins got deleted when NBT orders got replaced. While this is fixed now I would like to repeat that its highly encouraged not to have any coins on this account which should not be considered as liquidity.


Another non-mandatory update:

Its important for the server but on client side it also fixes a bug when shutting down the client reported by @Joe (thanks!).

Highly recommended, though not mandatory client update:

  • trading bots do not place orders anymore if they would match an existing order
  • trading bots synchronize their actions with the server time to avoid interfering with each other

Hi creon, first of all thanks for the great work you are doing with your NuPool, I find it a very exciting project.

Since updating to 0.36 I get the following error messages

on BitcoinCoId

 unable to place ask btc order of X nbt at Z on bitcoincoid: exception caught: 'BitcoinCoId' object has no attribute 'pair_id'


ERROR: unable to place ask btc order at X on bter: matching order at Y detected

for future reporting, do you prefer if I open an issue on github?

Thanks so much for testing Due to my custodianship, and my difficulties creating a second accound on their exchange, I am not able to see if what I wrote actually makes sense. I think I fixed the issue you are seeing there, but I cannot promise that everything will be fine now.

Are you using the repository? Then you can just pull, otherwise I will make a new release soon.

I am seeing this too. Actually this is the expected behavior due to the new update. It means that your bot would fill an existing order if it would place your NBT at the required price, so somebody is buying NBT for more than 1 USD. While an option to really execute those orders in those cases might be in the interest of some users, I think in general people prefer not filling other’s orders here.

However, it was extremely verbose with those warnings so I changed it to only output it when an actual order replacement was expected.

No, here is fine. Again, thanks so much for testing. Would be nice if you could report back if is working.

just pulled and still experiencing the same for bitcoincoid

unable to place ask btc order of X nbt at Z on bitcoincoid: exception caught: 'BitcoinCoId' object has no attribute 'pair_id'

this is a nice extra feature to have as an option

Another error message that I see less frequently is this

ERROR: exception caught in trading bot: 'btc'

Hmm this is strange, there is no single field called ‘pair_id’ since my last commit. Are you sure that updating the repository was successful and that you restarted the client?

This is ok, sometimes exchanges don’t deliver a correct json object. There is nothing to worry about that and I will soon degrade those messages to debug status such that they won’t get printed in the terminal.

I tried again with a fresh clone and the error still there

I have also tried with older versions, just to check that didn’t have to do with my configuration, and I don’t get the bitcoincoid error.