-
-
Save tej87681088/a454b23fa33244d9cdad450ecd7c12cd to your computer and use it in GitHub Desktop.
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
# streamlit run app.py | |
# http://localhost:8501/ | |
# 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 | |
st.set_page_config(page_title='量化投資', page_icon=':bar_chart:', layout='wide') | |
with st.sidebar: | |
st.title('TEJAPI股票學習') | |
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)) | |
#輸入股價 | |
# 使用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) | |
upper_bound = st.number_input('上界值:',value=150) | |
lower_bound = st.number_input('下界值:',value=100) | |
interval = st.number_input('網格區間:',value=10) | |
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 | |
st.title('🌐STREAMLIT股票資料網格交易應用') | |
st.write("") | |
tab1, tab2, tab3 = st.tabs(["交易訊號", "資料集", "投資績效表"]) | |
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) | |
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", | |
) | |
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment