<style>
html, body, .ui-content {
background-color: #333;
color: #ddd;
}
body > .ui-infobar {
display: none;
}
.ui-view-area > .ui-infobar {
display: block;
}
.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4,
.markdown-body h5,
.markdown-body h6 {
color: #ddd;
}
.markdown-body h1,
.markdown-body h2 {
border-bottom-color: #ffffff69;
}
.markdown-body h1 .octicon-link,
.markdown-body h2 .octicon-link,
.markdown-body h3 .octicon-link,
.markdown-body h4 .octicon-link,
.markdown-body h5 .octicon-link,
.markdown-body h6 .octicon-link {
color: #fff;
}
.markdown-body img {
background-color: transparent;
}
.ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a {
color: white;
border-left: 2px solid white;
}
.expand-toggle:hover,
.expand-toggle:focus,
.back-to-top:hover,
.back-to-top:focus,
.go-to-bottom:hover,
.go-to-bottom:focus {
color: white;
}
.ui-toc-dropdown {
background-color: #333;
}
.ui-toc-label.btn {
background-color: #191919;
color: white;
}
.ui-toc-dropdown .nav>li>a:focus,
.ui-toc-dropdown .nav>li>a:hover {
color: white;
border-left: 1px solid white;
}
.markdown-body blockquote {
color: #bcbcbc;
}
.markdown-body table tr {
background-color: #5f5f5f;
}
.markdown-body table tr:nth-child(2n) {
background-color: #4f4f4f;
}
.markdown-body code,
.markdown-body tt {
color: #eee;
background-color: rgba(230, 230, 230, 0.36);
}
a,
.open-files-container li.selected a {
color: #5EB7E0;
}
</style>}
# Python + Flask 虛擬美國股票交易網站 Part8 (Watchlist/ 顯示持股清單/ 查詢交易紀錄)
###### tags: `CS50` `Python` `Flask`
## 前言
前面已經把基本的交易股票功能做好了,再來就是要在網頁顯示現在所持有的個股,以及如果有需要,可以到個人交易頁面查詢交易紀錄。
## 新增至觀察清單
新增到觀察清單的途徑是先使用quote功能查詢股票資訊,再從股票個資的頁面按加入觀察清單來新增。因為一樣需要登入才能使用此功能,所以也是將這個程式碼放在`admin.py` 裡。
```
@admin_bp.route("/watchlist/new", methods=['GET', 'POST'])
@login_required
def add_to_watchlist():
symbol = request.args.get("symbol")
symbol_in_watchlist = Watchlist.query.filter_by(symbol = symbol)
for stock in symbol_in_watchlist:
if stock.symbol == symbol:
flash("Already existed in your watchlist",'success')
return redirect(url_for('admin.show_watchlist'))
watchlist = Watchlist(symbol = symbol)
db.session.add(watchlist)
current_user.watchlist.append(watchlist)
db.session.commit()
return redirect(url_for('admin.show_watchlist'))
```
接著是顯示觀察清單的頁面 vfinance/templates/admin/watchlist.html
```
{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}WatchList{% endblock %}
{% block content %}
<div class="page-header">
<h1>Wathchlist
<small class='text-muted'>{{ pagination.total }}</small>
</h1>
{% if watchlist %}
<table class='table table-striped'>
<thead>
<th>Symbol</th>
<th>Price</th>
<th>Change</th>
<th>ChangePercent</th>
<th>Open</th>
<th>High</th>
<th>Low</th>
<th>Volume</th>
<th>Week52High</th>
<th>Week52Low </th>
</thead>
{% for n in range(pagination.total) %}
<tr>
<td><a href="{{ url_for('admin.show_quote', symbol = symbols[n])}}">{{ symbols[n] }}</a></td>
<td>{{ symbols[n]}}</td>
<td>{{ changes[n] }}</td>
<td>{{ changePercents[n] }}</td>
<td>{{ openprices[n] }}</td>
<td>{{ highs[n] }}</td>
<td>{{ lows[n] }}</td>
<td>{{ volumes[n] }}</td>
<td>{{ week52Highs[n] }}</td>
<td>{{ week52Lows[n] }} </td>
<td>
<form class="inline" method="post"
action="{{ url_for('admin.delete_from_watchlist', symbol = symbols[n], next=request.full_path) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');">Delete
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
{% block footer %}{% endblock %}
```
在觀察清單中,每隻個股的最後一個欄位放了一個delete按鈕,只要當用戶對這個股票心灰意冷的時候,就可以按下去。
```
<form class="inline" method="post" action="{{ url_for('admin.delete_from_watchlist', symbol = symbols[n], next=request.full_path) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?');">Delete</button>
</form>
```
## 用戶首頁
從資料庫查找用戶的portfolio是否有值,有的話就會渲染出來。並簡單的計算一下現在的持股水位
```
from flask import render_template, flash, redirect, url_for, request, current_app, Blueprint, abort, make_response
from flask_login import current_user
from vfinance.forms import QuoteForm
from vfinance.models import TradeHistory, Portfolio, User
from vfinance.utils import lookup
from vfinance.extensions import db
home_bp = Blueprint("home", __name__)
@home_bp.route("/")
def index():
if current_user.is_authenticated:
form = QuoteForm()
if form.validate_on_submit():
symbol = form.symbol.data
return redirect(url_for('admin.show_quote', symbol = symbol))
cash = current_user.cash
page = request.args.get('page',1,type=int)
per_page = 15
pagination = Portfolio.query.with_parent(current_user).order_by(Portfolio.name.asc()).paginate(page,per_page)
portfolio = pagination.items
prices = []
position = 0
if portfolio:
for stock in portfolio:
quote = lookup(stock.symbol)
quantity = stock.quantity
price = quote['price']
prices.append(price)
position += float(quantity)*float(price)
current_user.position = position
account_value = float(cash) + float(position)
db.session.commit()
return render_template("home/index.html", portfolio= portfolio, pagination = pagination, prices = prices, cash = cash, position = position, account_value = account_value, form = form)
else:
return render_template("home/index.html")
```
```
{% extends "base.html" %}
{% from 'bootstrap/pagination.html' import render_pagination %}
{% block title %}
Home
{% endblock title %}
{% block content %}
{% if current_user.is_authenticated %}
<div class="page-header">
<h1>Welcome: {{ current_user.username }}</h1>
<h3>Account Value: {{ account_value }}</h3>
<h3>Buying power: {{ cash }}</h3>
<h3>Position: {{ position }}</h3>
</div>
<div class='row'>
<div class='col-sm-10'>
{% if portfolio %}
<h3>
Portfolio<small class='text-muted'>{{ pagination.total }}</small>
</h3>
<table class="table table-striped">
<thead>
<tr>
<th>Symbol</th>
<th>Qty</th>
<th>Pruch</th>
<th>Mkt Price</th>
</tr>
</thead>
{% for n in range(pagination.total) %}
<tr>
<td><a href="{{ url_for('admin.show_quote', symbol = portfolio[n].symbol)}}">{{ portfolio[n].symbol}}</a></td>
<td>{{ portfolio[n].quantity}}</td>
<td>{{ portfolio[n].purchase_price}}</td>
<td>{{lookup(portfolio[n].symbol)['price']}}</td>
</tr>
{% endfor %}
</table>
{% endif %}
<div class="page-footer">{{ render_pagination(pagination) }}</div>
</div>
<div class='col-sm-2 sidebar'>
{% include "home/_sidebar.html" %}
</div>
</div>
{% endif %}
{% endblock content %}
```
在用戶首頁的模板中,插入了_sidebar.html 主要是能讓用戶在首頁也看到自己觀察清單的價位
vfinance/templates/home.html
```
{% if watchlist %}
<div class='row'>
<div class="card mb-6">
<div class="card-header">Watchlist</div>
<ul class="list-group list-group-flush">
{% for n in range(watchlist|length) %}
<li class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
<a href="{{ url_for('admin.show_quote', symbol = watchlist[n].symbol)}}">
{{ watchlist[n].symbol}}
</a>
<a>
{{ priceInWatchlist[n] }}
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
```
## 交易明細
每次交易的時候都會紀錄交易明細,現在只要寫個function把交易明細從資料庫拿出來然後渲染到一個html上面就好了
```
@admin_bp.route("/trade_history", methods=['GET', 'POST'])
@login_required
def trade_history():
# get data from database
page = request.args.get('page', 1, type=int)
per_page = 10
pagination = TradeHistory.query.with_parent(current_user).order_by(TradeHistory.timestamp.desc()).paginate(page, per_page)
trade_history = pagination.items
return render_template("admin/trade_history.html", trade_history = trade_history, pagination = pagination)
```
vfinance/templates/admin/trade_histoy.html
```
{% extends 'base.html' %}
{% from 'bootstrap/form.html' import render_form %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="page-header">
<h1>Trade History
<small class="text-muted">{{ pagination.total }}</small>
</h1>
</div>
{% if trade_history %}
<table class= 'table table-striped'>
<thead>
<tr>
<th>TimeStamp</th>
<th>Transaction</th>
<th>Change</th>
</tr>
</thead>
{% for trade in trade_history %}
<tr>
<td>{{trade.timestamp}}</td>
<td>{{ trade.symbol }} {{ trade.action }} {{ trade.quantity }} @ {{ trade.price }}</td>
<td>{% if trade.action =="Buy" %}
{{ (trade.quantity)*(trade.price)*(-1)}}
{% else %}
{{ (trade.quantity)*(trade.price)}}
{% endif %}</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% endblock %}
{% block footer %}{% endblock %}
```
這樣基本上就完成了這個虛擬股票交易網站,雖然頁面有點醜就是了。