├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── __pycache__ ├── app.cpython-37.pyc └── pattern.cpython-37.pyc ├── main.py ├── pattern.py ├── requirements.txt ├── runtime.txt ├── stock-pattern-demo.ipynb ├── templates ├── error.html ├── index.html └── result.html └── uwsgi.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/jupyternotebooks 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=jupyternotebooks 3 | 4 | ### JupyterNotebooks ### 5 | # gitignore template for Jupyter Notebooks 6 | # website: http://jupyter.org/ 7 | 8 | .ipynb_checkpoints 9 | */.ipynb_checkpoints/* 10 | 11 | # IPython 12 | profile_default/ 13 | ipython_config.py 14 | 15 | # Remove previous ipynb_checkpoints 16 | # git rm -r .ipynb_checkpoints/ 17 | 18 | # End of https://www.toptal.com/developers/gitignore/api/jupyternotebooks -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: python main.py runserver 0.0.0.0:5000 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stock-pattern 2 | 주가 종목 패턴 발굴기 3 | -------------------------------------------------------------------------------- /__pycache__/app.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teddylee777/stock-pattern/e4d70bf6bbd481ac4bd062ce61500c76c62c1afa/__pycache__/app.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/pattern.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teddylee777/stock-pattern/e4d70bf6bbd481ac4bd062ce61500c76c62c1afa/__pycache__/pattern.cpython-37.pyc -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import re 2 | from flask import Flask, render_template, request, Response, url_for, redirect 3 | import pattern as pt 4 | import io 5 | import time 6 | import os 7 | from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas 8 | import matplotlib 9 | import matplotlib.pyplot as plt 10 | import matplotlib.gridspec as gridspec 11 | from mpl_finance import candlestick_ohlc 12 | import numpy as np 13 | import FinanceDataReader as fdr 14 | 15 | matplotlib.use('Agg') 16 | 17 | app = Flask(__name__) 18 | 19 | @app.route('/', methods=['GET', 'POST']) 20 | def index(): 21 | if request.method == 'GET': 22 | return render_template('index.html') 23 | else: 24 | code = request.form['code'] 25 | startdate = request.form['startdate'] 26 | enddate = request.form['enddate'] 27 | if request.form['action'] == '패턴검색': 28 | return redirect(url_for('pattern', startdate=startdate, enddate=enddate, code=code)) 29 | elif request.form['action'] == '차트확인': 30 | return render_template('index.html', startdate=startdate, enddate=enddate, code=code, chart=True) 31 | 32 | @app.route('/plot.png', methods=['GET']) 33 | def plot_png(): 34 | code = request.args.get('code', None) 35 | startdate = request.args.get('startdate', None) 36 | enddate = request.args.get('enddate', None) 37 | print(startdate) 38 | p = pt.PatternFinder() 39 | p.set_stock(code) 40 | result = p.search(startdate, enddate) 41 | print(result) 42 | if len(result) > 0: 43 | fig = p.plot_pattern(list(result.keys())[0]) 44 | output = io.BytesIO() 45 | FigureCanvas(fig).print_png(output) 46 | return Response(output.getvalue(), mimetype='image/png') 47 | 48 | @app.route('/plotchart.png', methods=['GET']) 49 | def plot_chart(): 50 | code = request.args.get('code', None) 51 | startdate = request.args.get('startdate', None) 52 | enddate = request.args.get('enddate', None) 53 | 54 | fig = plt.figure() 55 | fig.set_facecolor('w') 56 | gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1]) 57 | axes = [] 58 | axes.append(plt.subplot(gs[0])) 59 | axes.append(plt.subplot(gs[1], sharex=axes[0])) 60 | axes[0].get_xaxis().set_visible(False) 61 | 62 | print(code) 63 | data = fdr.DataReader(code) 64 | data_ = data[startdate:enddate] 65 | print(code, startdate, enddate) 66 | 67 | x = np.arange(len(data_.index)) 68 | ohlc = data_[['Open', 'High', 'Low', 'Close']].values 69 | dohlc = np.hstack((np.reshape(x, (-1, 1)), ohlc)) 70 | 71 | # 봉차트 72 | candlestick_ohlc(axes[0], dohlc, width=0.5, colorup='r', colordown='b') 73 | 74 | # 거래량 차트 75 | axes[1].bar(x, data_['Volume'], color='grey', width=0.6, align='center') 76 | axes[1].set_xticks(range(len(x))) 77 | axes[1].set_xticklabels(list(data_.index.strftime('%Y-%m-%d')), rotation=90) 78 | axes[1].get_yaxis().set_visible(False) 79 | 80 | plt.tight_layout() 81 | 82 | output = io.BytesIO() 83 | FigureCanvas(fig).print_png(output) 84 | return Response(output.getvalue(), mimetype='image/png') 85 | 86 | 87 | @app.errorhandler(403) 88 | @app.errorhandler(404) 89 | @app.errorhandler(410) 90 | @app.errorhandler(500) 91 | def page_not_found(e): 92 | return render_template('error.html') 93 | 94 | @app.route('/pattern', methods=['GET', 'POST']) 95 | def pattern(): 96 | if request.method == 'POST': 97 | code = request.form['code'] 98 | startdate = request.form['startdate'] 99 | enddate = request.form['enddate'] 100 | else: 101 | code = request.args.get('code', None) 102 | startdate = request.args.get('startdate', None) 103 | enddate = request.args.get('enddate', None) 104 | p = pt.PatternFinder() 105 | p.set_stock(code) 106 | result = p.search(startdate, enddate) 107 | N = 5 108 | preds = p.stat_prediction(result, period=N) 109 | 110 | if len(preds) > 0: 111 | avg_ = preds.mean() * 100 112 | min_ = preds.min() * 100 113 | max_ = preds.max() * 100 114 | size_ = len(preds) 115 | print(avg_, min_, max_, size_) 116 | return render_template('result.html', code=code, startdate=startdate, enddate=enddate, avg=round(avg_, 2), min=round(min_, 2), max=round(max_, 2), size=size_) 117 | else: 118 | return render_template('result.html', code=code, startdate=startdate, enddate=enddate, noresult=1) 119 | 120 | if __name__ == '__main__': 121 | port = int(os.environ.get("PORT", 5000)) 122 | app.run(host="0.0.0.0", port=port, debug=False) 123 | # app.run(debug=True) 124 | -------------------------------------------------------------------------------- /pattern.py: -------------------------------------------------------------------------------- 1 | import FinanceDataReader as fdr 2 | import matplotlib.pyplot as plt 3 | import pandas as pd 4 | import numpy as np 5 | from matplotlib.figure import Figure 6 | 7 | 8 | class PatternFinder(): 9 | def __init__(self, period=5): 10 | self.period = period 11 | 12 | def set_stock(self, code: str): 13 | self.code = code 14 | self.data = fdr.DataReader(code) 15 | self.close = self.data['Close'] 16 | self.change = self.data['Change'] 17 | return self.data 18 | 19 | def search(self, start_date, end_date, threshold=0.98): 20 | base = self.close[start_date:end_date] 21 | self.base_norm = (base - base.min()) / (base.max() - base.min()) 22 | self.base = base 23 | 24 | window_size = len(base) 25 | moving_cnt = len(self.data) - window_size - self.period - 1 26 | cos_sims = self.__cosine_sims(moving_cnt, window_size) 27 | 28 | self.window_size = window_size 29 | cos_sims = cos_sims[cos_sims > threshold] 30 | return cos_sims 31 | 32 | 33 | def __cosine_sims(self, moving_cnt, window_size): 34 | def cosine_similarity(x, y): 35 | return np.dot(x, y) / (np.sqrt(np.dot(x, x)) * np.sqrt(np.dot(y, y))) 36 | 37 | # 유사도 저장 딕셔너리 38 | sim_list = [] 39 | 40 | for i in range(moving_cnt): 41 | target = self.close[i:i+window_size] 42 | 43 | # Normalize 44 | target_norm = (target - target.min()) / (target.max() - target.min()) 45 | 46 | # 코사인 유사도 저장 47 | cos_similarity = cosine_similarity(self.base_norm, target_norm) 48 | 49 | # 코사인 유사도 <- i(인덱스), 시계열데이터 함께 저장 50 | sim_list.append(cos_similarity) 51 | return pd.Series(sim_list).sort_values(ascending=False) 52 | 53 | 54 | def plot_pattern(self, idx, period=5): 55 | if period != self.period: 56 | self.period = period 57 | 58 | top = self.close[idx:idx+self.window_size+period] 59 | top_norm = (top - top.min()) / (top.max() - top.min()) 60 | fig = Figure() 61 | axis = fig.add_subplot(1, 1, 1) 62 | axis.plot(self.base_norm.values, label='base', color='black', alpha=0.7) 63 | axis.plot(top_norm.values, label='prediction', color='red', linestyle='dashed') 64 | axis.plot(top_norm.values[:len(self.base_norm.values)], label='pattern', color='red', linestyle='solid') 65 | axis.axvline(x=len(self.base_norm)-1, c='tomato', linestyle='dotted') 66 | axis.axvspan(len(self.base_norm.values)-1, len(top_norm.values)-1, facecolor='yellow', alpha=0.3) 67 | axis.legend() 68 | axis.get_yaxis().set_visible(False) 69 | axis.get_xaxis().set_visible(False) 70 | 71 | preds = self.change[idx+self.window_size: idx+self.window_size+period] 72 | print(f'pred: {preds.mean()*100} % ') 73 | return fig 74 | 75 | def stat_prediction(self, results, period=5): 76 | idx_list = list(results.keys()) 77 | mean_list = [] 78 | for idx in idx_list: 79 | pred = self.change[idx+self.window_size: idx+self.window_size+period] 80 | mean_list.append(pred.mean()) 81 | return np.array(mean_list) -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teddylee777/stock-pattern/e4d70bf6bbd481ac4bd062ce61500c76c62c1afa/requirements.txt -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.7.11 -------------------------------------------------------------------------------- /stock-pattern-demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 57, 6 | "source": [ 7 | "import warnings\r\n", 8 | "import FinanceDataReader as fdr\r\n", 9 | "import matplotlib.pyplot as plt\r\n", 10 | "from matplotlib.figure import Figure\r\n", 11 | "import pandas as pd\r\n", 12 | "import numpy as np\r\n", 13 | "\r\n", 14 | "warnings.filterwarnings('ignore')" 15 | ], 16 | "outputs": [], 17 | "metadata": {} 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "source": [ 23 | "# finance datareader로부터 데이터 불러오기\r\n", 24 | "data = fdr.DataReader('KS11')\r\n", 25 | "data" 26 | ], 27 | "outputs": [ 28 | { 29 | "output_type": "execute_result", 30 | "data": { 31 | "text/html": [ 32 | "
\n", 33 | "\n", 46 | "\n", 47 | " \n", 48 | " \n", 49 | " \n", 50 | " \n", 51 | " \n", 52 | " \n", 53 | " \n", 54 | " \n", 55 | " \n", 56 | " \n", 57 | " \n", 58 | " \n", 59 | " \n", 60 | " \n", 61 | " \n", 62 | " \n", 63 | " \n", 64 | " \n", 65 | " \n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | " \n", 131 | " \n", 132 | " \n", 133 | " \n", 134 | " \n", 135 | " \n", 136 | " \n", 137 | " \n", 138 | " \n", 139 | " \n", 140 | " \n", 141 | " \n", 142 | " \n", 143 | " \n", 144 | " \n", 145 | " \n", 146 | " \n", 147 | " \n", 148 | " \n", 149 | " \n", 150 | " \n", 151 | " \n", 152 | " \n", 153 | " \n", 154 | " \n", 155 | " \n", 156 | " \n", 157 | " \n", 158 | " \n", 159 | " \n", 160 | " \n", 161 | " \n", 162 | " \n", 163 | " \n", 164 | " \n", 165 | " \n", 166 | " \n", 167 | " \n", 168 | "
CloseOpenHighLowVolumeChange
Date
1981-05-01123.60123.60123.60123.603330000.00.0098
1981-05-02123.50123.50123.50123.502040000.0-0.0008
1981-05-04120.60120.60120.60120.601930000.0-0.0235
1981-05-06120.70120.70120.70120.701690000.00.0008
1981-05-07119.30119.30119.30119.301480000.0-0.0116
.....................
2021-09-273133.643121.703146.353119.26883230000.00.0027
2021-09-283097.923133.403134.463095.72894910000.0-0.0114
2021-09-293060.273055.503069.043030.60821560000.0-0.0122
2021-09-303068.823054.873079.433046.43885820000.00.0028
2021-10-013019.183056.213062.603015.01855480000.0-0.0162
\n", 169 | "

