CS50 Finance is CS50 Psets I ahve solved.
Implement a website via which users can “buy” and “sell” stocks, a la the below.
C$50 Finance, a web app via which you can manage portfolios of stocks. Not only will this tool allow you to check real stocks’ actual prices and portfolios’ values, it will also let you buy (okay, “buy”) and sell (okay, “sell”) stocks by querying IEX for stocks’ prices.
Indeed, IEX lets you download stock quotes via their API (application programming interface) using URLs like https://api.iex.cloud/v1/data/core/quote/nflx?token=API_KEY. Notice how Netflix’s symbol (NFLX) is embedded in this URL; that’s how IEX knows whose data to return. That link won’t actually return any data because IEX requires you to use an API key (more about that in a bit), but if it did, you’d see a response in JSON (JavaScript Object Notation) format like this:
{ "avgTotalVolume": 4329597, "calculationPrice": "tops", "change": 1.21, "changePercent": 0.00186, "closeSource": "official", "companyName": "NetFlix Inc", "currency": "USD", "iexAskPrice": 662.59, "iexAskSize": 8080, "iexBidPrice": 652.65, "iexBidSize": 102, "iexClose": 652.66, "iexCloseTime": 1636479523133, "iexLastUpdated": 1636479523133, "iexMarketPercent": 0.03419734093274243, "iexOpen": 652.66, "iexOpenTime": 1636479523133, "iexRealtimePrice": 652.66, "iexRealtimeSize": 30, "iexVolume": 43968, "lastTradeTime": 1636479523133, "latestPrice": 652.66, "latestSource": "IEX real time price", "latestTime": "12:38:43 PM", "latestUpdate": 1636479523133, "marketCap": 288341929921, "openSource": "official", "peRatio": 58.85, "previousClose": 651.45, "previousVolume": 2887523, "primaryExchange": "NASDAQ", "symbol": "NFLX", "week52High": 690.97, "week52Low": 463.41, "ytdChange": 0.2066202315388457 } Notice how, between the curly braces, there’s a comma-separated list of key-value pairs, with a colon separating each key from its value.
Let’s turn our attention now to getting this problem’s distribution code!
Before getting started on this assignment, we’ll need to register for an API key in order to be able to query IEX’s data. To do so, follow these steps:
Visit iexcloud.io/cloud-login#/register/. Select the “Individual” account type, then enter your name, email address, and a password, and click “Create account”. Once registered, scroll down to “Get started for free” and click “Select Start plan” to choose the free plan. Once you’ve confirmed your account via a confirmation email, visit https://iexcloud.io/console/tokens. Copy the key that appears under the Token column (it should begin with pk_). In your terminal window, execute: $ export API_KEY=value where value is that (pasted) value, without any space immediately before or after the =. You also may wish to paste that value in a text document somewhere, in case you need it again later.
Start Flask’s built-in web server (within finance/):
$ flask run Visit the URL outputted by flask to see the distribution code in action. You won’t be able to log in or register, though, just yet!
Within finance/, run sqlite3 finance.db to open finance.db with sqlite3. If you run .schema in the SQLite prompt, notice how finance.db comes with a table called users. Take a look at its structure (i.e., schema). Notice how, by default, new users will receive $10,000 in cash. But if you run SELECT * FROM users;, there aren’t (yet!) any users (i.e., rows) therein to browse.
Another way to view finance.db is with a program called phpLiteAdmin. Click on finance.db in your codespace’s file browser, then click the link shown underneath the text “Please visit the following link to authorize GitHub Preview”. You should see information about the database itself, as well as a table, users, just like you saw in the sqlite3 prompt with .schema.
Open up app.py. Atop the file are a bunch of imports, among them CS50’s SQL module and a few helper functions. More on those soon.
After configuring Flask, notice how this file disables caching of responses (provided you’re in debugging mode, which you are by default in your code50 codespace), lest you make a change to some file but your browser not notice. Notice next how it configures Jinja with a custom “filter,” usd, a function (defined in helpers.py) that will make it easier to format values as US dollars (USD). It then further configures Flask to store sessions on the local filesystem (i.e., disk) as opposed to storing them inside of (digitally signed) cookies, which is Flask’s default. The file then configures CS50’s SQL module to use finance.db.
Thereafter are a whole bunch of routes, only two of which are fully implemented: login and logout. Read through the implementation of login first. Notice how it uses db.execute (from CS50’s library) to query finance.db. And notice how it uses check_password_hash to compare hashes of users’ passwords. Also notice how login “remembers” that a user is logged in by storing his or her user_id, an INTEGER, in session. That way, any of this file’s routes can check which user, if any, is logged in. Finally, notice how once the user has successfully logged in, login will redirect to "/", taking the user to their home page. Meanwhile, notice how logout simply clears session, effectively logging a user out.
Notice how most routes are “decorated” with @login_required (a function defined in helpers.py too). That decorator ensures that, if a user tries to visit any of those routes, he or she will first be redirected to login so as to log in.
Notice too how most routes support GET and POST. Even so, most of them (for now!) simply return an “apology,” since they’re not yet implemented.
Next take a look at helpers.py. Ah, there’s the implementation of apology. Notice how it ultimately renders a template, apology.html. It also happens to define within itself another function, escape, that it simply uses to replace special characters in apologies. By defining escape inside of apology, we’ve scoped the former to the latter alone; no other functions will be able (or need) to call it.
Next in the file is login_required. No worries if this one’s a bit cryptic, but if you’ve ever wondered how a function can return another function, here’s an example!
Thereafter is lookup, a function that, given a symbol (e.g., NFLX), returns a stock quote for a company in the form of a dict with three keys: name, whose value is a str, the name of the company; price, whose value is a float; and symbol, whose value is a str, a canonicalized (uppercase) version of a stock’s symbol, irrespective of how that symbol was capitalized when passed into lookup.
Last in the file is usd, a short function that simply formats a float as USD (e.g., 1234.56 is formatted as $1,234.56).
Next take a quick look at requirements.txt. That file simply prescribes the packages on which this app will depend.
Glance too at static/, inside of which is styles.css. That’s where some initial CSS lives. You’re welcome to alter it as you see fit.
Now look in templates/. In login.html is, essentially, just an HTML form, stylized with Bootstrap. In apology.html, meanwhile, is a template for an apology. Recall that apology in helpers.py took two arguments: message, which was passed to render_template as the value of bottom, and, optionally, code, which was passed to render_template as the value of top. Notice in apology.html how those values are ultimately used! And here’s why 0:-)
Last up is layout.html. It’s a bit bigger than usual, but that’s mostly because it comes with a fancy, mobile-friendly “navbar” (navigation bar), also based on Bootstrap. Notice how it defines a block, main, inside of which templates (including apology.html and login.html) shall go. It also includes support for Flask’s message flashing so that you can relay messages from one route to another for the user to see.