當 TEJ API 資料庫遇上STREAMLIT網格交易 App

【新手入門】當 TEJ API 資料庫遇上STREAMLIT網格交易 App
Photo by Austin Distel on Unsplash

本文重點概要:

  • 文章難度:★★★☆☆
  • STREAMLIT APP 視覺化應用

前言

在之前的教學中,我們已經學會如何創建自己的 streamlit app了,詳細資料可以參考這篇文章,接下來這篇文章將用利用 TEJAPI 資料庫串聯 STREAMLIT 套件將網格交易的策略撰寫出來,並利用套件中的選擇日期、下拉式選單、數值選取工具來與圖表以及資料表做連動,使資料變成可以互動的 APP。

網格交易是一種交易策略,會藉由設定兩個參數上界與下界來選取出一個區間,再將我們的股價區分成一格一格的價格區間,當價格下跌並觸碰到下方的格子時就買進股票,當價格上漲並超過上面的格子時則賣出股票。

此策略是一種懶人策略,不太需要手動的去操作,除此之外還可以跟著價格的動盪賺錢,但是要注意幾個點使用此策略的話資金運用的效率會變得比手動交易來的低。

本次實作的交易APP畫面

本次實作的交易APP畫面

套件導入

本文使用 Windows 並以 VScode 作為編輯器

# STREAMLIT套件
import streamlit as st
import pandas as pd
# 可以互動的PLOT套件
import plotly.graph_objects as go
# 設置日期格式的套件
import datetime
from datetime import datetime as dt
from datetime import timedelta 
import tejapi

# 登入TEJ API
tejapi.ApiConfig.api_key = your_key
#把時間取消保留日期 (無視)
tejapi.ApiConfig.ignoretz = True

資料庫使用

. 交易資料-股價資料(TMN/APIPRCD)

Sidebar版面小工具設置

左側工具欄位
左側工具欄位

1.股票起始日期、股票結束日期工具設定

col1, col2 = st.columns(2)
with col1:
        # 將股票起使日期設置為變數d1
        d1 = st.date_input(
        "股票起始日期",
        # 並將預設資料設定為2022年的1/1
        datetime.date(2022, 1, 1))

    with col2:
        # 將股票起使日期設置為變數d2
        d2= st.date_input(
        "股票結束日期",
        datetime.date(2023, 2, 3))

2.股票代碼下拉式選單

由於當天的股票資料還沒收盤,抓取當天資料會出現錯誤,因此這邊改成抓取昨天的資料,並將資料的欄位opts這邊只留下coid(股價名稱資料)

#輸入股價
# 使用date套件的date獲取今天的日期資料
current_date = dt.now().date()
    # 使用date套件的timedelta獲取昨天的日期資料
    previous_date = current_date - timedelta(days=1)
    data = tejapi.get('TWN/APIPRCD',
                  mdate=previous_date,
                  opts={'columns':['coid']},
                  paginate=True)
    
    coids = data['coid'].tolist()
    stock_code = st.selectbox('選擇股票代碼', data)
    st.write('你選擇股票是: ', stock_code)

3.上界值、下界值、網格區間設定工具

這邊將預設的值設定為上界值為150、下界值為100、網格區間設定為

upper_bound = st.number_input('上界值:',value=150)
lower_bound = st.number_input('下界值:',value=100)
interval = st.number_input('網格區間:',value=10)

右側圖表、資料集、報酬率的設置

在右側的面板中我們配置了三項工具來方便我們觀察數據的走勢,包括買進賣出的訊號圖表、資料集、報酬率

右側圖表、資料集、報酬率的設置

1.資料處理

剛剛在左側已經有了股票名稱、起始時間、結束時間,這邊將這些小工具參數填入我們的TEJAPI資料庫中,選取出我們需要的資料
再創建買進跟賣出的訊號的欄位,將下跌並碰觸到網格的資料填入買入訊號,將上漲超過網格的資料填入賣出訊號欄位

