---
# System prepended metadata

title: Python + Flask 虛擬美國股票交易網站 Part8 （Watchlist/ 顯示持股清單/ 查詢交易紀錄）
tags: [Flask, CS50, Python]

---

<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>
                    &nbsp;&nbsp;&nbsp;&nbsp;
                    <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 %}

```

這樣基本上就完成了這個虛擬股票交易網站，雖然頁面有點醜就是了。