10798 rows × 6 columns

\n", 170 | "
" 171 | ], 172 | "text/plain": [ 173 | " Close Open High Low Volume Change\n", 174 | "Date \n", 175 | "1981-05-01 123.60 123.60 123.60 123.60 3330000.0 0.0098\n", 176 | "1981-05-02 123.50 123.50 123.50 123.50 2040000.0 -0.0008\n", 177 | "1981-05-04 120.60 120.60 120.60 120.60 1930000.0 -0.0235\n", 178 | "1981-05-06 120.70 120.70 120.70 120.70 1690000.0 0.0008\n", 179 | "1981-05-07 119.30 119.30 119.30 119.30 1480000.0 -0.0116\n", 180 | "... ... ... ... ... ... ...\n", 181 | "2021-09-27 3133.64 3121.70 3146.35 3119.26 883230000.0 0.0027\n", 182 | "2021-09-28 3097.92 3133.40 3134.46 3095.72 894910000.0 -0.0114\n", 183 | "2021-09-29 3060.27 3055.50 3069.04 3030.60 821560000.0 -0.0122\n", 184 | "2021-09-30 3068.82 3054.87 3079.43 3046.43 885820000.0 0.0028\n", 185 | "2021-10-01 3019.18 3056.21 3062.60 3015.01 855480000.0 -0.0162\n", 186 | "\n", 187 | "[10798 rows x 6 columns]" 188 | ] 189 | }, 190 | "metadata": {}, 191 | "execution_count": 2 192 | } 193 | ], 194 | "metadata": {} 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": 3, 199 | "source": [ 200 | "# 종가만 추출\r\n", 201 | "close = data['Close']\r\n", 202 | "\r\n", 203 | "# 비교 기준 구간\r\n", 204 | "start_date = '2021-09-01'\r\n", 205 | "end_date = '2021-09-16'" 206 | ], 207 | "outputs": [], 208 | "metadata": {} 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": 4, 213 | "source": [ 214 | "# 기준 구간 시계열 차트\r\n", 215 | "close[start_date:end_date].plot();" 216 | ], 217 | "outputs": [ 218 | { 219 | "output_type": "display_data", 220 | "data": { 221 | "image/png": "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", 222 | "text/plain": [ 223 | "
" 224 | ] 225 | }, 226 | "metadata": { 227 | "needs_background": "light" 228 | } 229 | } 230 | ], 231 | "metadata": {} 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 5, 236 | "source": [ 237 | "base = close[start_date:end_date]\r\n", 238 | "base" 239 | ], 240 | "outputs": [ 241 | { 242 | "output_type": "execute_result", 243 | "data": { 244 | "text/plain": [ 245 | "Date\n", 246 | "2021-09-01 3207.02\n", 247 | "2021-09-02 3175.85\n", 248 | "2021-09-03 3201.06\n", 249 | "2021-09-06 3203.33\n", 250 | "2021-09-07 3187.42\n", 251 | "2021-09-08 3162.99\n", 252 | "2021-09-09 3114.70\n", 253 | "2021-09-10 3125.76\n", 254 | "2021-09-13 3127.86\n", 255 | "2021-09-14 3148.83\n", 256 | "2021-09-15 3153.40\n", 257 | "2021-09-16 3130.09\n", 258 | "Name: Close, dtype: float64" 259 | ] 260 | }, 261 | "metadata": {}, 262 | "execution_count": 5 263 | } 264 | ], 265 | "metadata": {} 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 6, 270 | "source": [ 271 | "base_norm = (base - base.min()) / (base.max() - base.min())\r\n", 272 | "base_norm" 273 | ], 274 | "outputs": [ 275 | { 276 | "output_type": "execute_result", 277 | "data": { 278 | "text/plain": [ 279 | "Date\n", 280 | "2021-09-01 1.000000\n", 281 | "2021-09-02 0.662370\n", 282 | "2021-09-03 0.935442\n", 283 | "2021-09-06 0.960030\n", 284 | "2021-09-07 0.787695\n", 285 | "2021-09-08 0.523072\n", 286 | "2021-09-09 0.000000\n", 287 | "2021-09-10 0.119801\n", 288 | "2021-09-13 0.142548\n", 289 | "2021-09-14 0.369692\n", 290 | "2021-09-15 0.419194\n", 291 | "2021-09-16 0.166703\n", 292 | "Name: Close, dtype: float64" 293 | ] 294 | }, 295 | "metadata": {}, 296 | "execution_count": 6 297 | } 298 | ], 299 | "metadata": {} 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": 7, 304 | "source": [ 305 | "# 윈도우 사이즈\r\n", 306 | "window_size = len(base)\r\n", 307 | "\r\n", 308 | "# 예측 기간\r\n", 309 | "next_date = 5\r\n", 310 | "\r\n", 311 | "# 검색 횟수\r\n", 312 | "moving_cnt = len(base) - window_size - next_date - 1" 313 | ], 314 | "outputs": [], 315 | "metadata": {} 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": 8, 320 | "source": [ 321 | "# 유사도 저장 딕셔너리\r\n", 322 | "sim_list = []\r\n", 323 | "\r\n", 324 | "for i in range(moving_cnt):\r\n", 325 | " target = close[i:i+window_size]\r\n", 326 | " \r\n", 327 | " # Normalize\r\n", 328 | " target_norm = (target - target.min()) / (target.max() - target.min())\r\n", 329 | " \r\n", 330 | " # 코사인 유사도 저장\r\n", 331 | " cos_similarity = cosine(base_norm, target_norm)\r\n", 332 | " \r\n", 333 | " # 코사인 유사도 <- i(인덱스), 시계열데이터 함께 저장\r\n", 334 | " sim_list.append(cos_similarity)" 335 | ], 336 | "outputs": [], 337 | "metadata": {} 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": 9, 342 | "source": [ 343 | "pd.Series(sim_list).sort_values().head(20)" 344 | ], 345 | "outputs": [ 346 | { 347 | "output_type": "execute_result", 348 | "data": { 349 | "text/plain": [ 350 | "Series([], dtype: float64)" 351 | ] 352 | }, 353 | "metadata": {}, 354 | "execution_count": 9 355 | } 356 | ], 357 | "metadata": {} 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": 10, 362 | "source": [ 363 | "idx = 3193\r\n", 364 | "\r\n", 365 | "top_ = close[idx:idx+window_size+next_date]\r\n", 366 | "top_norm = (top_ - top_.min()) / (top_.max() - top_.min())\r\n", 367 | "\r\n", 368 | "plt.plot(base_norm.values, label='base')\r\n", 369 | "plt.plot(top_norm.values, label='target')\r\n", 370 | "plt.axvline(x=len(base_norm)-1, c='r', linestyle='--')\r\n", 371 | "plt.axvspan(len(base_norm.values)-1, len(top_norm.values)-1, facecolor='yellow', alpha=0.3)\r\n", 372 | "plt.legend()\r\n", 373 | "plt.show()" 374 | ], 375 | "outputs": [ 376 | { 377 | "output_type": "display_data", 378 | "data": { 379 | "image/png": "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", 380 | "text/plain": [ 381 | "
" 382 | ] 383 | }, 384 | "metadata": { 385 | "needs_background": "light" 386 | } 387 | } 388 | ], 389 | "metadata": {} 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": 51, 394 | "source": [ 395 | "class PatternFinder():\r\n", 396 | " def __init__(self, period=5):\r\n", 397 | " self.period = period\r\n", 398 | " \r\n", 399 | " def set_stock(self, code: str):\r\n", 400 | " self.code = code\r\n", 401 | " self.data = fdr.DataReader(code)\r\n", 402 | " self.close = self.data['Close']\r\n", 403 | " self.change = self.data['Change']\r\n", 404 | " return self.data\r\n", 405 | " \r\n", 406 | " def search(self, start_date, end_date, threshold=0.98):\r\n", 407 | " base = self.close[start_date:end_date]\r\n", 408 | " self.base_norm = (base - base.min()) / (base.max() - base.min())\r\n", 409 | " self.base = base\r\n", 410 | " \r\n", 411 | " display(base)\r\n", 412 | " \r\n", 413 | " window_size = len(base)\r\n", 414 | " moving_cnt = len(self.data) - window_size - self.period - 1\r\n", 415 | " cos_sims = self.__cosine_sims(moving_cnt, window_size)\r\n", 416 | " \r\n", 417 | " self.window_size = window_size\r\n", 418 | " cos_sims = cos_sims[cos_sims > threshold]\r\n", 419 | " return cos_sims\r\n", 420 | " \r\n", 421 | " \r\n", 422 | " def __cosine_sims(self, moving_cnt, window_size):\r\n", 423 | " def cosine_similarity(x, y):\r\n", 424 | " return np.dot(x, y) / (np.sqrt(np.dot(x, x)) * np.sqrt(np.dot(y, y)))\r\n", 425 | " \r\n", 426 | " # 유사도 저장 딕셔너리\r\n", 427 | " sim_list = []\r\n", 428 | "\r\n", 429 | " for i in range(moving_cnt):\r\n", 430 | " target = self.close[i:i+window_size]\r\n", 431 | "\r\n", 432 | " # Normalize\r\n", 433 | " target_norm = (target - target.min()) / (target.max() - target.min())\r\n", 434 | "\r\n", 435 | " # 코사인 유사도 저장\r\n", 436 | " cos_similarity = cosine_similarity(self.base_norm, target_norm)\r\n", 437 | "\r\n", 438 | " # 코사인 유사도 <- i(인덱스), 시계열데이터 함께 저장\r\n", 439 | " sim_list.append(cos_similarity)\r\n", 440 | " return pd.Series(sim_list).sort_values(ascending=False)\r\n", 441 | "\r\n", 442 | " \r\n", 443 | " def plot_pattern(self, idx, period=5):\r\n", 444 | " if period != self.period:\r\n", 445 | " self.period = period\r\n", 446 | " \r\n", 447 | " top = self.close[idx:idx+self.window_size+period]\r\n", 448 | " top_norm = (top - top.min()) / (top.max() - top.min())\r\n", 449 | "\r\n", 450 | " plt.plot(self.base_norm.values, label='base')\r\n", 451 | " plt.plot(top_norm.values, label='target')\r\n", 452 | " plt.axvline(x=len(self.base_norm)-1, c='r', linestyle='--')\r\n", 453 | " plt.axvspan(len(self.base_norm.values)-1, len(top_norm.values)-1, facecolor='yellow', alpha=0.3)\r\n", 454 | " plt.legend()\r\n", 455 | " plt.show()\r\n", 456 | " \r\n", 457 | " \r\n", 458 | " preds = self.change[idx+self.window_size: idx+self.window_size+period]\r\n", 459 | " display(preds)\r\n", 460 | " print(f'pred: {preds.mean()*100} % ')\r\n", 461 | "\r\n", 462 | " \r\n", 463 | " def stat_prediction(self, result, period=5):\r\n", 464 | " idx_list = list(result.keys())\r\n", 465 | " mean_list = []\r\n", 466 | " for idx in idx_list:\r\n", 467 | " pred = self.change[idx+self.window_size: idx+self.window_size+period]\r\n", 468 | " mean_list.append(pred.mean())\r\n", 469 | " return np.array(mean_list)" 470 | ], 471 | "outputs": [], 472 | "metadata": {} 473 | }, 474 | { 475 | "cell_type": "code", 476 | "execution_count": 52, 477 | "source": [ 478 | "p = PatternFinder()" 479 | ], 480 | "outputs": [], 481 | "metadata": {} 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": 53, 486 | "source": [ 487 | "p.set_stock('018700')" 488 | ], 489 | "outputs": [ 490 | { 491 | "output_type": "execute_result", 492 | "data": { 493 | "text/html": [ 494 | "
\n", 495 | "\n", 508 | "\n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " \n", 547 | " \n", 548 | " \n", 549 | " \n", 550 | " \n", 551 | " \n", 552 | " \n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " \n", 557 | " \n", 558 | " \n", 559 | " \n", 560 | " \n", 561 | " \n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | "
OpenHighLowCloseVolumeChange
Date
1997-09-091416611416611416611416620NaN
1997-09-101322311322311322311322350-0.066546
1997-09-111426181426181256231256101010-0.050100
1997-09-121350481350481208821208821010-0.037640
1997-09-1313032813032812372612371310100.023420
.....................
2021-09-27292530802860295521262910.026042
2021-09-28291531502910299529822530.013536
2021-09-292900303528452975787362-0.006678
2021-09-30303531302965302011590500.015126
2021-10-013130392530803925413154680.299669
\n", 631 | "

6000 rows × 6 columns

\n", 632 | "
" 633 | ], 634 | "text/plain": [ 635 | " Open High Low Close Volume Change\n", 636 | "Date \n", 637 | "1997-09-09 141661 141661 141661 141662 0 NaN\n", 638 | "1997-09-10 132231 132231 132231 132235 0 -0.066546\n", 639 | "1997-09-11 142618 142618 125623 125610 1010 -0.050100\n", 640 | "1997-09-12 135048 135048 120882 120882 1010 -0.037640\n", 641 | "1997-09-13 130328 130328 123726 123713 1010 0.023420\n", 642 | "... ... ... ... ... ... ...\n", 643 | "2021-09-27 2925 3080 2860 2955 2126291 0.026042\n", 644 | "2021-09-28 2915 3150 2910 2995 2982253 0.013536\n", 645 | "2021-09-29 2900 3035 2845 2975 787362 -0.006678\n", 646 | "2021-09-30 3035 3130 2965 3020 1159050 0.015126\n", 647 | "2021-10-01 3130 3925 3080 3925 41315468 0.299669\n", 648 | "\n", 649 | "[6000 rows x 6 columns]" 650 | ] 651 | }, 652 | "metadata": {}, 653 | "execution_count": 53 654 | } 655 | ], 656 | "metadata": {} 657 | }, 658 | { 659 | "cell_type": "code", 660 | "execution_count": 54, 661 | "source": [ 662 | "result = p.search('2021-09-10', '2021-09-30')" 663 | ], 664 | "outputs": [ 665 | { 666 | "output_type": "display_data", 667 | "data": { 668 | "text/plain": [ 669 | "Date\n", 670 | "2021-09-10 2625\n", 671 | "2021-09-13 2620\n", 672 | "2021-09-14 2630\n", 673 | "2021-09-15 2625\n", 674 | "2021-09-16 2635\n", 675 | "2021-09-17 2635\n", 676 | "2021-09-23 2920\n", 677 | "2021-09-24 2880\n", 678 | "2021-09-27 2955\n", 679 | "2021-09-28 2995\n", 680 | "2021-09-29 2975\n", 681 | "2021-09-30 3020\n", 682 | "Name: Close, dtype: int64" 683 | ] 684 | }, 685 | "metadata": {} 686 | } 687 | ], 688 | "metadata": {} 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": 59, 693 | "source": [ 694 | "pred = p.stat_prediction(result)\r\n", 695 | "pred" 696 | ], 697 | "outputs": [ 698 | { 699 | "output_type": "execute_result", 700 | "data": { 701 | "text/plain": [ 702 | "array([-0.01124373, 0.00517852, 0. , 0. , -0.01440996,\n", 703 | " 0.00531945, 0.03402142, 0.00472001, -0.02052574, 0.00670564,\n", 704 | " 0.09559623])" 705 | ] 706 | }, 707 | "metadata": {}, 708 | "execution_count": 59 709 | } 710 | ], 711 | "metadata": {} 712 | }, 713 | { 714 | "cell_type": "code", 715 | "execution_count": 60, 716 | "source": [ 717 | "p.plot_pattern(2138)" 718 | ], 719 | "outputs": [ 720 | { 721 | "output_type": "display_data", 722 | "data": { 723 | "text/plain": [ 724 | "Date\n", 725 | "2006-03-07 0.040917\n", 726 | "2006-03-08 -0.050551\n", 727 | "2006-03-09 0.017761\n", 728 | "2006-03-10 -0.058170\n", 729 | "2006-03-13 -0.006176\n", 730 | "Name: Change, dtype: float64" 731 | ] 732 | }, 733 | "metadata": {} 734 | }, 735 | { 736 | "output_type": "stream", 737 | "name": "stdout", 738 | "text": [ 739 | "pred: -1.1243734433432984 % \n" 740 | ] 741 | } 742 | ], 743 | "metadata": {} 744 | }, 745 | { 746 | "cell_type": "markdown", 747 | "source": [ 748 | "## 웹으로 만들기" 749 | ], 750 | "metadata": {} 751 | }, 752 | { 753 | "cell_type": "code", 754 | "execution_count": 16, 755 | "source": [ 756 | "import ipywidgets as widgets\r\n", 757 | "from IPython.display import display, clear_output\r\n", 758 | "\r\n", 759 | "# !jupyter nbextension enable --py widgetsnbextension --sys-prefix\r\n", 760 | "# !jupyter serverextension enable voila --sys-prefix" 761 | ], 762 | "outputs": [], 763 | "metadata": {} 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": 17, 768 | "source": [ 769 | "start_date = widgets.DatePicker(description='시작일')\r\n", 770 | "end_date = widgets.DatePicker(description='종료일')" 771 | ], 772 | "outputs": [], 773 | "metadata": {} 774 | }, 775 | { 776 | "cell_type": "code", 777 | "execution_count": 18, 778 | "source": [ 779 | "# button send\r\n", 780 | "search_button = widgets.Button(\r\n", 781 | " description='예측하기',\r\n", 782 | " tooltip='Send',\r\n", 783 | " style={'description_width': 'initial'}\r\n", 784 | " )\r\n", 785 | "\r\n", 786 | "output = widgets.Output()\r\n", 787 | "\r\n", 788 | "def on_button_clicked(event):\r\n", 789 | " with output:\r\n", 790 | " clear_output()\r\n", 791 | " p = PatternFinder()\r\n", 792 | " p.set_stock(code.value)\r\n", 793 | " result = p.search(start_date.value.strftime('%Y-%m-%d'), end_date.value.strftime('%Y-%m-%d'))\r\n", 794 | " print(result)\r\n", 795 | " for idx in result.keys():\r\n", 796 | " p.plot_pattern(idx)\r\n", 797 | " \r\n", 798 | "search_button.on_click(on_button_clicked)\r\n", 799 | "\r\n", 800 | "vbox_result = widgets.VBox([search_button, output])" 801 | ], 802 | "outputs": [], 803 | "metadata": {} 804 | }, 805 | { 806 | "cell_type": "code", 807 | "execution_count": 19, 808 | "source": [ 809 | "label_headline = widgets.Label(\r\n", 810 | " value='Photo by CDC on Unsplash',\r\n", 811 | " )\r\n", 812 | "\r\n", 813 | "vbox_headline = widgets.VBox([label_headline])" 814 | ], 815 | "outputs": [], 816 | "metadata": {} 817 | }, 818 | { 819 | "cell_type": "code", 820 | "execution_count": 20, 821 | "source": [ 822 | "text_0 = widgets.HTML(value=\"

종목 코드

\")\r\n", 823 | "text_1 = widgets.HTML(value=\"

날짜 선택

\")\r\n", 824 | "text_2 = widgets.HTML(value=\"

패턴 예측

\")\r\n", 825 | "\r\n", 826 | "code = widgets.Text(placeholder='종목코드를 입력해 주세요')\r\n", 827 | "code.value = '035720'\r\n", 828 | "\r\n", 829 | "vbox_0 = widgets.VBox([text_0, code])\r\n", 830 | "vbox_1 = widgets.VBox([text_1,start_date, end_date ])\r\n", 831 | "vbox_2 = widgets.VBox([text_2, vbox_result])" 832 | ], 833 | "outputs": [], 834 | "metadata": {} 835 | }, 836 | { 837 | "cell_type": "code", 838 | "execution_count": 21, 839 | "source": [ 840 | "page = widgets.VBox([vbox_0, vbox_1 , vbox_2])\r\n", 841 | "display(page)" 842 | ], 843 | "outputs": [ 844 | { 845 | "output_type": "display_data", 846 | "data": { 847 | "application/vnd.jupyter.widget-view+json": { 848 | "version_major": 2, 849 | "version_minor": 0, 850 | "model_id": "7ab15e22cbfb46f48f56e5c8877a7c73" 851 | }, 852 | "text/plain": [ 853 | "VBox(children=(VBox(children=(HTML(value='

종목 코드

'), Text(value='035720', placeholder='종목코드를 입력해 주세요'))…" 854 | ] 855 | }, 856 | "metadata": {} 857 | } 858 | ], 859 | "metadata": {} 860 | }, 861 | { 862 | "cell_type": "code", 863 | "execution_count": null, 864 | "source": [], 865 | "outputs": [], 866 | "metadata": {} 867 | }, 868 | { 869 | "cell_type": "code", 870 | "execution_count": null, 871 | "source": [], 872 | "outputs": [], 873 | "metadata": {} 874 | } 875 | ], 876 | "metadata": { 877 | "kernelspec": { 878 | "name": "python3", 879 | "display_name": "Python 3.7.11 64-bit ('stock-pattern': conda)" 880 | }, 881 | "language_info": { 882 | "codemirror_mode": { 883 | "name": "ipython", 884 | "version": 3 885 | }, 886 | "file_extension": ".py", 887 | "mimetype": "text/x-python", 888 | "name": "python", 889 | "nbconvert_exporter": "python", 890 | "pygments_lexer": "ipython3", 891 | "version": "3.7.11" 892 | }, 893 | "interpreter": { 894 | "hash": "6494861ce0a9247699d4b666f0b06cd8da3baab5a92264a9fe7e94a414e538ea" 895 | } 896 | }, 897 | "nbformat": 4, 898 | "nbformat_minor": 5 899 | } -------------------------------------------------------------------------------- /templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 10 | 13 | 16 | 17 | 18 | 21 | 23 | 24 | 25 | 35 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 처음으로 44 |
45 |

오류........

46 |

오류가 발생하였습니다.

47 |

teddynote.ds@gmail.com로 제보해 주시면 수정하도록 하겠습니다.

48 | 49 |
50 |
51 |
52 |
53 |

© 테디노트. All Rights Reserved.

54 |

유튜브

55 |

블로그

56 |

제안: teddynote.ds@gmail.com

57 |
58 |
59 | 60 | 61 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 10 | 13 | 16 | 17 | 18 | 21 | 22 | 24 | 25 | 41 | 42 | 52 | 54 | 55 | 56 | 57 |
58 |
59 | 60 |

주식 패턴 검색기

61 |

종목 코드를 입력해 주세요. 종목 코드를 모르신다면 여기에서 검색해 주세요.

63 | 64 |
65 |
66 |
67 |
68 | 종목 코드 69 |
70 | {% if code %} 71 | 73 | {% else %} 74 | 76 | {% endif %} 77 |
78 |
79 |

날짜를 선택해 주세요.

80 |
81 |
82 |
83 |
84 | 85 |
86 | {% if startdate %} 87 | 89 | {% else %} 90 | 92 | {% endif %} 93 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | 103 |
104 | {% if enddate %} 105 | 107 | 108 | {% else %} 109 | 111 | {% endif %} 112 | 113 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | {% if chart %} 122 | Responsive image 124 | {% endif %} 125 |
126 |
127 | 128 |
129 |
130 | 132 |
133 |
134 |
135 | 136 |
137 | 138 | 139 |
140 |
141 |
142 |

© 테디노트. All Rights Reserved.

143 |

유튜브

144 |

블로그

145 |

제안: teddynote.ds@gmail.com

146 |
147 |
148 | 149 | 150 | 216 | 217 | -------------------------------------------------------------------------------- /templates/result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 10 | 13 | 16 | 17 | 18 | 21 | 23 | 24 | 25 | 35 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 처음으로 44 |
45 |
46 |
47 |

종목코드: {{code}}

48 |
49 |
50 |
51 |
52 |

기준일 : {{startdate}} ~ {{enddate}}

53 |
54 |
55 | {% if noresult %} 56 |

죄송합니다ㅠㅠ

57 |

검색하신 기간에는 유사 패턴을 찾을 수 없습니다.

58 | {% else %} 59 |

유사도 98%이상 패턴 검색에 대한 결과입니다.

60 |

{{size}}개의 유사 패턴이 검출 되었습니다.

61 |
    62 |
  • 최저 수익률은 {{min}} %의 수익률을 보였습니다.
  • 63 |
  • 최고 수익률은 {{max}} %의 수익률을 보였습니다.
  • 64 |
  • 평균 수익률은 {{avg}} %의 수익률을 보였습니다.
  • 65 |
66 |

유사도가 가장 높았던 패턴의 추세입니다.

67 |
68 | Responsive image 70 | 71 |
    72 |
  • base는 기준일의 주가 패턴을 나타냅니다
  • 73 |
  • target은 기준일과 가장 유사한 패턴의 결과를 보여줍니다 (향후 5일간)
  • 74 |
75 |
76 | {% endif %} 77 | 78 |
79 |
80 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | http-socket = :$(PORT) 3 | master = true 4 | die-on-term = true 5 | module = main:app 6 | memory-report = true --------------------------------------------------------------------------------