-
Notifications
You must be signed in to change notification settings - Fork 126
multi: show recently matched orders #1663
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
Conversation
You're referring to the distinction between Lines 227 to 240 in f6cccaa
|
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,
})
}
} |
1982168
to
6953c6e
Compare
7cec474
to
0c07e6e
Compare
0c07e6e
to
284e6f8
Compare
@@ -71,6 +72,8 @@ const animationLength = 500 | |||
|
|||
const anHour = 60 * 60 * 1000 // milliseconds | |||
|
|||
// const MAX_RECENT_MATCHES_COUNT = 20 |
There was a problem hiding this comment.
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? |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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:
bittrex:
That's all we need to show. And we don't want to waste much real estate on the screen.
There was a problem hiding this comment.
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.
284e6f8
to
f5095e5
Compare
f5095e5
to
5739b2f
Compare
dex/msgjson/types.go
Outdated
MatchID Bytes `json:"matchid"` | ||
MakerOID Bytes `json:"maker"` | ||
TakerOID Bytes `json:"taker"` |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
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. |
By statistics you mean the percentage of matches in some price range? |
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. |
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. |
5739b2f
to
79d3ad5
Compare
dex/msgjson/types.go
Outdated
LotSize uint64 `json:"lotsize"` | ||
RateStep uint64 `json:"ratestep"` | ||
MarketBuyBuffer float64 `json:"buybuffer"` | ||
RecentMatches map[uint64]uint64 `json:"recentmatches"` |
There was a problem hiding this comment.
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.
server/market/market.go
Outdated
basicMatches := make(map[uint64]uint64) | ||
for _, matchSet := range matches { | ||
for _, match := range matchSet.Matches() { | ||
basicMatches[match.Rate] = match.Quantity |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+-=
?
client/core/core.go
Outdated
} | ||
// update recent matches value | ||
for rate, qty := range matches { | ||
market.RecentMatches[rate] += qty |
There was a problem hiding this comment.
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.
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.
There was a problem hiding this comment.
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.
client/core/core.go
Outdated
@@ -217,6 +217,7 @@ func (dc *dexConnection) marketMap() map[string]*Market { | |||
EpochLen: mkt.EpochLen, | |||
StartEpoch: mkt.StartEpoch, | |||
MarketBuyBuffer: mkt.MarketBuyBuffer, | |||
RecentMatches: mkt.RecentMatches, |
There was a problem hiding this comment.
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 BookUpdate
s to consumers like the frontend, with various "actions":
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.
There was a problem hiding this comment.
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
4d9e881
to
37f265f
Compare
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: |
client/core/core.go
Outdated
|
||
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() |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
client/core/types.go
Outdated
MarketBuyBuffer float64 `json:"buybuffer"` | ||
Orders []*Order `json:"orders"` | ||
SpotPrice *msgjson.Spot `json:"spot"` | ||
RecentMatches []*recentMatch `json:"recentmatches"` |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
client/core/bookie.go
Outdated
marketID := marketName(b.base, b.quote) | ||
b.SetRecentMatches(note.MatchesSummary, note.EndStamp) | ||
b.send(&BookUpdate{ | ||
Action: "epoch_report", |
There was a problem hiding this comment.
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
37f265f
to
26bd4de
Compare
eca645c
to
1ea7cc3
Compare
There was a problem hiding this 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.
client/orderbook/orderbook.go
Outdated
ob.matchSummaryMtx.Lock() | ||
defer ob.matchSummaryMtx.Unlock() |
There was a problem hiding this comment.
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.
client/orderbook/orderbook.go
Outdated
@@ -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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exported functions need docs.
client/orderbook/orderbook.go
Outdated
if ob.matchesSummary == nil { | ||
ob.matchesSummary = make([]*MatchSummary, 0) | ||
} | ||
return ob.matchesSummary |
There was a problem hiding this comment.
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...) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Thanks for the review joe |
client/orderbook/orderbook.go
Outdated
ob.matchSummaryMtx.Lock() | ||
if ob.matchesSummary == nil { | ||
ob.matchesSummary = newMatchesSummary | ||
return | ||
} |
There was a problem hiding this comment.
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...) |
There was a problem hiding this comment.
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.
client/orderbook/orderbook.go
Outdated
// if len is greater than 100, slice the first 100. | ||
if len(ob.matchesSummary) > 100 { | ||
ob.matchesSummary = ob.matchesSummary[0:100] | ||
} | ||
ob.matchSummaryMtx.Unlock() | ||
} |
There was a problem hiding this comment.
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:]
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
d0af1f1
to
ad26185
Compare
client/orderbook/orderbook.go
Outdated
type MatchSummary struct { | ||
Rate uint64 | ||
Qty uint64 | ||
Age uint64 |
There was a problem hiding this comment.
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.
client/orderbook/orderbook.go
Outdated
if ob.matchesSummary == nil { | ||
ob.matchesSummary = make([]*MatchSummary, 0) | ||
} | ||
ob.matchSummaryMtx.Unlock() | ||
return ob.matchesSummary | ||
} |
There was a problem hiding this comment.
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[]) { |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
dex/msgjson/types.go
Outdated
@@ -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"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- You're missing an m in summary
- This should be camel-case
Rate: number | ||
Qty: number | ||
Age: Date |
There was a problem hiding this comment.
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.
dex/msgjson/types.go
Outdated
@@ -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"` |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
ad26185
to
b4bb476
Compare
b4bb476
to
7f4a761
Compare
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. |
A couple things wrong. The "Age" column shows just a date. The column should be "Time", and the time should be more precise than just the 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. |
server/market/market.go
Outdated
basicMatchesBuy := make(map[uint64]uint64) | ||
for _, matchSet := range matches { | ||
for _, match := range matchSet.Matches() { | ||
if match.Maker.Sell { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
094fb92
to
c6d1b1a
Compare
if match.Taker.Trade() != nil && match.Taker.Trade().Sell { | ||
basicMatchesSell[match.Rate] += match.Quantity | ||
} else { | ||
basicMatchesBuy[match.Rate] += match.Quantity |
There was a problem hiding this comment.
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?
c6d1b1a
to
425a4f8
Compare
Thanks. I think we'll iterate on the frontend parts a bit. Perhaps also we'll look at having We also will look into different aggregation server-side, but this is a basis to experiment. |
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:
smaller screen: