SeanChenTaipei 2 years ago
parent 6977b88ac2
commit 0481aba993
  1. 24
      main.py
  2. 113
      templates/custom.html
  3. 92
      templates/strategy_tw.html

@ -109,7 +109,6 @@ def rolling_optimize(ret, lookback=126, backtest=126, role="max_sharpe", gamma=N
rets = pd.DataFrame(np.vstack([rets, equally_weighted]).T, columns=['Portfolio', 'Equally'], index=ret.index[lookback:]) rets = pd.DataFrame(np.vstack([rets, equally_weighted]).T, columns=['Portfolio', 'Equally'], index=ret.index[lookback:])
return weight, rets return weight, rets
# Define the route for the index pages # Define the route for the index pages
@app.route('/') @app.route('/')
def index(): def index():
@ -127,8 +126,6 @@ def login_post():
username = request.form.get('username') username = request.form.get('username')
password = request.form.get('password') password = request.form.get('password')
print(username, password) print(username, password)
## Connect to the database ## Connect to the database
conn = psycopg2.connect(**SQL_CONFIG) conn = psycopg2.connect(**SQL_CONFIG)
with conn: with conn:
@ -253,19 +250,16 @@ def submit_stock_list():
## Query DB ## Query DB
conn = psycopg2.connect(**SQL_CONFIG) conn = psycopg2.connect(**SQL_CONFIG)
port = get_stock(conn, stock_list, session['tw']) port = get_stock(conn, stock_list, session['tw'])
if len(port.index) > 908: if len(port.index) > 1008:
port = port.iloc[-908:, :] port = port.iloc[-1008:, :]
conn.close() conn.close()
port = port.iloc[::3, :] port = port.iloc[::3, :]
port = port/port.iloc[0, :] port = port/port.iloc[0, :]
fig = port.plot(title='資產價格變化', labels=dict(index="Date", value="Price", variable="Assets")) fig = port.plot(title='資產價格變化', labels=dict(index="Date", value="Price", variable="Assets"))
fig['layout'] = {} fig['layout'] = {}
# 序列化 # 序列化
graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder) graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
for key in request.form: for key in request.form:
print(key, request.form[key]) print(key, request.form[key])
# Do something with the stock list heres # Do something with the stock list heres
@ -284,14 +278,11 @@ def buildPort():
if time.time() - session['lastCreateTime'] < 60: if time.time() - session['lastCreateTime'] < 60:
print("UNTIL: ", time.time()-session['lastCreateTime']) print("UNTIL: ", time.time()-session['lastCreateTime'])
return '''<span>投資組合建立時間間隔(或與登入時間間隔)必須大於60秒!</span>''' return '''<span>投資組合建立時間間隔(或與登入時間間隔)必須大於60秒!</span>'''
# print('last_creation', time.time() - session['lastCreateTime'])
session['lastCreateTime'] = time.time() session['lastCreateTime'] = time.time()
# print('last_creation', session['lastCreateTime'])
# print("-"*10)
for key in request.form: for key in request.form:
print(key, request.form[key], type(request.form[key])) print(key, request.form[key], type(request.form[key]))
# Portfolio Info # Portfolio Info , random name generator
name = request.form.get('name') name = request.form.get('name')
if name == '': if name == '':
prefix=''.join(random.choices(string.ascii_uppercase + string.digits, k=6)) prefix=''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
@ -348,14 +339,17 @@ def buildPort():
info['vol'], info['mdd'], info['annual_sr'], info['vol'], info['mdd'], info['annual_sr'],
info['beta'], info['alpha'], info['var10'], info['R2'], True, comment, stock_list, json.dumps(weight.to_dict()), json.dumps(rets.to_dict())) info['beta'], info['alpha'], info['var10'], info['R2'], True, comment, stock_list, json.dumps(weight.to_dict()), json.dumps(rets.to_dict()))
sql='insert into strategy \ sql='insert into strategy \
(date, name, username, competition, role, annual_ret, vol, mdd, annual_sr, beta, alpha, var10, R2, tw, notes, assets, weight, ret)\ (date, name, username,\
values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) RETURNING id;' competition, role, annual_ret,\
vol, mdd, annual_sr, beta, alpha,\
var10, R2, tw, notes, assets, weight, ret)\
values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) RETURNING id;'
with conn: with conn:
with conn.cursor() as curs: with conn.cursor() as curs:
curs.execute(sql, data) curs.execute(sql, data)
strategy_id = curs.fetchone()[0] strategy_id = curs.fetchone()[0]
conn.close() conn.close()
print("\n------Write in Success--------\n") print("\n------Strategy write in Success--------\n")
return f'''<span>投資組合已完成建立,請點擊 <a class="badge rounded-pill text-bg-warning" href="/result_view?strategy_id={strategy_id}">{strategy_id}</a>查詢分析結果。</span>''' return f'''<span>投資組合已完成建立,請點擊 <a class="badge rounded-pill text-bg-warning" href="/result_view?strategy_id={strategy_id}">{strategy_id}</a>查詢分析結果。</span>'''

