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:])
return weight, rets
# Define the route for the index pages
@app.route('/')
def index():
@ -127,8 +126,6 @@ def login_post():
username = request.form.get('username')
password = request.form.get('password')
print(username, password)
## Connect to the database
conn = psycopg2.connect(**SQL_CONFIG)
with conn:
@ -253,19 +250,16 @@ def submit_stock_list():
## Query DB
conn = psycopg2.connect(**SQL_CONFIG)
port = get_stock(conn, stock_list, session['tw'])
if len(port.index) > 908:
port = port.iloc[-908:, :]
if len(port.index) > 1008:
port = port.iloc[-1008:, :]
conn.close()
port = port.iloc[::3, :]
port = port/port.iloc[0, :]
fig = port.plot(title='資產價格變化', labels=dict(index="Date", value="Price", variable="Assets"))
fig['layout'] = {}
# 序列化
graphJSON = json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)
for key in request.form:
print(key, request.form[key])
# Do something with the stock list heres
@ -284,14 +278,11 @@ def buildPort():
if time.time() - session['lastCreateTime'] < 60:
print("UNTIL: ", time.time()-session['lastCreateTime'])
return '''<span>投資組合建立時間間隔(或與登入時間間隔)必須大於60秒!</span>'''
# print('last_creation', time.time() - session['lastCreateTime'])
session['lastCreateTime'] = time.time()
# print('last_creation', session['lastCreateTime'])
# print("-"*10)
for key in request.form:
print(key, request.form[key], type(request.form[key]))
# Portfolio Info
# Portfolio Info , random name generator
name = request.form.get('name')
if name == '':
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['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 \
(date, name, username, 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;'
(date, name, username,\
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.cursor() as curs:
curs.execute(sql, data)
strategy_id = curs.fetchone()[0]
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>'''

@ -5,90 +5,77 @@
{% block title %}Strategy Page{% endblock%}
{% block content %}
<div class="container-fluid" style="min-height:92%;position:relative;">
<!-- Button trigger modal -->
<button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#uploadModal">
上傳檔案
</button>
<!-- Modal -->
<div class="modal fade" id="uploadModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable">
<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">
<div class="card my-3">
<div class="card-header">
<h1 class="modal-title font-bold text-xl" style="color: #000055;">格式規範與上傳檔案</h1>
</div>
<div class="card-body">
<div class="row justify-content-center">
<div class="col-lg-5 col-md-5 col-sm-10">
<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>價格資訊需長度相同,且資產數量大於1檔才會進行回測。</li>
<li><span class="fa-li"><i class="fa-solid fa-flag"></i></span>範例如下圖所示。</li>
</ul>
<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">
<div class="card my-3">
<div class="p-2 font-bold text-lg">
投組最佳化配置
</div>
<div class="input-group">
<span class="input-group-text bg-info">輸入數據時長</span>
<select name="lookback" class="form-select">
<option value="21">1個月</option>
<option value="63">3個月</option>
<option selected value="126">6個月</option>
<option value="252">12個月</option>
</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 class="p-2 font-bold text-lg">
投組最佳化配置
</div>
<div class="input-group">
<span class="input-group-text bg-info">輸入數據時長</span>
<select name="lookback" class="form-select">
<option value="21">1個月</option>
<option value="63">3個月</option>
<option selected value="126">6個月</option>
<option value="252">12個月</option>
</select>
</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>
<button id="uploadCheck" type="submit" class="btn btn-outline-primary ms-auto">確認上傳</button>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">關閉</button>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script>
$(document).ready(function(){
$("#uploadModal").modal('show');
});
function changeFunc(value) {
console.log(value);
if (value === 'quadratic_utility') {
$('#gamma').css("display", "flex");
} else {
$('#gamma').css("display", "none");
console.log(value);
if (value === 'quadratic_utility') {
$('#gamma').css("display", "flex");
} else {
$('#gamma').css("display", "none");
}
}
}
</script>
{% endblock script %}

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

Loading…
Cancel
Save