Skip to content
This repository has been archived by the owner on Dec 16, 2022. It is now read-only.

[WIP] work on returnTicker #18

Closed
wants to merge 11 commits into from
Closed

[WIP] work on returnTicker #18

wants to merge 11 commits into from

Conversation

JonathonDunford
Copy link
Contributor

@JonathonDunford JonathonDunford commented Feb 9, 2018

We will need another database table for ticker information.

Proposed information to store:

ticker = {
        'tokenAddr': ,
        'quoteVolume': ,
        'baseVolume': ,
        'last': ,
        'bid': ,
        'ask': ,   
    }

bid and ask can come from orders
last can come from trades

Would volume have to be determined by adding all of the orders in our orderbook?

@freeatnet
Copy link
Contributor

How and when do we update bid and ask? Off the top of my head, they should at least change when new orders are created (and the price is higher / lower than previous one) and when there is no more volume available at the price reflected. How do we fetch that?

@JonathonDunford
Copy link
Contributor Author

We haven't finished the code for #8 but that could be where we change the bid/ask based on volume changes.

OR

We could have something that scans our own database on a regular frequency, compares it to ticker data, then changes ticker data where it sees fit.

We can invalidate orders separately and the change would be reflected there.

@@ -231,6 +231,36 @@ def format_order(record):

await sio.emit('market', response, room=sid)

#return ticker information, if no token specified show all
@sio.on('returnTicker')
Copy link
Contributor

@freeatnet freeatnet Feb 10, 2018

Choose a reason for hiding this comment

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

There isn't a separate returnTicker event: instead, returnTicker is a key on a market event, which is emitted to a specific user in response to a getMarket event by that user.

if token:

#get ticker for passed in token
tickers = await get_tickers(token)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should return all tickers regardless.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If you pass in a specific token, it shouldn't transmit unnecessary data?

Copy link
Contributor

Choose a reason for hiding this comment

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

This data fills out the values in the bottom-left ticker though, so it needs to be for all tokens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Referenced here: #21

Fixed now.

#get returnTicker, grabs data from tickers table
async def get_tickers(token_hexstr):

#init
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: according to PEP8, the standard Python code style guide, "They should start with a # and a single space."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

async def get_tickers(token_hexstr):

#init
where = ''
Copy link
Contributor

Choose a reason for hiding this comment

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

placeholder_args should also be defined here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed.

"quoteVolume": str(contract.denormalize_value(ticker["quote_volume"])),
"baseVolume": str(contract.denormalize_value(ticker["base_volume"])),
"last": str(contract.denormalize_value(ticker["last"])),
"percentChange": str(contract.denormalize_value(ticker["percent_change"])),
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a 100% sure what the values are going to look like in raw format, we should double-check these conversions once we have them.

"percentChange": str(contract.denormalize_value(ticker["percent_change"])),
"bid": str(contract.denormalize_value(ticker["bid"])),
"ask": str(contract.denormalize_value(ticker["ask"])),
"modified": datetime.now().isoformat()
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the modified value supposed to reflect?

Copy link
Contributor Author

@JonathonDunford JonathonDunford Feb 10, 2018

Choose a reason for hiding this comment

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

When the last time that ticker was updated

Copy link
Contributor

@freeatnet freeatnet Feb 10, 2018

Choose a reason for hiding this comment

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

Sounds like it should come from the DB then. Is this a placeholder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right. Whoops.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

"bid": str(contract.denormalize_value(ticker["bid"])),
"ask": str(contract.denormalize_value(ticker["ask"])),
"modified": ticker["modified"]
# "modified": datetime.now().isoformat()
Copy link
Contributor

Choose a reason for hiding this comment

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

Dead lines should be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Placeholder for when I write the code that updates the data. Will remove at that time.


# get ticker for passed in token
#tickers = await get_tickers(token)
tickers = await get_tickers()
Copy link
Contributor

Choose a reason for hiding this comment

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