@ -5,90 +5,77 @@
{% block title %}Strategy Page{% endblock%} {% block title %}Strategy Page{% endblock%}
{% block content %} {% block content %}
<div class="container-fluid" style="min-height:92%;position:relative;"> <div class="container-fluid" style="min-height:92%;position:relative;">
<!-- Button trigger modal --> <div class="card my-3">
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#uploadModal"> <div class="card-header">
上傳檔案 <h1 class="modal-title font-bold text-xl" style="color: #000055;">格式規範與上傳檔案</h1>
</button> </div>
<!-- Modal --> <div class="card-body">
<div class="modal fade" id="uploadModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-hidden="true"> <div class="row justify-content-center">
<div class="modal-dialog modal-dialog-scrollable"> <div class="col-lg-5 col-md-5 col-sm-10">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title font-bold" style="color: #000055;">格式規範與上傳檔案</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<ul class="fa-ul"> <ul class="fa-ul">
<li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>上傳之csv檔需包含header,且第一行為時間資訊。</li> <li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>上傳之csv檔需包含header,且第一行為時間資訊。</li>
<li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>價格資訊需長度相同,且資產數量大於1檔才會進行回測。</li> <li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>價格資訊需長度相同,且資產數量大於1檔才會進行回測。</li>
<li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>範例如下圖所示。</li> <li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>範例如下圖所示。</li>
</ul> </ul>
<img src="{{ url_for('static', filename='img/file.jpg') }}" class="img-fluid mb-3" alt="SINGUP IMAGE"> <img src="{{ url_for('static', filename='img/file.jpg') }}" class="img-fluid mb-3" alt="SINGUP IMAGE">
</div>
<div class="col-lg-7 col-md-7 col-sm-10">
<form method="POST" enctype="multipart/form-data"> <form method="POST" enctype="multipart/form-data">
<div class="card my-3"> <div class="p-2 font-bold text-lg">
<div class="p-2 font-bold text-lg"> 投組最佳化配置
投組最佳化配置 </div>
</div> <div class="input-group">
<div class="input-group"> <span class="input-group-text bg-info">輸入數據時長</span>
<span class="input-group-text bg-info">輸入數據時長</span> <select name="lookback" class="form-select">
<select name="lookback" class="form-select"> <option value="21">1個月</option>
<option value="21">1個月</option> <option value="63">3個月</option>
<option value="63">3個月</option> <option selected value="126">6個月</option>
<option selected value="126">6個月</option> <option value="252">12個月</option>
<option value="252">12個月</option> </select>
</select>
</div>
<div class="input-group">
<span class="input-group-text bg-info">再平衡頻率</span>
<select name="frequency" class="form-select">
<option value="21">每月</option>
<option value="63">每季</option>
<option selected value="126">每半年</option>
<option value="252">每年</option>
</select>
</div>
<div class="input-group">
<span class="input-group-text bg-info">最佳化目標函數</span>
<select name="role" class="form-select" onchange="changeFunc(value);">
<option selected value="max_sharpe">最大化夏普比率</option>
<option value="max_sortino">最大化索提諾比率</option>
<option value="min_volatility">最小化波動率</option>
<option value="quadratic_utility">最大化效用函數</option>
</select>
</div>
<div class="input-group" style="display: none;" id="gamma">
<span class="input-group-text bg-info">風險厭惡係數</span>
<input type="number" id="gamma" name="gamma" name="targetAnnualVolatility" class="form-control fmt-pct" value="30" autocomplete="off">
<span class="input-group-text">%</span>
</div>
</div> </div>
<div class="form-group d-flex"> <div class="input-group">
<span class="input-group-text bg-info">再平衡頻率</span>
<select name="frequency" class="form-select">
<option value="21">每月</option>
<option value="63">每季</option>
<option selected value="126">每半年</option>
<option value="252">每年</option>
</select>
</div>
<div class="input-group">
<span class="input-group-text bg-info">最佳化目標函數</span>
<select name="role" class="form-select" onchange="changeFunc(value);">
<option selected value="max_sharpe">最大化夏普比率</option>
<option value="max_sortino">最大化索提諾比率</option>
<option value="min_volatility">最小化波動率</option>
<option value="quadratic_utility">最大化效用函數</option>
</select>
</div>
<div class="input-group" style="display: none;" id="gamma">
<span class="input-group-text bg-info">風險厭惡係數</span>
<input type="number" id="gamma" name="gamma" name="targetAnnualVolatility" class="form-control fmt-pct" value="30" autocomplete="off">
<span class="input-group-text">%</span>
</div>
<div class="form-group d-flex mt-3">
<input type="file" class="form-control-file" id="csv_file" name="csv_file" accept=".csv" max-file="3" required> <input type="file" class="form-control-file" id="csv_file" name="csv_file" accept=".csv" max-file="3" required>
<button id="uploadCheck" type="submit" class="btn btn-outline-primary ms-auto">確認上傳</button> <button id="uploadCheck" type="submit" class="btn btn-outline-primary ms-auto">確認上傳</button>
</div> </div>
</form> </form>
</div> </div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">關閉</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> <script>
$(document).ready(function(){
$("#uploadModal").modal('show');
});
function changeFunc(value) { function changeFunc(value) {
console.log(value); console.log(value);
if (value === 'quadratic_utility') { if (value === 'quadratic_utility') {
$('#gamma').css("display", "flex"); $('#gamma').css("display", "flex");
} else { } else {
$('#gamma').css("display", "none"); $('#gamma').css("display", "none");
}
} }
}
</script> </script>
{% endblock script %} {% endblock script %}

