Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TX21: need to prevent sellers from getting no indivisible coins from a matching order #170

Closed
marv-engine opened this issue May 22, 2014 · 69 comments

Comments

@marv-engine
Copy link

This needs to be addressed as part of the discussion for PR #165 but that thread was getting long so I broke this out.

This issue is about the possibility that a seller can end up getting 0 indivisible coins from someone's matching order. This is due to the fact that the number of indivisible coins affected by an exchange has to be rounded down to a whole integer value.

On average, a seller will not receive the equivalent of .5 indivisible coins when another order matches with the seller's order that desires an indivisible currency. At worst, the seller won't receive the equivalent of .99999999 coins in a tx21 coin swap. At best, he won't lose out on any equivalent fractional parts.

A seller who posts an order desiring an indivisible coin has no control over the number of his coins that will match a future tx21 order. So, it's possible that the calculated number of indiv coins he will get could be less than 1 and would therefore be rounded down to 0. That's the worst case. The same rounding will occur regardless of the number of indiv coins that were calculated. So, the seller is open to the risk of many of these transactions that give away his coins with the potential that he will get 0 coins in return, or possibly a non-zero amount that's less than he desires. He has no control over this.

We should protect the seller, perhaps by letting him specify the minimum number of desired coins he's willing to accept in a tx21 transaction. That number would be used in the matching algorithm until the seller has fewer than that number left for sale.

Maybe there's another way to address this risk.

Note that participating in a crowdsale has the same truncation issue, but it happens only when a participant explicitly sends coins. In that case, the user isn't subject to the risk caused by future actions of others.

@dexX7
Copy link
Member

dexX7 commented May 22, 2014

Maybe I fail to grasp the problem, but since indivisible units are not further divisible and an amount between < 1 and > 0 doesn't exist in the first place? Or is your intention to specifiy exactly this?

If there are 5 IndivisibleCoins up for sale for a total amount of 10.0 MSC, someone has a balance of 6.35 MSC and wants to buy as many as possible, then he ends up with a final balance 0.35 MSC + 3 IndivisibleCoins.

@dacoinminster
Copy link
Contributor

I've worked through a couple examples, and I just can't find a way to make this bug actually happen without an impossible transaction (i.e. trying to buy or sell 0.4 indivisible coins, which the protocol does not allow).

So lets imagine I place an offer to buy 10 indivisible coins on the dEX for 1000 divisible USDCoins each. Can anybody hypothesize a transaction which exploits rounding error to steal my USDCoins?

I couldn't figure out a way to do it.

@marv-engine
Copy link
Author

Here's a case:
time 0: A1 offers 3 MSC for 1 Indiv ==> unit price = .3
time 1: A2 offers 1 Indiv for 2 MSC ==> unit price = 2 (reciprocal is .5)