We could probably extract that to above if we no longer differentiate by token.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

""".format(where),
*placeholder_args)

# format ticker information to camelcase from underscore, sanitize, add modified date
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, also, suggestion! Python docstrings might be a better way to do this

if token:

# get ticker for passed in token
#tickers = await get_tickers(token)
Copy link
Contributor

Choose a reason for hiding this comment

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

Also dead code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

if token:

Copy link
Contributor

Choose a reason for hiding this comment

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

Unnecessary new lines

@JonathonDunford
Copy link
Contributor Author

Now, we need something that actually populates this data.

I think the best method may be using an observer to scan our orders table and comparing actual data to the old data stored in tickers.

Maybe there is a better way to only update it on certain events, but this would be a catch all and we could cancel/update orders separately without fear of breaking this system.

@JonathonDunford
Copy link
Contributor Author

JonathonDunford commented Feb 11, 2018

Convo copied here for posterity.

BigBrother - Today at 10:56 AM
Well, instead of relying on ED data to update our tickers, we would scan our own orders + trades table to get bid/ask/volume from orders and last/percent from trades(edited)

freeatnet - Today at 10:57 AM
With you on that!

BigBrother - Today at 10:58 AM
And since the rest of the backend handles updating and making sure that data is accurate
regardless of ED connectability
That should be the most reliable source of data?
That's my thinking at least.

freeatnet - Today at 11:02 AM
Absolutely agreed. To clarify: In my mind, there are two ways of doing this:

  1. Relying on data we store, we periodically update ticker data from scratch by looking up the right orders and trades.
  2. Relying on data we process, we track certain events and update ticker incrementally, based on the context of the event

First way is much, much simpler. We may have revisit it for purposes of real-time updating in the future, but it should work for now.

Jonathon Dunford added 2 commits February 11, 2018 17:07
Clean websocket_server a little.
Copy link
Contributor

@freeatnet freeatnet left a comment

Choose a reason for hiding this comment

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

Some minor things pointed out.

I'm also not sure about iterating over all orders to build a ticker. Perhaps, the ticker updater should know a list of token addresses which it should update; then, for each token, fetch fields with specific SQL queries.

For example, a ticker bid value can be obtained by running something like

SELECT MAX(amount_give / amount_get)
FROM orders
WHERE token_get = :zero_addr
    AND token_give = :token_addr
    AND (state = 'OPEN'::orderstate AND expires > :current_block AND available_volume > 0)

This approach is a little harder on the DB, but it is faster than iterating over all orders and is somewhat simpler to implement than partial updates.

return

# Main function. Grabs orders and trades, then scans through orders and trades to update `tickers`
async def update_tickers():
Copy link
Contributor

Choose a reason for hiding this comment

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

Main function should be called main() and be called if the module is the main module of the python invocation, like so.

# Main function. Grabs orders and trades, then scans through orders and trades to update `tickers`
async def update_tickers():

orders = await get_orders();
Copy link
Contributor

Choose a reason for hiding this comment

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

No semicolons needed if you have just one expression / statement on a line.

return await conn.fetch(
"""
SELECT token_give, amount_give, token_get, amount_get, expires
FROM orders
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty good SELECT statement for pulling all orders!

app = web.Application()
sio.attach(app)

ZERO_ADDR = "0x0000000000000000000000000000000000000000"
Copy link
Contributor

Choose a reason for hiding this comment

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

We should extract these into reusable constants. app/constants.py, perhaps?


else:
# If there isn't a current recorded bid OR current recorded bid is lower than the bid in this order.
if not has_attr(ticker_updates[order["token_get"]], 'bid') || ticker_updates[order["token_get"]]["bid"] < this_orders_bid:
Copy link
Contributor

Choose a reason for hiding this comment

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

The logical or operator is or.

@JonathonDunford
Copy link
Contributor Author

JonathonDunford commented Feb 17, 2018

Moved to #40

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

Successfully merging this pull request may close these issues.

None yet

2 participants