@ -19,10 +19,9 @@ div.card{
color: #000093; color: #000093;
} }
.scroll { .scroll {
max-height: 471px; max-height: 390px;
overflow-y: auto; overflow-y: auto;
} }
@keyframes cursor { @keyframes cursor {
0% { 0% {
opacity: 0; opacity: 0;
@ -43,7 +42,7 @@ div.card{
{% endblock style %} {% endblock style %}
{% block content %} {% block content %}
<div class="container-fluid" style="background-color: #ffffff;min-height:92%;position:relative;"> <div class="container-fluid" style="background-color: #ffffff;">
<div class="container-fluid py-2"> <div class="container-fluid py-2">
<div class="alert alert-dark m-0" role="alert"> <div class="alert alert-dark m-0" role="alert">
<ul class="fa-ul"> <ul class="fa-ul">
@ -52,7 +51,7 @@ div.card{
<li><span class="fa-li"><i class="fa-solid fa-scroll"></i></span>未輸入投資組合名稱則會由系統隨機生成。</li> <li><span class="fa-li"><i class="fa-solid fa-scroll"></i></span>未輸入投資組合名稱則會由系統隨機生成。</li>
</ul> </ul>
</div> </div>
<div class="row mb-3"> <div class="row">
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="card my-2 font-bold" style="border-radius: 7px;"> <div class="card my-2 font-bold" style="border-radius: 7px;">
<div class="card-header"> <div class="card-header">
@ -63,13 +62,9 @@ div.card{
<ol class="list-group list-group-flush list-group-numbered"> <ol class="list-group list-group-flush list-group-numbered">
<li class="list-group-item"> <li class="list-group-item">
<span class="ps-2">輸入投資組合名稱。</span> <span class="ps-2">輸入投資組合名稱。</span>
<input if="imput1" name="portName" type="text" class="form-control" placeholder="EX. 韓總 No.1" required>
</li> </li>
<li class="list-group-item"> <li class="list-group-item">
<span class="ps-2">選擇所參加的課程或競賽。</span> <span class="ps-2">選擇所參加的課程或競賽。</span>
<select id="competition" class="form-select" size="1" name="competition">
{% include 'competitions.html' %}
</select>
</li> </li>
<li class="list-group-item"><span class="ps-2">選擇資產後按下 <span class="badge bg-secondary">加入</span></li> <li class="list-group-item"><span class="ps-2">選擇資產後按下 <span class="badge bg-secondary">加入</span></li>
<li class="list-group-item"><span class="ps-2">按下 <button type="button" class="btn btn-outline-primary btn-sm" disabled>確認資產</button> 後查看資產價格動態圖表。</span></li> <li class="list-group-item"><span class="ps-2">按下 <button type="button" class="btn btn-outline-primary btn-sm" disabled>確認資產</button> 後查看資產價格動態圖表。</span></li>
@ -83,38 +78,48 @@ div.card{
<div class="col-lg-6 col-md-6 col-sm-12"> <div class="col-lg-6 col-md-6 col-sm-12">
<div class="card my-2 scroll" style="border-radius: 7px;"> <div class="card my-2 scroll" style="border-radius: 7px;">
<div class="card-header d-flex"> <div class="card-header d-flex">
<div class="py-2 font-bold text-lg"> <div class="py-2 font-bold text-xl">
已選擇的資產 名稱與選擇資產
</div> </div>
</div> </div>
<div class="card-body d-flex mb-0"> <div class="card-body">
<input name="assetSelect" class="form-control" list="datalistOptions" id="stockAll" placeholder="輸入資產名稱..."> <label for="portName" class="form-label font-bold">輸入投資組合名稱: </label>
<datalist id="datalistOptions"> <input id="portName" name="portName" type="text" class="form-control mb-3" placeholder="EX. 韓總 No.1" required>
{% if session.tw==0 %} <label for="competition" class="form-label font-bold">選擇所屬課程或競賽: </label>
{% for key, data in data_us.items() -%} <select id="competition" class="form-select mb-3" size="1">
<option value="{{ key|e }}">{{ key|e }} | {{ data|e }}</option> {% include 'competitions.html' %}
</select>
<label for="stockAll" class="form-label font-bold">選擇資產: </label>
<div class="d-flex">
<input name="assetSelect" class="form-control" list="datalistOptions" id="stockAll" placeholder="輸入資產名稱...">
<datalist id="datalistOptions">
{% if session.tw==0 %}
{% for key, data in data_us.items() -%}
<option value="{{ key|e }}">{{ key|e }} | {{ data|e }}</option>
{% endfor %}
{% endif %}
{% for key, data in data_tw.items() -%}
<option value="{{ key|e }}">{{ key|e }} | {{ data|e }} </option>
{% endfor %} {% endfor %}
{% endif %} </datalist>
{% for key, data in data_tw.items() -%} <button class="btn btn-secondary btn-sm"
<option value="{{ key|e }}">{{ key|e }} | {{ data|e }} </option> type="button"
{% endfor %} id="addStockBtn">
</datalist> 加入
<button class="btn btn-secondary btn-sm" </button>
type="button" </div>
id="addStockBtn"> <div>
加入 <ol class="list-group list-group-numbered py-3" id="stock-list" type="1">
</button> <li class="list-group-item">
</div> <span class="px-2">2330.TW</span>
<div class='mb-auto'> <a class="btn btn-sm btn-danger float-right delete-btn">
<ol class="list-group list-group-numbered px-3 pb-3" id="stock-list" type="1"> <i class="fas fa-trash-alt"></i>
<li class="list-group-item"> </a>
<span class="px-2">2330.TW</span> </li>
<a class="btn btn-sm btn-danger float-right delete-btn"> </ol>
<i class="fas fa-trash-alt"></i> </div>
</a>
</li>
</ol>
</div> </div>
</div> </div>
</div> </div>
@ -132,18 +137,11 @@ div.card{
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div id="graph" style="max-height:50vh">
<div class="col-lg-12 col-md-12 col-sm-12"> <span>
<div id="graph" style="max-height:50vh"> 圖表將在此渲染。
<span> </span>
圖表將在此渲染。
</span>
</div>
</div>
<div class="col-lg-3 col-md-3 col-sm-8">
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="card mt-3"> <div class="card mt-3">

Loading…
Cancel
Save