stock_id = {stock_code}
    gte, lte = {d1}, {d2}
    tejdata= tejapi.get('TWN/APIPRCD',
                        paginate = True,
                        coid = stock_id,
                        mdate = {'gte':gte, 'lte':lte},
                        chinese_column_name=True
                        )
    df = tejdata
    df.reset_index(drop=True, inplace=True)
    
    # 創建 Buy_Signal 和 Sell_Signal 列,預設為 False
    df['Buy_Signal'] = False
    df['Sell_Signal'] = False

    # 在適當的條件下,將 Buy_Signal 和 Sell_Signal 設置為 True
    grid = list(range(lower_bound, upper_bound + interval, interval))
    for i in range(1, len(df)):
        for price in grid:
            if df['收盤價'][i-1] > price >= df['收盤價'][i]:
                df.at[i, 'Buy_Signal'] = True
            if df['收盤價'][i-1] < price <= df['收盤價'][i]:
                df.at[i, 'Sell_Signal'] = True

2.交易訊號圖表


先設置我們的工具選項欄位

tab1, tab2, tab3 = st.tabs(["交易訊號", "資料集", "投資績效表"])

將收盤價繪製成股票走勢圖並寫進tab1當中,再將買賣訊號加入在圖表當中

with tab1:
    st.title(stock_code)
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=df['資料日'], y=df['收盤價'], name=stock_code))

    # 創建網格,將線條顏色設置為灰色
    for price in grid:
        fig.add_shape(type="line", x0=df['資料日'].iloc[0], x1=df['資料日'].iloc[-1], y0=price, y1=price, line=dict(color='gray'))

    # 加入買進訊號的散點圖
    buy_signals = df[df['Buy_Signal']]
    fig.add_trace(go.Scatter(x=buy_signals['資料日'], y=buy_signals['收盤價'], mode='markers', name='Buy Signal', marker=dict(color='green', size=10, symbol='triangle-up')))

    # 加入賣出訊號的散點圖
    sell_signals = df[df['Sell_Signal']]
    fig.add_trace(go.Scatter(x=sell_signals['資料日'], y=sell_signals['收盤價'], mode='markers', name='Sell Signal', marker=dict(color='red', size=10, symbol='triangle-down')))
    fig.update_layout()
    st.plotly_chart(fig)

3.資料集的建立

將前面讀取到的df資料輸入到tab2中,並加入資料下載按鈕

with tab2:
    st.dataframe(df, height=500)  
    @st.cache_data
    def convert_df(df):
        # IMPORTANT: Cache the conversion to prevent computation on every rerun
        return df.to_csv().encode("utf-8")
    csv = convert_df(df)

    st.download_button(
        label="點此下載資料範例",
        data=csv,
        file_name=f"{stock_code}股價資料.csv",
        mime="text/csv",)

4.交易資料表、投資績效表設定

這裡初始我們定義了幾個參數
● principal:本金
● position:股票部位持有張數
● cash:現金部位持有量
● order_unit:交易單位數

這邊將本金設定為500000,並將每次的買次交易張數設定為1000股,並將每次的交易時間、交易成本、交易單位、持倉狀況、現金價值變成新的資料集trade_book,並利用裡面的參數去計算投資的報表

trade_book的交易表欄位名稱:

● BuyTime: 買入時間點
● SellTime: 賣出時間點
● CashFlow: 現金流出入量
● TradeUnit: 買進賣出張數
● HoldingPosition: 持有部位
● CashValue: 剩餘現金量