The offers match at the older (A1's) unit price (.3). A2 wants only 2 MSC so it has to give only 2 * .3 = .6 Indivs in return. .6 will be rounded down to 0.

@dacoinminster
Copy link
Contributor

In the example you give, wouldn't A1 get 1 indiv and A2 get 3 MSC? I don't think the fact that A2 wants 2 MSC comes into play, since it is the later order and a better price was available.

@dexX7
Copy link
Member

dexX7 commented May 22, 2014

The new sell order's unit price is computed from two of the fields in the transaction message: the "Amount desired" divided by the "Amount for sale".

Here is a problem I noticed a few minutes ago:

Divisible tokens are actually token * 10000000, so let's try the order:

New Order A:

For sale: 30000000 MSC (bin: 0000'0001'1100'1001'1100'0011'1000'0000)
Desired: 1 IND (bin: 0000'0000'0000'0000'0000'0000''0000'0001)

What is the unit price here? The calculation unitprice = 1/30000000 would result in unitprice = 0 unless fractions are introduced (what is done internally at other places, too) and later rounded. But then there may indeed need for further clarifications.

@Bitoy
Copy link

Bitoy commented May 23, 2014

@marv-engine
edit you are correct
tx 21 is unit price = amount desired / amount for sale

time 0: A1 offers 3 MSC for 1 Indiv ==> unit price = 1/3 = .33
time 1: A2 offers 1 Indiv for 2 MSC ==> reciprocal unit price = 1/2 = .5

If the Indiv coins round to 0 the match is not valid. Get the next available match.

@Bitoy
Copy link

Bitoy commented May 23, 2014

@dexX7 if the unit price is 0 tx should be invalid.

@Bitoy
Copy link

Bitoy commented May 23, 2014

For Indiv coins of tx 21 let's drop the roundown. Indiv coins must be a whole number

A1 offers 3 MSC for 1 Indiv ==> unit price = .3
B1 offers 4 msc for 2 Indiv unit price = .5
A2 offers 1 Indiv for 2 MSC ==> unit price = 2 (reciprocal is .5)

2 MSC * .3 (A1 offer) = .6 Indivs. not a whole number Invalid match
2 MSC * .5 (B1 offer) = 1 Indivs. Whole number it matches.

A2 trades with B1.

@Bitoy
Copy link

Bitoy commented May 23, 2014

@dacoinminster interesting point.

A1 offers 3 msc for 1 Indiv = .3
A2 offers 1 Indiv for 2 msc = .5

A2 ask A1 how much would you pay for 1 Indiv ? A1 replies 3 msc
Since it's greater than 2 msc. A2 takes it.

A1 gets 1 Indiv gives 3 msc
A2 gets 3 msc gives 1 Indiv

(A1 is happy, A2 is happier. The important thing is both are happy :)

@marv-engine
Copy link
Author

@dacoinminster @Bitoy There are two ways that could be used to determine the number of coins exchanged when a non-exact match is found and seller is offering more coins than buyer desires:

  1. "full buy/partial sell":
    • buyer gets the number of coins he desires, in exchange for fewer coins than he's offering to sell
    • seller gives the number of coins buyer desires, in exchange for fewer coins than he desires
  2. "over buy/full sell":
    • buyer gets more coins than he desires, in exchange for the number of coins he's offering to sell
    • seller gives all the coins he's offering to sell, in exchange for the number of coins he desires

My understanding is that the "full buy/partial sell" is what we want. The objective is to fulfill the new (buyer's) order for a desired number of coins, as opposed to selling all the coins the existing (seller) order is offering. Per the referenced algorithm, the PR currently says:

The coins from each matching order and the new order are exchanged between the corresponding addresses at the unit price specified by the matching order until the new order is completely fulfilled or there are no more matching orders.

@marv-engine
Copy link
Author

@dexX7 I'm not sure if the protocol's internal representation of the number of divis & indiv tokens matters in determining the unit price, or do we just treat them as a regular double precision float and integer for the calculation. I have to think that one through. It might be yet another edge case (YAEC).

I do agree with:

If the Indiv coins round to 0 the match is not valid. Get the next available match.

Here's a way to handle the divis for indiv case:
After rounding down the number of indiv coins to be transferred, use that integer value to correct the calculation of the number of divis coins being exchanged. That eliminates any loss in the exchange!

For the situation where the coins for sale and desired are both indivs:
There's still the case where the truncated fraction is a significant percentage of the calculated number of coins, e.g. worst case 1.999999 becomes 1 (50% loss), or even 19.99999 becomes 19 (5% loss). I think the same recalculation of the other coin amount could be used here to reduce the loss, but it won't eliminate fractions in all (many?) cases.

At what point does the truncated fraction not matter? The user could limit his exposure if he's allowed to specify the minimum purchase amount.

Specifying a minimum purchase amount would also let sellers offer volume discounts. Without it, a buyer could choose to buy 1 satoshi of a divis coin or 1 indiv coin, even if the seller wants to sell billions.

@dexX7
Copy link
Member

dexX7 commented May 23, 2014

Marv: the property "divisible" and "indivisible" acts as amount modifier:

1 Indivisible Unit = 1 Base Unit
1 Divisible Unit = 10000000 Base Unit

@Bitoy
Copy link

Bitoy commented May 23, 2014

@marv-engine
Nice solution. this works div for indiv or indiv for div
"After rounding down the number of indiv coins to be transferred, use that integer value to correct the calculation of the number of divis coins being exchanged. That eliminates any loss in the exchange!"

indiv for indiv
Adding a minimum amount to purchase will not fix the truncation problem.
Suggest that we use @dacoinminster approach or the "whole number matching"

@marv-engine
Copy link
Author

There's no way to fix the indiv for indiv integer problem in general.

So, one of the parties will be subject to receiving up to the equivalent of .99999999 coins less than they should in each match. The question is how can the parties limit their loss? I see two alternatives:

  1. set a minimum purchase amount which limits the percentage of the loss compared to the number of coins they do receive.
  2. set the maximum fractional amount they are willing to give up in each indiv for indiv match.

The min purchase amount alternative does support a seller offering a volume discount (which could also be accomplished if a single order allowed tiered unit pricing).

We need to make a decision so the devs can start working on implementation.

@dacoinminster
Copy link
Contributor

I still don't see an actual example of this problem here. It still seems to me that this problem can't actually happen with the transactions which we can express.

For indiv vs indiv:

UserA: wants to sell 1 indivA to get 2 indivB
UserB: wants to sell 3 indivB to get 1 indivA
Result: UserA gets 2 indivB, UserB gets 1 indivA (earlier terms are used)

Where's the fractional problem here?

The problem I see here is what precision to store unit price. I suggest double-precision floats should be sufficient.

@marv-engine
Copy link
Author

Time 0: A1 offers 20 Indiv1 coins for 10 Indiv2 coins (unit price .5)
Time 1: A2 offers 12 Indiv2 coins for 17 Indiv1 coins (unit price ~1.4, reciprocal ~.6)

Process:

  1. A1's sell order matches A2's buy order (.5 < .6)
    * so want to move 17 Indiv1 coins from A1 to A2 in return for 17 * .5 = 8.5 Indiv2 coins from A2 to A1

There's no way the number of both coins transferred can be whole numbers.

@dacoinminster
Copy link
Contributor

I think the result of this example would be:

A1 gets 10 indiv1 coins, A2 gets 20 indiv2 coins and has 2 Indiv2 coins left for sale.

HOWEVER, the unit price of 1.4 would no longer make sense with only 2 coins for sale, and I think the problem WOULD then happen in subsequent transactions.

@dacoinminster
Copy link
Contributor

Solution: after each sale, update the unit price to the next best price which can be represented in a MSC TX21. So for Marv's example above, after the sale was complete, the A2 would have 2 Indiv2 coins left for sale in exchange for 3 indiv1 coins, adjusting the unit price to a more favorable 1.5

@dacoinminster
Copy link
Contributor

Sorry - hit close by accident. This is definitely still open :)

@dacoinminster
Copy link
Contributor

OK, adjusting Marv's example, I can make this happen in one tx:

Time 0: A1 offers 20 Indiv1 coins for 11 Indiv2 coins (unit price .55)
Time 1: A2 offers 6 Indiv2 coins for 8 Indiv1 coins (unit price ~1.33333, reciprocal ~.75)

This results in a partial fill. A1 gets 6 indiv2, but A2 should get 3.3 indiv1!

Do we all agree that this is a good example to work from?

@dacoinminster
Copy link
Contributor

A human being looking at the example above would probably suggest A1 gets 6 indiv2, A2 gets 10 indiv1, for unit price of 0.6, which is better than the asking price on both sides, but good luck describing a general-purpose algorithm to do that!!

edit: fixed my wrong math :)

@marv-engine
Copy link
Author

@dacoinminster Coins are transferred from the seller (A1) to the buyer (A2) until he gets the amount he desires, so A2 will get 17 Indiv1 coins and has to give 8.5 Indiv2 coins to A1.

My example is clear to me. It looks like yours is backwards. The transfer amounts are a function of the 8 Indiv1 coins desired, not the 6 Indiv2 coins for sale.

@dacoinminster
Copy link
Contributor

The spec says throw away the coins desired, and keep unit price instead, right?

@marv-engine
Copy link
Author

In fact, it does.

@marv-engine
Copy link
Author

But the unit price implies the amount desired (which decreases when this existing order is partially used to fulfill a buy order).

@marv-engine
Copy link
Author

Clarified:
But the (unit price * amount for sale) determines the amount desired (both of which decrease at a fixed ratio: the unit price, when this existing order is partially used to fulfill a buy order).

@marv-engine
Copy link
Author

No one ever gets more than they desire. They might pay less than they're willing to, though.

@dexX7
Copy link
Member

dexX7 commented May 26, 2014

Yes, I think this is a good idea. What happens in the case of fractional amounts?

Using the old example:

A1 offers 20 Indiv2 and wants 10 Indiv1 at >= 0.5 Indiv1/Indiv2.
A2 wants 59 Indiv2 and offers 30 Indiv1 at <= 0.50847458 Indiv1/Indiv2.
A1 trades 20 Indiv2 for 10 Indiv1 at 0.5 Indiv1/Indiv2.
A1 now has 0 Indiv2 remaining and wants 0 Indiv1.
A2 now has 20 Indiv1 remaining and wants 39 Indiv2.

39 * 0,50847458 = 19.83050862

I think rounding up is the correct solution, because the user still gets what he wanted for the price he wanted - in total - and no orders are created which are "higher than market price", e.g. reversed unit price of 0.48717, if rounded down to 19.

@Bitoy
Copy link

Bitoy commented May 26, 2014

Agreed round up if there is a fraction.

X= Roundup (remaining No. Of coins desired * original price )

Change = no. Of coins for sale - X

Btw precision of unit price must be defined in the spec. Suggest up to 8 decimal places.

@dexX7
Copy link
Member

dexX7 commented May 26, 2014

Floating point numbers are somewhat special and instead of using a fixed number of rounded decimal places, I suggest to use a standardized percision, e.g. "double percision" as defined in IEEE 794, see:

http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers
http://en.wikipedia.org/wiki/Double_precision_floating-point_format

This is C/.NET data type "double" and "float" in Python as far as I know.

Might also be worth to specify this spec wide. I assume the differences related to the Exodus balance are based on this.

@dexX7
Copy link
Member

dexX7 commented May 26, 2014

I uploaded an order match simulator:

https://github.com/dexX7/simple-order-matching

@ghost
Copy link

ghost commented May 26, 2014

thanks dexX, I'm checking this out now

@marv-engine
Copy link
Author

Remember, the Amount desired is needed only to determine the unit price (exchange ratio) for Action=New or Update. After that, the remaining number of coins desired is determined by calculating the remaining number of coins for sale multiplied by the unit price.

A1 offers 20 Indiv2 and desires 10 Indiv1 at >= 0.5 Indiv1/Indiv2.
No matches,
    A1 offer stored as 20 Indiv2 for sale, unit price is .5 Indiv1
A2 offers 30 Indiv1 and desires 59 Indiv2 at 1.96666667 Indiv2/Indiv1 (at <= 0.50847458 Indiv1/Indiv2)
    A2 offer stored as 30 Indiv1 for sale, unit price is 1.96666667 Indiv2
A1 matches with A2, so A1 trades 20 Indiv2 for 10 Indiv1 at 0.5 Indiv1/Indiv2.
    A1 now has 0 Indiv2 remaining and wants 0 Indiv1. (completed, can be removed)
    A2 offer now stored as 20 Indiv1 at 1.96666667 Indiv2/Indiv1
        could be thought of as: offers 20 Indiv1 and desires 39.33333333 Indiv2
A3 offers 40 Indiv2 and desires 20 Indiv1 at 0.5 (at <= 2 Indiv2/Indiv1)
A3 matches current A2, so A2 trades 20 Indiv1 for 40 Indiv2 (A2 gets a fractional amount more than he desired)

This fractional additional amount has to be given or these two orders would not match. I haven't figured out if this happens only for a match that completes one of the orders.

@dexX7
Copy link
Member

dexX7 commented May 28, 2014

Actually I think storing both amount offered as well as desired amount might be safer whereby the unit price is calculated on the fly. The desired amount seems to be the most important property and the other values are more flexible, as long as the limits are not overstepped.

I apply those assumptions (with loose words):

  • only whole amounts can be traded
  • a trade can be in favor for an user, but can't be more expensive than defined by the limit
  • in the case of fractional amounts (e.g. the example 20 for 10, 12 for 17), the desired amount is rounded up, but the unit price must stay within the limit
  • not more than available can be traded
  • available amounts are adjusted downwards, if an user gained more than expected (e.g. A1: 20 for 10, A2: 12 for 17 => 17 trade for 9 -- A2 completed and A1 is updated with 2 for 1 instead of 3 for 1)

I'm not sure, if this is the absolut minimum, but this seems to be sufficient for these examples:

Calculating the traded amounts:

a1_available = order_old.amount_for_sale
a1_unit_price = order_old.get_unit_price()

a1_desired = round_up(order_old.amount_desired)
a2_desired = round_up(order_new.amount_desired)

# traded amounts
amount_to_a2 = min(a2_desired, a1_available)
amount_to_a1 = round_up(amount_to_a2 * a1_unit_price)

Updating amounts after a trade:

# amount received is the amount gained from a trade
updated_amount_desired = amount_desired - amount_received
updated_amount_for_sale = updated_amount_desired / get_unit_price()

And the example:

New Order [ID 0] created: 20 Indiv2 offered, 10 Indiv1 desired @ 0.5 (2.0)
New Order [ID 1] created: 30 Indiv1 offered, 59 Indiv2 desired @ 1.96666667 (0.50847458)
New Order [ID 2] created: 40 Indiv2 offered, 20 Indiv1 desired @ 0.5 (2.0)

Executed [ID 1], [ID 0]: 20 Indiv2 traded for 10 Indiv1 @ 0.5 (2.0)

Updated Order [ID 0]: 20 => 0 Indiv2 offered, 10 => 0 Indiv1 desired
[ID 0] filled completely: 10/10 Indiv1

Updated Order [ID 1]: 30 => 20 Indiv1 offered, 59 => 39 Indiv2 desired @ 1.95 (0.51282051)
[ID 1] partially filled: 20/59 Indiv2

Executed [ID 2], [ID 1]: 20 Indiv1 traded for 39 Indiv2 @ 1.95 (0.51282051)

Updated Order [ID 1]: 20 => 0 Indiv1 offered, 39 => 0 Indiv2 desired
[ID 1] filled completely: 59/59 Indiv2

Updated Order [ID 2]: 40 => 0 Indiv2 offered, 20 => 0 Indiv1 desired
[ID 2] filled completely: 20/20 Indiv1

@marv-engine
Copy link
Author

The unit price is not changed as the result of a match, so it doesn't change on the fly.

This step isn't correct:

Updated Order [ID 1]: 30 => 20 Indiv1 offered, 59 => 39 Indiv2 desired @ 1.95 (0.51282051)

The unit price remains at 1.96666667 (0.50847458), what it was when the order was placed.

The only way the unit price changes is with an Update action. This would be much easier to keep straight if tx21 accepted the unit price rather than the amount desired.

@dexX7
Copy link
Member

dexX7 commented May 28, 2014

How would that work or what do you suggest otherwise? 39.333 is not a whole number.

@dexX7
Copy link
Member

dexX7 commented May 28, 2014

Actually I see no reason why this is not legit. A2 wants 59 for 30 @ 0.50847 and gets 20 for 10 at @ 0.5 which is in A2's favor and within A1' allowed range.

Edit: green is allowed for both (fullscreen: http://i.imgur.com/qSTrATC.png):

biig

@marv-engine
Copy link
Author

I think rounding up in the calculation (to 40 Indiv2 if a buyer wants to purchase 20 Indiv1) will address it the same way as in other cases - it just requires the resulting calculated unit price to be <= the buyer's unit price. The seller (ID 1) will get a fractional amount more than he had asked for.

If the unit price is recalculated after a partial purchase:
A2 wants 59 Indiv2 for 30 Indiv1 @ 0.50847
New order from A4:
A4 wants 2 Indiv1 for 58 Indiv2 @ 0.0345 (29.0)
A2 gets 58 Indiv2 for 2 Indiv1, so now
A2 now wants 1 Indiv2 for 28 Indiv1 @0.0357 (which is no where near his original unit price)

When larger quantities are involved, the unit price can have more wild swings. The seller is at the mercy of future buyers, whose actions would end up changing the seller's unit price in unpredictable ways.

@dexX7
Copy link
Member

dexX7 commented May 28, 2014

A2 wants 59, offers 30 -- this is an unit price of 1.966 ("the "Amount desired" divided by the "Amount for sale") and A4 would get 2 for ~4 and complete, if I understand it correctly.

Why would A2 get such a high amount? I assume A2 was listed before A4.

@dacoinminster
Copy link
Contributor

Guys, unit price doesn't move once it is set. This is another place to reduce complexity. If nothing else, it avoids having orders move around their position in the order book (which is ordered by unit price).

I see a lot of discussion here, but nothing which indicates we can't use the round-up method to avoid fractional losses of indiv properties. The same logic will apply to divisible properties too, avoiding losses of fractions beyond the eighth decimal place.

Marv, I think once you have the appropriate clarifications in the spec, we can close this.

@dexX7
Copy link
Member

dexX7 commented May 28, 2014

Why would you move the unit price? I think this is not really up for discussion. The rounding-up method seems to work fine and I posted the exact ruleset I applied with this method a few posts ago. For my part I'd like to know if my understanding is correct.

The only edge case which may need further clarification is related to this: #170 (comment)

@Bitoy
Copy link

Bitoy commented May 29, 2014

@marv-engine please check if this correct.

A1 offers 20 Indiv2 and desires 10 Indiv1 at >= 0.5 Indiv1/Indiv2.
No matches,
A1 offer stored as 20 Indiv2 for sale, unit price is .5 Indiv1
A2 offers 12 Indiv1 and desires 17 Indiv2 at 1.416 Indiv2/Indiv1 (at <= 0.7058 Indiv1/Indiv2)
A1 matches with A2, so A1 trades 17 Indiv2 for 9 Indiv1 at 0.5 Indiv1/Indiv2 rounded up.
A2 now has 3 Indiv1 remaining and wants 0 Indiv2 (completed, can be removed) 3 indiv1 returned to A2
A1 offer now stored as 3 Indiv2 at .5

A3 offer 6 indiv1 for 3 indiv2
A1 matches A3, so A1 trades 3 indiv2 for 6 indiv1
A3 and A1 closes.

A1in total gets 15 indiv1 (9 + 6 : 5 more than the original 10 he wants)
A2 gets 17 indiv2 + 3 indiv1 as change
A3 gets 3 indiv2

Total: 20 indiv2, 18 indiv1

@marv-engine
Copy link
Author

I've been thinking about this a lot and I'm starting to appreciate the approach where the unit price (exchange ratio) is calculated based on the remaining number of coins desired and the remaining number of coins for sale, as opposed to unit price remaining fixed.

When a new order is posted, that unit price (reciprocal of amount desired / amount for sale) is used to find matching existing orders with a unit price <= the new order's unit price. If the new order is not fulfilled (either there are no existing orders that match or the matching orders don't offer enough coins), then the new order is added to the list of existing orders and its unit price is recalculated based on its remaining amount desired and remaining amount for sale.

If an existing order is partially consumed to fulfill a new order, the existing order's unit price is recalculated based on its remaining amount desired and remaining amount for sale. If the order involves divisible coins on both sides, then the new unit price will be the same as the previous one because coins were exchanged at exactly the unit price.

If the order involves an indivisible coin on either or both sides, then the new unit price will change if a fractional portion of the coin was rounded up to the next whole number. Note that the new unit price could be dramatically lower than the previous unit price, depending on the new number of coins remaining.

Case 1:
* Existing order: 200 Indiv1 for sale, 10 Indiv2 wanted, unit price = 0.05
     * New order:   1 Indiv1 wanted, 9 Indiv2 for sale, reciprocal of unit price = 9.0
* After rounding up 1 * 0.05 Indiv2 coins from the new order and exchanging, the new order is completely fulfilled at 0.05 unit price, much less than the 9.0 unit price he was willing to pay
* Existing order: 199 Indiv1 for sale, 9 Indiv2 wanted, unit price = 0.0450
Case 2:
* Existing order: 20 Indiv1 for sale,  10 Indiv2 wanted, unit price = 0.5
     * New order: 17 Indiv1 wanted, 9 Indiv2 for sale, reciprocal of unit price = 0.5294
* After rounding up 17 * .5 Indiv2 coins from the new order and exchanging, the new order is fulfilled at unit price of 0.5294
* Existing order: 3 Indiv1 for sale, 1 Indiv2 wanted, unit price = .333

This will have the effect of making the remaining existing order more attractive to new orders - because the unit price is now lower. So these remaining smaller orders will match and be used sooner than they would have at their previous, higher, unit price.

When an existing order is fully consumed, the average unit price for all the matches it was part of will be the original Amount desired / Amount for sale, because the seller will have received the original number of coins desired in exchange for the original number of coins for sale.

One issue to be addressed by all implementations - the unit price or its reciprocal could be a very large or very small number, so it must be stored in a datatype that can hold values that range from:

  • 0.00000001 / 9,223,372,036,854,775,807 to
  • 9,223,372,036,854,775,807 / 0.00000001
    and calculations have to maintain the precision of the value.

Did I miss anything or make any mistakes?

@Bitoy
Copy link

Bitoy commented May 30, 2014

For case 2
If we make the unit price adjustable (.3333), it will favor the next buyer. (Next Buyer gets coins at lower price)
If the unit price is fixed(.5), it favors the seller. (Seller gets more coins)
The third option is to give a change to the seller based on his original price. This option is neutral to the seller and next buyer.

Whatever option we take, no one (seller, buyer, or next buyer) will loose out.

What do we want? pro seller, neutral or pro buyer?

@Bitoy
Copy link

Bitoy commented May 30, 2014

Let's calculate the unit price up to 8 decimals. If it rounds to 0 the tx is invalid.

@dexX7
Copy link
Member

dexX7 commented May 30, 2014

I use MSC/BTC now, because it's more vivid for me and the terms buy and sell order are used loosely:

Case 1:
There is a sell order for 200 MSC at a price of 0.05 BTC/MSC (total 10 BTC)*. And then another user throws in a crazy buy order: he is wants to buy 1 MSC and is willing to pay up to 9 BTC/MSC (!).

The order is executed and the buyer receives 1 MSC, but at a price of 0.05, because he buys into the existing 200 MSC sell wall. He get what he wants and after updating: 199 MSC are up for sale at a price of 0.05 BTC/MSC (total: 9.95 BTC).

Case 2:
I'd put up 2 Indiv2 for sale for 1 Indiv1 desired. #173 (route B vs. C)

Edit: the new order should not be more expensive, because this would lower the chances of a fill, so in doubt: make it cheaper. Storing "amount for sale" and "amount desired" instead of "unit price" may solve the problem of fractions.

Edit 2: *actually he wants to have 10 BTC instead of selling 200 MSC, but since this is no conflict here, I used this point of view for the sake of an easier example.

@marv-engine
Copy link
Author

@Bitoy says:

For case 2
If we make the unit price adjustable (.3333), it will favor the next buyer. (Next Buyer gets coins at lower price)

For the next sale, the price will be lower than it was, but that means the sale can happen sooner than it would have (favoring the seller if he wants to sell ASAP) and, in total, the seller still gets the number of coins desired for the number of coins he's selling. It all balances out because the earlier sale was at an effective unit price that was higher than the seller's original terms.

If the unit price is fixed(.5), it favors the seller. (Seller gets more coins)

But the remaining coins may not sell as fast as they would with a lower unit price, so that's not helpful to the seller.

@dexX7 says:

Storing "amount for sale" and "amount desired" instead of "unit price" may solve the problem of fractions.

I recommend storing unit price because it can be included in a table index, making it faster to find existing orders that match the new order.

@marv-engine
Copy link
Author

@m21 the group review and my discussion w/ JR and Adam happened after this thread, so this should not be an obstacle to merging PR #165.

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

No branches or pull requests

4 participants