-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #34 from rstudio/aron-simpler-content-deps
simplify content and its dependencies
- Loading branch information
Showing
11 changed files
with
315 additions
and
13,691 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,10 @@ | ||
# Stock Pricing Service | ||
# Simple Flask application | ||
|
||
## About this example | ||
Recreate the `manifest.json` with: | ||
|
||
An API allows you to turn your models into production services that other tools and teams can use. APIs are a great way for software engineering teams to use your models without translating them into different languages. | ||
|
||
|
||
## Learn more | ||
|
||
* [Flask Introduction](https://palletsprojects.com/p/flask/) | ||
* [Flask Documentation](https://flask.palletsprojects.com/en/1.1.x/) | ||
|
||
## Requirements | ||
|
||
* Python 2 versions 2.7 or higher; Python 3 versions 3.5 or higher | ||
```bash | ||
python3.11 -m venv env | ||
. ./env/bin/activate | ||
pip install rsconnect-python | ||
rsconnect write-manifest flask --overwrite . | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,131 +1,20 @@ | ||
# -*- coding: utf-8 -*- | ||
import os | ||
import numpy as np | ||
import pandas as pd | ||
from flask import Flask | ||
from flask_restx import Api, Resource, fields | ||
from flask import Flask, jsonify, request, url_for | ||
|
||
# Fetch prices from local CSV using pandas | ||
prices = pd.read_csv( | ||
os.path.join(os.path.dirname(__file__), "prices.csv"), | ||
index_col=0, | ||
parse_dates=True, | ||
) | ||
|
||
|
||
# Configure the Flask app using RestX for swagger documentation | ||
app = Flask(__name__) | ||
app.config["SWAGGER_UI_DOC_EXPANSION"] = "list" | ||
app.config["RESTX_MASK_SWAGGER"] = False | ||
app.config["ERROR_INCLUDE_MESSAGE"] = False | ||
|
||
|
||
api = Api( | ||
app, | ||
version="0.1.0", | ||
title="Stocks API", | ||
description="The Stocks API provides pricing and volatility data for a " | ||
"limited number of US equities from 2010-2018", | ||
) | ||
ns = api.namespace("stocks") | ||
|
||
# Define stock and price models for marshalling and documenting response objects | ||
tickers_model = ns.model( | ||
"Tickers", | ||
{ | ||
"tickers": fields.List( | ||
fields.String(description="Ticker of the stock"), | ||
description="All available stock tickers", | ||
), | ||
}, | ||
) | ||
|
||
stock_model = ns.model( | ||
"Stock", | ||
{ | ||
"ticker": fields.String(description="Ticker of the stock"), | ||
"price": fields.Float(description="Latest price of the stock"), | ||
"volatility": fields.Float(description="Latest volatility of the stock price"), | ||
}, | ||
) | ||
|
||
price_model = ns.model( | ||
"Price", | ||
{ | ||
"date": fields.Date, | ||
"high": fields.Float(description="High price for this date"), | ||
"low": fields.Float(description="Low price for this date"), | ||
"close": fields.Float(description="Closing price for this date"), | ||
"volume": fields.Integer(description="Daily volume for this date"), | ||
"adjusted": fields.Float(description="Split-adjusted price for this date"), | ||
}, | ||
) | ||
|
||
|
||
class TickerNotFound(Exception): | ||
def __init__(self, ticker): | ||
self.ticker = ticker | ||
self.message = "Ticker `{}` not found".format(self.ticker) | ||
|
||
def __str__(self): | ||
return "TickerNotFound('{}')".format(self.ticker) | ||
|
||
|
||
# Our simple API only has a few GET endpoints | ||
@ns.route("/") | ||
class StockList(Resource): | ||
"""Shows a list of all available tickers""" | ||
|
||
@ns.marshal_with(tickers_model) | ||
def get(self): | ||
tickers = prices["ticker"].unique() | ||
return {"tickers": tickers} | ||
|
||
|
||
@ns.route("/<string:ticker>") | ||
@ns.response(404, "Ticker not found") | ||
@ns.param("ticker", "The ticker for the stock") | ||
class Stock(Resource): | ||
"""Shows the latest price and volatility for the specified stock""" | ||
|
||
@ns.marshal_list_with(stock_model) | ||
def get(self, ticker): | ||
if ticker not in prices["ticker"].unique(): | ||
raise TickerNotFound(ticker) | ||
|
||
ticker_prices = prices[prices["ticker"] == ticker] | ||
current_price = ticker_prices["close"].last("1d").round(2) | ||
current_volatility = np.log( | ||
ticker_prices["adjusted"] / ticker_prices["adjusted"].shift(1) | ||
).var() | ||
|
||
return { | ||
"ticker": ticker, | ||
"price": current_price, | ||
"volatility": current_volatility, | ||
@app.route("/ping") | ||
def ping(): | ||
return jsonify( | ||
{ | ||
"headers": dict(request.headers), | ||
"environ": dict(os.environ), | ||
"link": url_for("ping"), | ||
"external_link": url_for("ping", _external=True), | ||
} | ||
|
||
|
||
@ns.route("/<string:ticker>/history") | ||
@ns.response(404, "Ticker not found") | ||
@ns.param("ticker", "The ticker for the stock") | ||
class StockHistory(Resource): | ||
"""Shows the price history for the specified stock""" | ||
|
||
@ns.marshal_list_with(price_model) | ||
def get(self, ticker): | ||
if ticker not in prices["ticker"].unique(): | ||
raise TickerNotFound(ticker) | ||
|
||
ticker_prices = prices[prices["ticker"] == ticker] | ||
ticker_prices["date"] = ticker_prices.index | ||
return ticker_prices.to_dict("records") | ||
|
||
|
||
@api.errorhandler(TickerNotFound) | ||
def handle_ticker_not_found(error): | ||
return {"message": error.message}, 404 | ||
) | ||
|
||
|
||
if __name__ == "__main__": | ||
app.run(debug=True) | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,27 @@ | ||
{ | ||
"version": 1, | ||
"locale": "en_US.UTF-8", | ||
"metadata": { | ||
"appmode": "python-api", | ||
"entrypoint": "app" | ||
}, | ||
"locale": "en_US.UTF-8", | ||
"python": { | ||
"version": "3.8.3", | ||
"version": "3.11.8", | ||
"package_manager": { | ||
"name": "pip", | ||
"version": "19.2.3", | ||
"version": "24.0", | ||
"package_file": "requirements.txt" | ||
} | ||
}, | ||
"files": { | ||
"requirements.txt": { | ||
"checksum": "8932b9a8a1e488ba6dac3592d8012ced" | ||
"checksum": "cfdea7934019617dc8e2d9a7ea631e00" | ||
}, | ||
"README.md": { | ||
"checksum": "5a68ee77af4872bc1b0187a0adb26a5c" | ||
"checksum": "6c47ae2907b4af6076c12704f2e3a375" | ||
}, | ||
"app.py": { | ||
"checksum": "9799c3b834b555cf02e5896ad2997674" | ||
}, | ||
"prices.csv": { | ||
"checksum": "e0bc27e3dd358c360863807e09079985" | ||
}, | ||
"thumbnail.jpg": { | ||
"checksum": "c25a15924781145e8831661e641e5202" | ||
"checksum": "564fcab35569c7ee7b3ddc206fe8cfd0" | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.