Skip to content

Conversation

vctt94
Copy link
Member

@vctt94 vctt94 commented Jun 15, 2022

This PR closes #1143

I am adding a basic match struct into MatchProofNote so we can be notified on the client.

On the client I added a new recent matches table in our market page. Here how it looks right now:

This is how it looks right now:

image

smaller screen:

image

@chappjc
Copy link
Member

chappjc commented Jun 16, 2022

I noticed the MakerOID might be a parameter which causes confusion, as it is possible to have many makers into a limit order.

You're referring to the distinction between Match and MatchSet (both dex/order types), where a MatchSet has one taker order and N maker orders. Note that a Match has it's own Quantity that is not necessarily the amount of either maker or taker order. I think the []BasicMatch should be distilled from all the Matchs from the MatchSetss. In other words, if (*MatchSet).Matches returns 4 matches, there should be 4 BasicMatchs appended all with the same taker and different makers.

dcrdex/dex/order/match.go

Lines 227 to 240 in f6cccaa

// MatchSet represents the result of matching a single Taker order from the
// epoch queue with one or more standing limit orders from the book, the Makers.
// The Amounts and Rates of each standing order paired are stored. The Rates
// slice is for convenience since each rate must be the same as the Maker's
// rate. However, a amount in Amounts may be less than the full quantity of the
// corresponding Maker order, indicating a partial fill of the Maker. The sum
// of the amounts, Total, is provided for convenience.
type MatchSet struct {
Epoch EpochID
Taker Order
Makers []*LimitOrder
Amounts []uint64
Rates []uint64
Total uint64

@chappjc
Copy link
Member

chappjc commented Jun 16, 2022

rougly:

var basicMatches []*order.BasicMatch
for _, matchSet := range matches {
	for i, match := range matchSet.Matches() {
		basicMatches = append(basicMatches, &order.BasicMatch{
			MatchID:  match.ID(),
			MakerOID: match.Maker.ID(),
			TakerOID: match.Taker.ID(),
			Quantity: match.Quantity,
			Rate:     match.Rate,
		})
	}
}

@chappjc chappjc marked this pull request as draft June 16, 2022 01:08
@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from 1982168 to 6953c6e Compare June 16, 2022 18:33
@vctt94 vctt94 force-pushed the add-recently-matched-orders branch 4 times, most recently from 7cec474 to 0c07e6e Compare July 7, 2022 20:00
@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from 0c07e6e to 284e6f8 Compare July 15, 2022 15:06
@vctt94 vctt94 changed the title [wip] multi: show recently matched orders multi: show recently matched orders Jul 15, 2022
@vctt94 vctt94 marked this pull request as ready for review July 15, 2022 15:12
@@ -71,6 +72,8 @@ const animationLength = 500

const anHour = 60 * 60 * 1000 // milliseconds

// const MAX_RECENT_MATCHES_COUNT = 20
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we limit the amount of recent matches we show? How many?

case 'stamp':
return (a: BasicMatches, b: BasicMatches) => this.recentMatchesSortDirection * (b.age.getTime() - a.age.getTime())

// Sort by taker and maker?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does sorting by taker and maker make sense?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm only now getting back to this, but looking at the screenshot, I really don't think there's any point of showing the maker and taker order IDs at all.

What binance shows by default in a small section:

image

bittrex:

image

That's all we need to show. And we don't want to waste much real estate on the screen.

Copy link
Member

@chappjc chappjc Jul 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion to include the OIDs in the BasicMatch was really just for the server->client messaging, which gives the backend some more capability to audit the matches based on the match_proof and other orderbook subscription data. The frontend can be very simple.

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from 284e6f8 to f5095e5 Compare July 19, 2022 18:26
@vctt94 vctt94 marked this pull request as draft July 19, 2022 18:27
@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from f5095e5 to 5739b2f Compare July 22, 2022 12:56
@vctt94 vctt94 marked this pull request as ready for review July 22, 2022 13:01
Comment on lines 849 to 851
MatchID Bytes `json:"matchid"`
MakerOID Bytes `json:"maker"`
TakerOID Bytes `json:"taker"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@chappjc: My suggestion to include the OIDs in the BasicMatch was really just for the server->client messaging, which gives the backend some more capability to audit the matches based on the match_proof and other orderbook subscription data.

Can you be more specific?

This is > 200 bytes per match sent to every consumer. I think we'd need a pretty compelling reason to justify that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MatchID itself can be omitted, but what I'm describing is how an observer can determine what orders were matched in an epoch. This can only be done at present by:

  • subscribe to orderbook
  • observe all the epoch order notes in a full epoch
  • at epoch close use the match_proof note with the preimages listed to collect the orders that should be matched
  • run the matching algorithm against book - major burden, not for the casual user

The idea of just sending the matches (this PR) is to avoid the above if we just want matches for display purposes. If someone really wants to audit the matches, they can go to the trouble the steps above with an orderbook subscription.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree that a casual user needs that much detail, but I'm just one guy. How about we just do a slice of tuples?

Matches [][2]uint64 `json:"matches"` // [rate, qty]

and I guess we should send it in the EpochReportNote. Is it relevant to the match proof?

Copy link
Member

@chappjc chappjc Jul 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually what I'm suggesting in my other comment #1663 (comment) Except I think we can aggregate all matches for a given rate and sum the quantity.

I do concede that every match in an epoch isn't quite necessary, but I do think that giving the amounts and rates, even if aggregated over many matches, is important. No exchange I have ever seen does not give you this info. We need to be able to show something like the screenshots in #1663 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I think we can remove BasicMatch, and add

	// quantity of matches by rate: {rate}qty
	Matches map[uint64]uint64 `json:"matches"`

into MatchCycleStats so it is reported on EpochReportNote?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems reasonable. Using a map to aggregate quantities for a rate is certainly convenient (e.g. ms[rate]+=qty just works even before the first entry for a given rate).

As for the JSON in the epoch report, a slice of tuples might end up better for unmarshalling.

"matchSummary": [
  [11000, 20],
  [11400, 40],
  [12000, 60],
]

vs.

"matchSummary": {
  "11000": 20,
  "11400": 40,
  "12000": 60,
}

Because when you marshal a map[uint64]uint64, it makes the key into a string. But as long as the quoted key represents a number it unmarshals fine, so maybe it's fine. https://go.dev/play/p/uJO6JL64CuV Go ahead and feel it out.

Copy link
Member Author

@vctt94 vctt94 Aug 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed the sugested changes, aggregating in a map [rate]quantity.

Still need to fix the site build, to show this information properly, but pushed it before changing the front, so I can get your opinions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with a tuple would be that we would need to run the array in order to find the rate so we can sum new matches from the server, while with a map we would simple sum it up, like you said: ms[rate]+=qty.

So I believe it would be simpler to just convert the matchSummary from string to uint, but I am fine with either way.

Which one do you prefer @chappjc ?

Copy link
Member

@chappjc chappjc Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with a tuple would be that we would need to run the array in order to find the rate so we can sum new matches from the server, while with a map we would simple sum it up, like you said: ms[rate]+=qty.

I guess I have to review your current code, but I thought that aggregation happens only on the server to reduce an epochs matches, and we can send anything else to the client although it might require converting and sorting I suppose. (i.e. Use a map to simplify the aggregation at the end of an epoch, send a different format).

So I believe it would be simpler to just convert the matchSummary from string to uint, but I am fine with either way.

Which one do you prefer @chappjc ?

No preference yet. I'd have to get into the code and feel it out. I don't feel like it's a critical design decision except that updating a msgjson type commits us to live with that choice.

@buck54321
Copy link
Member

This whole thing is a hard sell for me. I think showing matches live makes sense when matches can occur at any time, but when there's epochs, we're really just displaying the results of the last match cycle, which is better summarized with statistics rather than specific information about the matches.

@vctt94
Copy link
Member Author

vctt94 commented Jul 29, 2022

By statistics you mean the percentage of matches in some price range?

@buck54321
Copy link
Member

How many matches and what was the average and the range of buy and sell prices. I'd like standard deviation too, but most users won't know what to do with it. That info can be encoded in tens of bytes, as opposed to individual matches which may be more like tens of kilobytes.

Note that the bandwidth requirements of additional notification data compounds with busier markets, since we have more activity (= larger match data reports) and more subscribers.

@chappjc
Copy link
Member

chappjc commented Jul 29, 2022

This whole thing is a hard sell for me. I think showing matches live makes sense when matches can occur at any time, but when there's epochs, we're really just displaying the results of the last match cycle, which is better summarized with statistics rather than specific information about the matches.

It's just that literally every exchange shows all fills.

How I can see the information distilled for an epoch is with pairings of cumulative amounts filled at different prices. Each amount can be the result of multiple fills, but I think it's fine to gloss over that and just report how much was filled at a certain price.

I think that reducing an epoch's matches to a couple numbers like avg, range, etc. is losing too much information. However, I'm on board to scrap sending every match made separately and condensing the info instead, just not that much.

@chappjc chappjc added this to the 0.6/1.0 milestone Jul 31, 2022
@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from 5739b2f to 79d3ad5 Compare August 1, 2022 19:52
@vctt94 vctt94 marked this pull request as draft August 1, 2022 19:55
LotSize uint64 `json:"lotsize"`
RateStep uint64 `json:"ratestep"`
MarketBuyBuffer float64 `json:"buybuffer"`
RecentMatches map[uint64]uint64 `json:"recentmatches"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The msgjson.Market type "describes a market and its variables, and is returned as part of a ConfigResult". This isn't the place for RecentMatches.
This is a message type, but we won't be setting and sending the data with it from the server. For just keeping track of things client side, we should use the client data structures.

basicMatches := make(map[uint64]uint64)
for _, matchSet := range matches {
for _, match := range matchSet.Matches() {
basicMatches[match.Rate] = match.Quantity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+-=?

}
// update recent matches value
for rate, qty := range matches {
market.RecentMatches[rate] += qty
Copy link
Member

@chappjc chappjc Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is forever growing. It shouldn't combine across epochs either. Time/epoch information is being thrown away, with no sense for what "recent" is.

The server should aggregate within just one epoch, while the client does should not aggregate at all.

A recent matches table on the markets page should be scrolling (but we can limit the number remembered), with each epoch containing matches appending to it.

image

Browse to either bittrex/binance and open the DCR/BTC market and watch the table.

Our table will obviously only update every epoch (like the candles) at the most, but there is a temporal element.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The EpochReportNote is a notification that is part of the "orderbook" subscription, so the place for data from these notes would likely be the bookie, like where the candles are maintained, not the dc.cfg.Markets that is part of the server's market config.
The time (the notion of what is recent) comes from the epoch index for the note.

@@ -217,6 +217,7 @@ func (dc *dexConnection) marketMap() map[string]*Market {
EpochLen: mkt.EpochLen,
StartEpoch: mkt.StartEpoch,
MarketBuyBuffer: mkt.MarketBuyBuffer,
RecentMatches: mkt.RecentMatches,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recent matches won't be be coming from a 'config' response (in the msgjson.Market). I wasn't sure the best place to keep the data, so I reviewed what we're doing for other 'orderbook' and 'spots' subscription notifications.

Currently we decided to use the EpochReportNote, which is part of the orderbook subscription, to communicate recent matches from the server in notifications. Time-stamped recent matches for a market would be tracked by some new fields in dexConnection (e.g. the spots and spotsMtx fields), or possibly in the bookie for a given market (see how (*bookie).logEpochReport(*msgjson.EpochReportNote) already does this for this note but for the Candle data).

Now note how we send BookUpdates to consumers like the frontend, with various "actions":

dcrdex/client/core/types.go

Lines 570 to 578 in 3a476f1

const (
FreshBookAction = "book"
FreshCandlesAction = "candles"
BookOrderAction = "book_order"
EpochOrderAction = "epoch_order"
UnbookOrderAction = "unbook_order"
UpdateRemainingAction = "update_remaining"
CandleUpdateAction = "candle_update"
)

So, I imagine we'd follow that pattern.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using now epoch_report to update recent matches

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch 2 times, most recently from 4d9e881 to 37f265f Compare August 4, 2022 14:51
@vctt94
Copy link
Member Author

vctt94 commented Aug 4, 2022

Thanks for the review chapp. I am now storing the recent matches at bookies, and adding age by epoch, so we can show them diferently.

One thing about this solution, is that it is not possible to know if it is a sell or not, as we are only sending number of qty and rate.

So I am showing everything with the same color:

image

Comment on lines 209 to 223

dc.booksMtx.RLock()
var recentMatches []*recentMatch
book := dc.books[mkt.Name]
if book != nil {
matchesSummary := book.GetRecentMatches()
for _, m := range matchesSummary {
recentMatches = append(recentMatches, &recentMatch{
Age: m.Age,
Qty: m.Qty,
Rate: m.Rate,
})
}
}
dc.booksMtx.RUnlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this makes sense here. Let's just keep recent matches as part of the order book feed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed. The reason I did that aws to have recent matches information on after refreshing the page, but I guess it is fine waiting for the next epoch

MarketBuyBuffer float64 `json:"buybuffer"`
Orders []*Order `json:"orders"`
SpotPrice *msgjson.Spot `json:"spot"`
RecentMatches []*recentMatch `json:"recentmatches"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per other comment, I think this should go. But, while we're here, we wouldn't have an exported field of unexported types.

recentMatch should be exported, and the RecentMatches field should move to core.OrderBook.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree about that. I don't think I was explicit this point in #1663 (comment), but this Market struct doesn't deal with order book things. The Orders that are in this struct are your own. The only possible violation there is the spot price, but we didn't include that in the OrderBook types.

Copy link
Member

@chappjc chappjc Aug 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also before we get too committed to the naming, RecentMatches and recentMatch suggests that each entry is an actual match, but this is a distillation of an epoch's matches into a summary. These form an epoch match summary, where each entry is a match quantity sum at a given price level.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only possible violation there is the spot price, but we didn't include that in the OrderBook types.

We get the spot price from the "price_feed" subscription instead of the order book feed, and we expect to have it for all markets all of the time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed! I was thinking of candles. My bad.

marketID := marketName(b.base, b.quote)
b.SetRecentMatches(note.MatchesSummary, note.EndStamp)
b.send(&BookUpdate{
Action: "epoch_report",
Copy link
Member

@chappjc chappjc Aug 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This action would need to be defined in a const, near where FreshBookAction is defined. But let's not use "epoch_report" since the recent match summary is just a subset of the epoch report message from the server. Perhaps epoch_match_summary

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from 37f265f to 26bd4de Compare August 11, 2022 12:30
@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from eca645c to 1ea7cc3 Compare August 25, 2022 20:25
@vctt94 vctt94 marked this pull request as ready for review August 25, 2022 20:25
Copy link
Member

@JoeGruffins JoeGruffins left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok to me.

Comment on lines 618 to 619
ob.matchSummaryMtx.Lock()
defer ob.matchSummaryMtx.Unlock()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need to hold the lock until after the range over matches.

@@ -604,3 +613,30 @@ func (ob *OrderBook) BestFill(sell bool, qty uint64) ([]*Fill, bool) {
func (ob *OrderBook) BestFillMarketBuy(qty, lotSize uint64) ([]*Fill, bool) {
return ob.sells.bestFill(qty, true, lotSize)
}

func (ob *OrderBook) SetMatchesSummary(matches map[uint64]uint64, ts uint64) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exported functions need docs.

Comment on lines 638 to 652
if ob.matchesSummary == nil {
ob.matchesSummary = make([]*MatchSummary, 0)
}
return ob.matchesSummary
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want to hold the lock for this.

ob.matchesSummary = newMatchesSummary
return
}
ob.matchesSummary = append(newMatchesSummary, ob.matchesSummary...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This grows forever. Maybe a good idea to prune sometimes? Does not look very big, but not sure whats best. Maybe is len is more than 100_000 chop off the first 10_000?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now if matchesSummary is bigger than 100, it will slice out the first ones added to the queue

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100 seems pretty low. I don't know whats best though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think you don't need to chop off so much. I suggest

if len(ob.matchesSummary) > maxLength {
        ob.matchesSummary = ob.matchesSummary[len(ob.matchesSummary)-maxLength+100:]
}

So, if the thing is over 1000 set it to 900.

@vctt94
Copy link
Member Author

vctt94 commented Aug 29, 2022

Thanks for the review joe

Comment on lines 631 to 642
ob.matchSummaryMtx.Lock()
if ob.matchesSummary == nil {
ob.matchesSummary = newMatchesSummary
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to defer the unlock or will remain locked on this return.

ob.matchesSummary = newMatchesSummary
return
}
ob.matchesSummary = append(newMatchesSummary, ob.matchesSummary...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

100 seems pretty low. I don't know whats best though.

Comment on lines 637 to 650
// if len is greater than 100, slice the first 100.
if len(ob.matchesSummary) > 100 {
ob.matchesSummary = ob.matchesSummary[0:100]
}
ob.matchSummaryMtx.Unlock()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only save tho older ones correct? I think you want to throw away the older ones, so ob.matchesSummary = ob.matchesSummary[100:]

https://go.dev/play/p/_Dg-K0wEnNA

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also, this would make more sense if len is over 1000 or so, otherwise would delete almost everything whenever it hits 100.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah 100 is too small.

Now I am doing 1000 and if it is greater than 1100.

Thanks for the suggestion

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from d0af1f1 to ad26185 Compare August 31, 2022 13:54
type MatchSummary struct {
Rate uint64
Qty uint64
Age uint64
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Time stamp is much more flexible. An age is stale as soon as you write it.

Comment on lines 647 to 672
if ob.matchesSummary == nil {
ob.matchesSummary = make([]*MatchSummary, 0)
}
ob.matchSummaryMtx.Unlock()
return ob.matchesSummary
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a great reason to ensure we are returning a non nil slice. Caller should handle it properly.

updateToolTipCol(tr, 'age', match.Age.toLocaleTimeString())
}

bindToRecentMatches (matches: RecentMatch[]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not binding anything

}
}

updateRecentMatchRow (tr: HTMLElement, match: RecentMatch) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only used once. Wanna just do it inline?

@@ -1176,6 +1176,8 @@ type EpochReportNote struct {
Epoch uint64 `json:"epoch"`
BaseFeeRate uint64 `json:"baseFeeRate"`
QuoteFeeRate uint64 `json:"quoteFeeRate"`
// quantity of matches by rate: [rate]qty
MatchesSummary map[uint64]uint64 `json:"matchessumary"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. You're missing an m in summary
  2. This should be camel-case

Comment on lines 319 to 321
Rate: number
Qty: number
Age: Date
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these upper-case? Add JSON tags or do type conversion as needed to fix this.

@@ -1176,6 +1176,8 @@ type EpochReportNote struct {
Epoch uint64 `json:"epoch"`
BaseFeeRate uint64 `json:"baseFeeRate"`
QuoteFeeRate uint64 `json:"quoteFeeRate"`
// quantity of matches by rate: [rate]qty
MatchesSummary map[uint64]uint64 `json:"matchessumary"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about separating into buys and sells so we can apply colors eventually.

Copy link
Member

@chappjc chappjc Aug 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The is one issue with aggregating. Some are takers buying, some are takers selling, combined in quantity at a given rate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can keep separate maps/slices all the way down.

Copy link
Member

@chappjc chappjc Aug 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me. To be clear you mean in processEpochReady, two maps server-side, one for aggregated taker sells and one for aggregated taker buys?
match.Taker.Trade().Sell
Then send em on down to client that way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. I guess we lose order information that way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's roll with this as is. If we want to iterate later, we can.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By let's rool with this as is, you mean as is right now, or with the taker sell and taker buy map? I think it woul be good separating buyers and sellers

Copy link
Member

@chappjc chappjc Sep 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's super clear to you how to get two maps and show them in different colors, you can go for it if it doesn't eat much of your time. It's just that we were chatting in matrix and we're just a little undecided how we ultimately want the matches, considering different or even no aggregation again.

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from ad26185 to b4bb476 Compare September 6, 2022 19:40
@vctt94
Copy link
Member Author

vctt94 commented Sep 6, 2022

Thanks for the review.

That's how it looks after last review:

image

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from b4bb476 to 7f4a761 Compare September 6, 2022 19:51
@chappjc
Copy link
Member

chappjc commented Sep 6, 2022

Nice, thanks. That should be good to merge and move to next steps. I'm pretty certain we don't want recent matches taking up nearly that much real estate, more like a small column carved out of where the current depth chart is, but we can refine after merging.

@chappjc
Copy link
Member

chappjc commented Sep 6, 2022

A couple things wrong.

image

The "Age" column shows just a date. The column should be "Time", and the time should be more precise than just the date.
Please see the model screen captures in #1663 (comment)
Consistent with those examples would be just the time, no date.

Other thing is that the colors (or the sell bool) is backwards. Sell=true means the taker order was a sell, but it seems to be showing the opposite.

basicMatchesBuy := make(map[uint64]uint64)
for _, matchSet := range matches {
for _, match := range matchSet.Matches() {
if match.Maker.Sell {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if match.Taker.Sell.

I'm 99% certain that the buy/sell for a match considers the taker.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I was confused by this. Believe it is properly now.

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from 094fb92 to c6d1b1a Compare September 7, 2022 16:07
if match.Taker.Trade() != nil && match.Taker.Trade().Sell {
basicMatchesSell[match.Rate] += match.Quantity
} else {
basicMatchesBuy[match.Rate] += match.Quantity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this create a 0 -> 0 bucket for cancel order matches?

@vctt94 vctt94 force-pushed the add-recently-matched-orders branch from c6d1b1a to 425a4f8 Compare September 20, 2022 15:27
@chappjc chappjc merged commit f9a565e into decred:master Sep 20, 2022
@chappjc
Copy link
Member

chappjc commented Sep 20, 2022

Thanks. I think we'll iterate on the frontend parts a bit.

Perhaps also we'll look at having SetMatchesSummary take both buys and sells, and then return the new match summary so we don't have to explicity go get it.

We also will look into different aggregation server-side, but this is a basis to experiment.

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

Successfully merging this pull request may close these issues.

ui: show recently matched orders
4 participants