今天在 "股票發大財-用 Python 預測完轉股市高手精解" 這本書的第 11 章讀到如何在 Django 專案網頁中用 Matplotlib 繪圖, 我覺得這在建構金融資訊系統時很有用, 晚上得空便來實作看看. 關於 Django 用法參考 :
本篇測試我們只是要在 Django 網頁中顯示 Python 繪圖結果而已, 不會用到資料庫, 也不需要建立 App, 因此可採用最簡化網站配置方式, 將網頁應用程式直接建在專案目錄下, 只要在上層專案目錄 project1 下建立一個專案模板目錄 templates, 以及在下層專案目錄 project1 下建立視圖程式 views.py 即可 :
1. Django 專案的最簡配置用法 :
使用 django-admin startproject <project_name> 指令建立專案 project1 :
D:\python\test\django_projects>django-admin startproject project1
這會在目前工作目錄下建立兩層的 project1 資料夾, 專案主要內容放在第二層專案目錄下 :
D:\python\test\django_projects>tree project1 /f
列出磁碟區 新增磁碟區 的資料夾 PATH
磁碟區序號為 1258-16B8
D:\PYTHON\TEST\DJANGO_PROJECTS\PROJECT1
│ manage.py
│
└─project1
asgi.py
settings.py
urls.py
wsgi.py
__init__.py
接下來在上層專案目錄 project1 底下建立一個 templates 專案模板資料夾 :
D:\python\test\django_projects>mkdir templates
再次用 tree 顯示樹狀結構, 可見在上層專案目錄下有一個 templates 目錄了 :
D:\python\test\django_projects>tree project1 /f
列出磁碟區 新增磁碟區 的資料夾 PATH
磁碟區序號為 1258-16B8
D:\PYTHON\TEST\DJANGO_PROJECTS\PROJECT1
│ manage.py
│
├─project1
│ asgi.py
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│
└─templates
然後切換到下層專案目錄 project1 下, 用程式編輯器新增一個 Python 視圖程式 views.py, 再次用 tree 顯示檔案結構 :
D:\python\test\django_projects>tree project1 /f
列出磁碟區 新增磁碟區 的資料夾 PATH
磁碟區序號為 1258-16B8
D:\PYTHON\TEST\DJANGO_PROJECTS\PROJECT1
│ manage.py
│
├─project1
│ asgi.py
│ settings.py
│ urls.py
│ views.py
│ wsgi.py
│ __init__.py
│
└─templates
接著用記事本開啟下層 project1 目錄下的專案設定檔 settings.py, 搜尋串列變數 "TEMPLATES", 位置大約是在第 54 列左右, 預設內容如下 :
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [], # 設定專案模板目錄
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
其中的 DIRS 屬性就是用來設定專案模板目錄位置的, 預設為空串列, 請在串列中輸入BASE_DIR/'templates', 然後將 settings.py 存檔 :
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR/'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
其中 BASE_DIR 為上層專案目錄之絕對路徑, 定義於 DIRS 的前面.
這樣我們的簡配 Drango 網站結構便準備好了, 只要撰寫 templates 目錄下的網頁模板, 然後編輯 視圖程式 views.py 將變數傳遞給模板網頁顯示, 最後於 urls.py 中設定 URL 請求與視圖程式中函式之對應即可.
例如要在網頁中顯示 'Hello World" 與現在時間, 先編輯一個如下之模板網頁 helloworld.htm 並儲存於上層專案目錄的 templates 資料夾下 :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Hello World</title>
</head>
<body>
<h2>Hello World! 現在的時間是 {{now}}</h2>
</body>
</html>
其中 {{now}} 就是視圖程式 views.py 傳遞過來要顯示的變數, 然後切換到下層專案目錄, 編輯視圖程式 views.py :
from django.shortcuts import render
from datetime import datetime
def helloworld(request):
now=datetime.now()
return render(request, 'helloworld.htm', locals())
此處內建函式 locals() 會把 helloworld() 內的區域變數與其值打包成字典傳給 render() 去給要渲染的網頁 helloworld.htm.
最後是編輯路由程式 urls.py 設定 URL 與視圖程式內函式的對應 :
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('helloworld/', views.helloworld), # 指定 URL 之對應處理函式
]
此處要從目前目錄匯入 views.py 模組, 然後於 urlpatterns 串列中用 path() 函式為 URL='helloworld/' 路由指定由 views.py 裡的 helloworld() 函式處理.
至此便完成全部專案了, 在上層專案目錄下啟動開發伺服器 :
python manage.py runserver
D:\python\test\django_projects\project1>python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
August 12, 2024 - 16:12:34
Django version 4.2.4, using settings 'project1.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
這時在瀏覽器網址列輸入 http://127.0.0.1:8000/helloworld 即可看到網頁內容 :
以上便是 Django 專案網站的最簡單用法.
2. 在 Django 專案中用 Matplotlib 繪圖 :
在上面的簡配 Django 專案網站基礎上, 我們想增加一個 URL 來讓網站顯示 Matplotlib 繪製的圖形, 例如畫一個資產配置圓餅圖可參考之前的 Matplotlib 測試 :
程式如下 :
import numpy as np
import matplotlib.pyplot as plt
data=[600, 200, 100, 50, 50] #資產配置 (百萬元)
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate'] #資產標籤
plt.pie(data, labels=labels, autopct='%.2f%%') #繪製圓餅圖
plt.title('Asset Allocation') #設定圖形標題
plt.show()
這會開啟一個繪圖視窗顯示圓餅圖 :
這要如何顯示在 Django 網站上呢? 方法是在視圖程式中先將 Matplotlib 所繪製的圖形存檔到 ByteIO 物件串流中, 然後用 base64 模組的 b64encide() 函式讀取串流後解碼為 png 圖檔, 再將其傳遞給網頁模板渲染.
首先編輯一個網頁模板 show_piechart.htm 儲存到 templates 資料夾下 :
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Matplotlib Pie Chart</title>
</head>
<body>
<h3>顯示 Matplotlib 繪製之圓餅圖</h3>
<img src="{{img}}">
</body>
</html>
然後編輯視圖程式 views.py, 添加一個處理函式 show_piechart(), 並於其中撰寫 Matplotlib 程式碼繪製圓餅圖, 但不同之處是先將圖形物件存入 IO 串流緩衝器, 再讀出來轉成 PNG 檔, 完整程式碼如下 :
# views.py
from django.shortcuts import render
from datetime import datetime
import numpy as np
import matplotlib.pyplot as plt
import imp
import sys
from io import BytesIO
import base64
def helloworld(request):
now=datetime.now()
return render(request, 'helloworld.htm', locals())
def show_piechart(request):
imp.reload(sys) # 避免匯入 Matplotlib 時出現編碼錯誤用
data=[600, 200, 100, 50, 50]
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate']
plt.pie(data, labels=labels, autopct='%.2f%%')
plt.title('Asset Allocation')
buffer=BytesIO() # 建立 ByteIO 串流物件
plt.savefig(buffer) # 將圖形物件存入 ByteIO 串流快取內
plt.close() # 避免更新頁面出現異常必須關閉 PyPlot 物件
base64img=base64.b64encode(buffer.getvalue()) # 讀取 IO 快取編碼為 Base64 圖像
img=f'data:image/png;base64,{base64img.decode()}' # 指定網頁媒體為圖片資料
return render(request, 'show_piechart.htm', {'img': img})
注意, show_piechart() 函式一開始必須呼叫 img.reload(sys), 這是用來解決匯入 Matplotlib 時出現編碼錯誤之用. 其次是當圖形物件存入 BytesIO 串流快取後就須將圖形物件關閉, 否則重新整理此網頁時會出現錯誤.
最後編輯路由程式 urls.py, 添加 URL='show_piechart/' 與 views.show_piechart 之對應 :
# urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('helloworld/', views.helloworld),
path('show_piechart/', views.show_piechart),
]
這樣便完成全部設定了, 於瀏覽器網址列輸入 http://127.0.0.1:8000/show_piechart/ :
接下來測試利用上面的方法在 Django 網站中繪製 K 線圖.
3. 在 Django 專案中用 mplfinance 畫 K 線圖 :
繪製 K 線圖可以使用 mplfinance 套件, 用法參考 :
獲取 K 線圖資料源最方便的方法是利用 yfinance 套件, 用法參考 :
例如繪製近 30 天的 0050 K 線圖, 先匯入 yfinace 與 mplfinance 套件 :
>>> import yfinance as yf
>>> import mplfinance as mpf
呼叫 yfinance 的 download() 函式下載 0050 收盤資料, 預設為近 30 天資料 :
>>> data=yf.download('0050.tw', period='1mo')
[*********************100%%**********************] 1 of 1 completed
>>> data
Open High ... Adj Close Volume
Date ...
2024-07-12 198.199997 198.350006 ... 195.351791 14158267
2024-07-15 197.649994 198.100006 ... 195.699997 9790185
2024-07-16 196.550003 199.000000 ... 196.850006 13255538
2024-07-17 196.399994 196.500000 ... 194.100006 14489778
2024-07-18 188.600006 190.899994 ... 190.600006 17880013
2024-07-19 188.750000 189.000000 ... 186.250000 16453910
2024-07-22 185.399994 185.399994 ... 180.699997 21211846
2024-07-23 183.699997 186.300003 ... 186.300003 12527079
2024-07-24 186.300003 186.300003 ... 186.300003 0
2024-07-25 186.300003 186.300003 ... 186.300003 0
2024-07-26 178.050003 179.449997 ... 179.000000 25906618
2024-07-29 181.800003 182.000000 ... 180.600006 9946264
2024-07-30 179.250000 181.300003 ... 180.600006 13107381
2024-07-31 178.850006 181.149994 ... 180.850006 9078554
2024-08-01 185.000000 185.100006 ... 184.000000 14061699
2024-08-02 178.149994 178.899994 ... 174.699997 29706815
2024-08-05 166.649994 166.649994 ... 158.750000 55577204
2024-08-06 167.300003 170.399994 ... 167.500000 63140961
2024-08-07 169.850006 174.750000 ... 174.149994 31824740
2024-08-08 171.050003 172.399994 ... 170.550003 25841105
2024-08-09 175.000000 176.899994 ... 175.850006 23353756
2024-08-12 177.250000 179.800003 ... 178.050003 17571739
[22 rows x 6 columns]
然後呼叫 mplfinace 的 plot() 函式指定 type='candle' 即可繪製 K 線圖 :
>>> mpf.plot(data, type='candle')
由於 mplfinance 是在 Matplotlib 套件基礎上打造的, 因此可以用上面繪製 Matplotlib 圖形的方法在 Django 上畫 K 線圖.
先編輯網頁模板 show_0050_candle.htm :
<!doctype html>
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="cache-control" content="no-cache">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>0050 Candle Chart</title>
</head>
<body>
<h3>顯示 mplfinance 繪製之 0050 K 線圖</h3>
<img src="{{img}}">
</body>
</html>
在視圖程式需增加匯入 yfinance 與 mplfinance :
import yfinance as yf
import mplfinance as mpf
然後添加 show_0050_candle(), 完整程式碼如下 :
# views.py
from django.shortcuts import render
from datetime import datetime
import numpy as np
import matplotlib.pyplot as plt
import imp
import sys
from io import BytesIO
import base64
import yfinance as yf
import mplfinance as mpf
def helloworld(request):
now=datetime.now()
return render(request, 'helloworld.htm', locals())
def show_piechart(request):
imp.reload(sys)
data=[600, 200, 100, 50, 50]
labels=['Stock', 'Bond', 'Cash', 'Gold', 'Real estate']
plt.pie(data, labels=labels, autopct='%.2f%%')
plt.title('Asset Allocation')
buffer=BytesIO()
plt.savefig(buffer)
plt.close()
base64img=base64.b64encode(buffer.getvalue())
img=f'data:image/png;base64,{base64img.decode()}'
return render(request, 'show_piechart.htm', {'img': img})
def show_0050_candle(request):
imp.reload(sys)
data=yf.download('0050.tw', period='1mo')
fig, axes=mpf.plot(data, type='candle', returnfig=True, figsize=(6, 4))
buffer=BytesIO()
fig.savefig(buffer)
base64img=base64.b64encode(buffer.getvalue())
img=f'data:image/png;base64,{base64img.decode()}'
return render(request, 'show_0050_candle.htm', {'img': img})
最後修改 urls.py, 增加 URL='show_0050_candle/' 與 views.show_0050_candle 的對應 :
# urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('helloworld/', views.helloworld),
path('show_piechart/', views.show_piechart),
path('show_0050_candle/', views.show_0050_candle),
]
於瀏覽器網址列輸入 http://127.0.0.1:8000/show_0050_candle/ :
參考 :
沒有留言:
張貼留言