with tab3:
    st.title('交易模擬')
    
    # 初始化本金和交易紀錄表
    principal = 500000
    cash = principal
    position = 0
    order_unit = 1000  # 每次交易的股數
    trade_book = pd.DataFrame(columns=['Coid', 'BuyOrSell', 'BuyTime', 'SellTime', 'CashFlow', 'TradeUnit', 'HoldingPosition', 'CashValue'])

    st.write("本金:", principal)

    # 進行網格交易並更新交易紀錄表
    for i in range(1, len(df)):
        cu_time = df['資料日'][i]
        cu_close = df['收盤價'][i]
        n_time = df['資料日'][i - 1]
        n_open = df['開盤價'][i]

        if position == 0 and df['Buy_Signal'][i]:
            # 判斷是否進行買進
            position += 1
            order_time = n_time
            order_price = n_open
            friction_cost = (20 if order_price * order_unit * 0.001425 < 20 else order_price * order_unit * 0.001425)
            total_cost = -1 * order_price * order_unit - friction_cost
            cash += total_cost
            trade_book = trade_book.append({'Coid': stock_id, 'BuyOrSell': 'Buy', 'BuyTime': order_time, 'SellTime': None, 'CashFlow': total_cost, 'TradeUnit': order_unit, 'HoldingPosition': position, 'CashValue': cash}, ignore_index=True)

        elif position > 0 and df['Sell_Signal'][i]:
            # 判斷是否進行賣出
            order_price = n_open
            cover_time = n_time
            friction_cost = (20 if order_price * order_unit * 0.001425 < 20 else order_price * order_unit * 0.001425) + order_price * order_unit * 0.003
            total_cost = order_price * order_unit - friction_cost
            cash += total_cost
            trade_book = trade_book.append({'Coid': stock_id, 'BuyOrSell': 'Sell', 'BuyTime': None, 'SellTime': cover_time, 'CashFlow': total_cost, 'TradeUnit': -1 * order_unit, 'HoldingPosition': position, 'CashValue': cash}, ignore_index=True)
            position = 0

    st.write("交易紀錄表:")
    st.dataframe(trade_book)

    # 計算交易相關數據
    overallreturn = ((cash - principal) / principal) * 100
    num_trade = len(trade_book)
    num_buy = len(trade_book[trade_book['BuyOrSell'] == 'Buy'])
    num_sell = len(trade_book[trade_book['BuyOrSell'] == 'Sell'])

    # 計算平均交易報酬
    avg_return_ = (trade_book['CashFlow'] / (trade_book['TradeUnit'] * trade_book['CashValue'])).mean() * 100

    # 計算平均持有期間
    trade_book['BuyTime'] = pd.to_datetime(trade_book['BuyTime'])  # 將BuyTime轉換為datetime格式
    trade_book['SellTime'] = pd.to_datetime(trade_book['SellTime'])  # 將SellTime轉換為datetime格式
    # 計算持有期間並排除掉NaN和負值
    trade_book['HoldPeriod'] = (trade_book['SellTime'] - trade_book['BuyTime']).dt.days
    trade_book['HoldPeriod'] = trade_book['HoldPeriod'].apply(lambda x: max(x, 0))  # 將持有期間中的負值改為0
    avg_hold_period_ = trade_book['HoldPeriod'].mean()

    # 計算勝率
    winning_rate = (len(trade_book[trade_book['CashFlow'] > 0]) / num_trade) * 100

    # 計算最大獲利交易報酬和最大損失交易報酬
    trade_book['ReturnPercentage'] = (trade_book['CashFlow'] / (trade_book['TradeUnit'] * trade_book['CashValue'])) * 100
    max_win = trade_book['ReturnPercentage'].max()
    max_loss = trade_book['ReturnPercentage'].min()

    # 計算最低現金持有量
    min_cash = trade_book['CashValue'].min()

    # 呈現交易相關數據
    st.write('總績效:', overallreturn, '%')
    st.write('交易次數:', num_trade, '次')
    st.write('買入次數:', num_buy, '次')
    st.write('賣出次數:', num_sell, '次')
    st.write('平均交易報酬:', avg_return_, '%')
    st.write('勝率:', winning_rate, '%')
    st.write('最大獲利交易報酬:', max_win, '%')
    st.write('最大損失交易報酬:', max_loss, '%')
    st.write('最低現金持有量:', min_cash)

開啟STREAMLIT

設定完成後,我們可以藉由終端機來啟動 STREAMLIT 專案,透過在終端機輸入 streamlit run app.py 即可執行。

streamlit run app.py

結語

本次實作使用 STREAMLIT 套件,讓 TEJAPI 的資料庫能夠以互動式 APP 的方式呈現數據之外,還可以加上網格交易的交易策略。讓讀者在設計APP時也可以去思考自己的交易策略還可以怎麼在APP上執行,並加入自己的程式中,讓整個分析出來的資料不只是一張報表。此外 ,TEJ API 提供廣泛的臺灣企業和金融市場相關資料,包括財務、股票、基金、宏觀經濟、產業等領域,並提供專業報告和研究,幫助公司研究、投資決策和市場預測。

溫馨提醒,本次介紹與標的僅供參考,不代表任何商品或投資上的建議。之後也會介紹使用 TEJ 資料庫來建構各式選擇權模型,所以歡迎對選擇權交易有興趣的讀者,選購 TEJ E-Shop 的相關方案,用高品質的資料庫,建構出適合自己的訂價模型。

完整程式碼

延伸閱讀

相關連結

返回總覽頁
Procesing