Spaces:
Runtime error
Runtime error
mdj1412
commited on
Commit
โข
c109682
0
Parent(s):
commit first
Browse files- .gitattributes +34 -0
- .gitignore +3 -0
- LICENSE +21 -0
- README.md +62 -0
- app.py +348 -0
- dataset_creation/SP500_crawling.py +67 -0
- dataset_creation/crawling_nasdaq_news.py +578 -0
- dataset_creation/nasdaq100_crawling.py +67 -0
- dataset_creation/nasdaq_data.py +126 -0
- dataset_creation/nasdaq_url.tsv +110 -0
- dataset_creation/save_news_url.tsv +0 -0
- dataset_creation/textLength.xlsx +0 -0
- dataset_creation/tickers_numAndAvg.xlsx +0 -0
- modules/inference.py +287 -0
- modules/templates/example.html +0 -0
- requirements.txt +11 -0
- static/css/chartStyle.css +132 -0
- static/css/news.css +207 -0
- static/css/style.css +287 -0
- static/js/chartIndex.js +246 -0
- static/js/chartjs-chart-financial.js +525 -0
- static/js/index.js +274 -0
- static/js/news.js +248 -0
- templates/chart.html +164 -0
- templates/index.html +106 -0
- templates/news_analysis.html +88 -0
.gitattributes
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
29 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
30 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
31 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
32 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
33 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
34 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
templates/news.html
|
2 |
+
**/__pycache__/**
|
3 |
+
news/**
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2022 sod723
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Stock News Analysis
|
2 |
+
|
3 |
+
This project uses Natural Language Processing (NLP) techniques and machine learning models to analyze stock news, extract valuable insights, and provide visualizations to help users make informed investment decisions.
|
4 |
+
|
5 |
+
## Features
|
6 |
+
1. Named Entity Recognition (NER) - This feature identifies and extracts important entities such as company names, people, and locations from news articles to help users stay up-to-date with the latest news related to their investments.
|
7 |
+
2. Tk-instruct Model - This feature allows users to ask questions about the stock market and receive answers in a conversational format. The Tk-instruct model uses machine learning algorithms to understand natural language queries and provides relevant information to the user.
|
8 |
+
3. Stock Chart Visualization - This feature provides users with an interactive chart that visualizes the historical performance of a stock. Users can customize the time frame and chart settings to view the information that is most relevant to them.
|
9 |
+
4. News Crawler - This feature enables users to keep track of the latest news related to their investments. The news crawler regularly scans news websites and automatically extracts articles that mention specific companies or industries.
|
10 |
+
|
11 |
+
|
12 |
+
## Installation
|
13 |
+
1. Clone the repository
|
14 |
+
```console
|
15 |
+
git clone https://github.com/mdj1412/Stock_News_Analysis.git
|
16 |
+
```
|
17 |
+
2. Install the required packages
|
18 |
+
```console
|
19 |
+
pip install -r requirements.txt
|
20 |
+
```
|
21 |
+
3. Run the application
|
22 |
+
```console
|
23 |
+
python app.py
|
24 |
+
```
|
25 |
+
|
26 |
+
|
27 |
+
## Dependency
|
28 |
+
* pandas
|
29 |
+
* beautifulsoup4
|
30 |
+
* Flask
|
31 |
+
* torch
|
32 |
+
* transformers
|
33 |
+
* accelerate
|
34 |
+
* bitsandbytes
|
35 |
+
* spacy
|
36 |
+
* yfinance
|
37 |
+
|
38 |
+
|
39 |
+
## Demo
|
40 |
+
* You can check a little faster through the demo [here](https://huggingface.co/spaces/mdj1412/stock_news_summaries_AI).
|
41 |
+
|
42 |
+
|
43 |
+
## License
|
44 |
+
This project is licensed under the MIT [License]() file for details.
|
45 |
+
|
46 |
+
|
47 |
+
|
48 |
+
|
49 |
+
|
50 |
+
|
51 |
+
---
|
52 |
+
title: Stock News Summaries AI
|
53 |
+
emoji: ๐
|
54 |
+
colorFrom: blue
|
55 |
+
colorTo: red
|
56 |
+
sdk: gradio
|
57 |
+
sdk_version: 3.17.0
|
58 |
+
app_file: app.py
|
59 |
+
pinned: false
|
60 |
+
---
|
61 |
+
|
62 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from bs4 import BeautifulSoup
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
from flask import Flask, jsonify, request, render_template
|
6 |
+
|
7 |
+
import spacy
|
8 |
+
from spacy import displacy
|
9 |
+
|
10 |
+
# ๋ชจ๋ธ์ ์ ์ฉ์ํค๋ ํ์ผ
|
11 |
+
from modules.inference import Tk_instruct
|
12 |
+
|
13 |
+
# Stocks Data
|
14 |
+
from dataset_creation.nasdaq_data import get_list, get_data
|
15 |
+
|
16 |
+
|
17 |
+
# Flask Object ์์ฑ
|
18 |
+
# __name__์ ํ์ฌ ์คํ ์ค์ธ ๋ชจ๋ ์ด๋ฆ์ ์ ๋ฌํ๋ ๊ฒ์ด๋ค.
|
19 |
+
app = Flask(__name__)
|
20 |
+
|
21 |
+
|
22 |
+
# def stocks() ์ฌ์ฉ & News Data
|
23 |
+
# Pandas DataFrame : ticker, name, sector, industry, diff, open, close, date
|
24 |
+
demo_dic = get_list()
|
25 |
+
|
26 |
+
|
27 |
+
|
28 |
+
|
29 |
+
|
30 |
+
##### Home #####
|
31 |
+
@app.route('/')
|
32 |
+
def home_page():
|
33 |
+
example_embed = 'This website analyzes stock market news and provides answers to questions related to news articles.'
|
34 |
+
return render_template('index.html', embed=example_embed)# html์ ๋ถ๋ฌ์ฌ ๋,
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
##### Data fetch #####
|
39 |
+
@app.route('/submit', methods=['GET', 'POST'])
|
40 |
+
def submit():
|
41 |
+
input_text = request.args.get('input_text')
|
42 |
+
return jsonify(result={"output":"My output is a summary of: "+input_text})
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
|
47 |
+
@app.route('/model', methods=['GET', 'POST'])
|
48 |
+
def model():
|
49 |
+
print("\t\t Start model !!!")
|
50 |
+
|
51 |
+
# Javascript ์์ ๋ฐ์ ๋ฉ์์ง
|
52 |
+
text_input = request.args.get('text_input')
|
53 |
+
|
54 |
+
print(f"Fetch from Javascript /inference, text_input : {text_input}")
|
55 |
+
|
56 |
+
# modules/reference.py ์์ ๋ชจ๋ธ ์ ์ฉ
|
57 |
+
output = Tk_instruct(text_input)
|
58 |
+
|
59 |
+
text_output = {"text_output": output}
|
60 |
+
print(f"Fetch from Javascript /inference, text_output : {text_output}")
|
61 |
+
return jsonify(result=text_output)
|
62 |
+
|
63 |
+
|
64 |
+
|
65 |
+
|
66 |
+
# Show Ticker's Table
|
67 |
+
@app.route('/stocks', methods=['GET', 'POST'])
|
68 |
+
def stocks():
|
69 |
+
result = demo_dic.to_dict() # dictionary ํํ๋ก ๋ณํ
|
70 |
+
return jsonify(result=result)
|
71 |
+
|
72 |
+
|
73 |
+
|
74 |
+
|
75 |
+
|
76 |
+
|
77 |
+
|
78 |
+
|
79 |
+
|
80 |
+
################################################################################################
|
81 |
+
|
82 |
+
|
83 |
+
# {ticker1: [{๋ ์ง1: [์ ๋ชฉ1, ์ ๋ชฉ2, ...]}, {๋ ์ง2: [์ ๋ชฉ3, ์ ๋ชฉ4, ...]}, ...], ticker2: [{๋ ์ง3: [์ ๋ชฉ5, ์ ๋ชฉ6, ...]}, {๋ ์ง4: [์ ๋ชฉ7, ์ ๋ชฉ8, ...]}, ...], ... }
|
84 |
+
ticker_dic = dict.fromkeys(demo_dic.ticker, []) # ticker1: [{๋ ์ง1: [์ ๋ชฉ1, ์ ๋ชฉ2, ...]}
|
85 |
+
|
86 |
+
|
87 |
+
|
88 |
+
dir = './news'
|
89 |
+
if not os.path.exists(dir):
|
90 |
+
raise NotImplementedError("Not exists News Data")# ์ค๋ฅ ๊ฐ์ ๋ฐ์
|
91 |
+
|
92 |
+
# News Data List ๊ฐ์ ธ์ค๊ธฐ
|
93 |
+
for key in os.listdir(dir):
|
94 |
+
if key not in ticker_dic.keys():
|
95 |
+
raise NotImplementedError("Not exists Ticker")# ์ค๋ฅ ๊ฐ์ ๋ฐ์
|
96 |
+
|
97 |
+
dir2 = os.path.join(dir, key)
|
98 |
+
ticker_dic[key] = dict.fromkeys(os.listdir(dir2), []) # ๋ ์ง1: [์ ๋ชฉ1, ์ ๋ชฉ2, ...]
|
99 |
+
|
100 |
+
for date in os.listdir(dir2):
|
101 |
+
dir3 = os.path.join(dir2, date)
|
102 |
+
title_list = [title for title in os.listdir(dir3)]
|
103 |
+
|
104 |
+
# ํด๋น ๋ ์ง์ News๊ฐ ์์ ์๋ ์์
|
105 |
+
if len(title_list) != 0:
|
106 |
+
ticker_dic[key][date] = title_list # [์ ๋ชฉ1, ์ ๋ชฉ2, ...]
|
107 |
+
else:
|
108 |
+
ticker_dic[key].pop(date)
|
109 |
+
|
110 |
+
|
111 |
+
# from IPython import embed; embed()
|
112 |
+
|
113 |
+
|
114 |
+
|
115 |
+
# Show Ticker's Title
|
116 |
+
@app.route('/<ticker>', methods=['GET', 'POST'])
|
117 |
+
def ticker(ticker):
|
118 |
+
example_embed = "%s Chart" % (ticker)
|
119 |
+
|
120 |
+
return render_template('chart.html', embed=example_embed)
|
121 |
+
|
122 |
+
|
123 |
+
# Show Ticker's Data
|
124 |
+
@app.route('/chart', methods=['GET', 'POST'])
|
125 |
+
def chart():
|
126 |
+
print("Start /chart ")
|
127 |
+
|
128 |
+
# Javascript ์์ ๋ฐ์ ๋ฉ์์ง
|
129 |
+
ticker = request.args.get('ticker')
|
130 |
+
|
131 |
+
# Implement Module
|
132 |
+
chart_data = get_data(tickers=[ticker], numOfDay=120)[0]
|
133 |
+
|
134 |
+
# ๋ ์ง ํ์ ๋ฐ๊พธ๊ธฐ
|
135 |
+
chart_data.index = [k.strftime("%Y-%m-%d") for k in chart_data.index]
|
136 |
+
|
137 |
+
result = chart_data.to_dict()
|
138 |
+
return jsonify(result=result)
|
139 |
+
|
140 |
+
|
141 |
+
@app.route('/news', methods=['GET', 'POST'])
|
142 |
+
def news():
|
143 |
+
print("Start /news ")
|
144 |
+
|
145 |
+
# Javascript ์์ ๋ฐ์ ๋ฉ์์ง
|
146 |
+
ticker = request.args.get('ticker')
|
147 |
+
|
148 |
+
news_dir = os.path.join('./news', ticker)
|
149 |
+
|
150 |
+
# ํด๋น Ticker์ ๋ ์ง๋ณ ๋ด์ค ์ ๋ชฉ์ ๊ฐ์ ธ์จ๋ค.
|
151 |
+
result = {}
|
152 |
+
for key in os.listdir(news_dir):
|
153 |
+
title_list = os.listdir(os.path.join(news_dir, key))
|
154 |
+
if len(title_list) != 0:
|
155 |
+
result[key] = os.listdir(os.path.join(news_dir, key))
|
156 |
+
|
157 |
+
# ์ต๊ทผ ๋ด์ค๋ถํฐ ๋ณด์ด๊ฒ (์ ๋ ฌ)
|
158 |
+
sorted_result = {}
|
159 |
+
for key, value in sorted(result.items(), reverse=True):
|
160 |
+
sorted_result[key] = value
|
161 |
+
|
162 |
+
return jsonify(result=sorted_result)
|
163 |
+
|
164 |
+
|
165 |
+
|
166 |
+
|
167 |
+
|
168 |
+
################################################################################################
|
169 |
+
|
170 |
+
# 1. ๊ธฐ๋ณธ url
|
171 |
+
# 2. ์ฟผ๋ฆฌ ์คํธ๋ง์ด ์กด์ฌํ๋ url
|
172 |
+
# : request.args.get('๋ณ์์ด๋ฆ')์ ์ฌ์ฉํ์ฌ /user?๋ณ์=๊ฐ&๋ณ์=๊ฐ&...์์ ์ํ๋ ๋ณ์์ ๊ฐ์ ์ป์ ์ ์๋ค.
|
173 |
+
# 3. clean URL
|
174 |
+
|
175 |
+
|
176 |
+
|
177 |
+
# Show Ticker's Title and News's Title
|
178 |
+
@app.route('/info', methods=['GET', 'POST'])
|
179 |
+
def ticker_title():
|
180 |
+
print("app.py : /info Start ")
|
181 |
+
|
182 |
+
# Javascript ์์ ๋ฐ์ ๋ฉ์์ง
|
183 |
+
ticker = request.args.get('ticker')
|
184 |
+
date = request.args.get('date')
|
185 |
+
title = request.args.get('title')
|
186 |
+
andSymbolInTitle = request.args.get('andSymbolInTitle')
|
187 |
+
|
188 |
+
|
189 |
+
# Title ์์ '&'๋ก ํ์๋์ด ์๋๋ฐ ๋ฐ๋ก ๊ตฌ๋ณํด์ผ ๋๋ค.
|
190 |
+
# andSymbolInTitle ์์ ๊ฐ์ ธ์จ '&' ์์น index๋ฅผ title๊ณผ ํฉ์ณ์ค๋ค.
|
191 |
+
if andSymbolInTitle != '':
|
192 |
+
andSymbolInTitle = andSymbolInTitle.split(',')
|
193 |
+
for i in range(len(andSymbolInTitle)): # String -> int
|
194 |
+
andSymbolInTitle[i] = int(andSymbolInTitle[i])
|
195 |
+
for idx in andSymbolInTitle:
|
196 |
+
title = title[0:idx] + '&' + title[idx:len(title)]
|
197 |
+
|
198 |
+
|
199 |
+
|
200 |
+
# ํด๋น Ticker, Date, Title์ URL์ ๊ฐ์ ธ์ค๊ธฐ
|
201 |
+
url_dir = "dataset_creation/save_news_url.tsv"
|
202 |
+
|
203 |
+
if not os.path.exists(url_dir):
|
204 |
+
raise NotImplementedError("Not exists {} directory", url_dir)
|
205 |
+
else:
|
206 |
+
df = pd.read_csv(url_dir, sep='\t', index_col=0)
|
207 |
+
|
208 |
+
filt = (df['ticker'] == ticker) & (df['date'] == date) & (df['title'] == title)
|
209 |
+
url = list(df.loc[filt, 'url'].values)
|
210 |
+
|
211 |
+
if len(url) != 1:
|
212 |
+
from IPython import embed; embed()
|
213 |
+
raise NotImplementedError("There exists many URL or empty")
|
214 |
+
else:
|
215 |
+
url = url[0]
|
216 |
+
|
217 |
+
example_embed1 = ticker
|
218 |
+
example_embed2 = "Date: %s" % (date)
|
219 |
+
example_embed3 = "Title: %s" % (title)
|
220 |
+
example_embed4 = url
|
221 |
+
|
222 |
+
|
223 |
+
return render_template('news_analysis.html', embed1=example_embed1, embed2=example_embed2, embed3=example_embed3, embed4=example_embed4)
|
224 |
+
|
225 |
+
|
226 |
+
|
227 |
+
|
228 |
+
|
229 |
+
|
230 |
+
@app.route('/ner', methods=['GET', 'POST'])
|
231 |
+
def ner():
|
232 |
+
print("Start /ner")
|
233 |
+
|
234 |
+
# Javascript ์์ ๋ฐ์ ๋ฉ์์ง
|
235 |
+
ticker = request.args.get('ticker')
|
236 |
+
date = request.args.get('date')
|
237 |
+
title = request.args.get('title')
|
238 |
+
|
239 |
+
print(ticker, date, title)
|
240 |
+
|
241 |
+
# ๋ด์ค ๋ฐ์ดํฐ ์์น ์ฐพ๊ธฐ ( in directory )
|
242 |
+
dir = os.path.join('./news', ticker, date, title+'.txt')
|
243 |
+
|
244 |
+
f = open(dir, 'r')
|
245 |
+
news_data = f.read()
|
246 |
+
|
247 |
+
|
248 |
+
|
249 |
+
# NER
|
250 |
+
nlp = spacy.load("en_core_web_sm")
|
251 |
+
doc = nlp(news_data) # News Data Analysis
|
252 |
+
|
253 |
+
|
254 |
+
# ํ์์๋ ์ฉ์ด๋ค ๋ฒ๋ฆฌ๊ธฐ
|
255 |
+
print("=====================================================================")
|
256 |
+
|
257 |
+
ents = {'text': [], 'start_char': [], 'end_char': [], 'label_': []}
|
258 |
+
for ent in doc.ents:
|
259 |
+
# print(ent.text, ent.start_char, ent.end_char, ent.label_)
|
260 |
+
|
261 |
+
# ๋ฒ๋ฆฌ๋ ์ฉ์ด๋ค
|
262 |
+
if ent.label_ == 'DATE':
|
263 |
+
continue
|
264 |
+
if ent.label_ == 'TIME':
|
265 |
+
continue
|
266 |
+
if ent.label_ == 'CARDINAL':
|
267 |
+
continue
|
268 |
+
if ent.label_ == 'MONEY':
|
269 |
+
continue
|
270 |
+
if ent.label_ == 'PERCENT':
|
271 |
+
continue
|
272 |
+
if ent.label_ == 'ORDINAL':
|
273 |
+
continue
|
274 |
+
|
275 |
+
|
276 |
+
print(ent.text, ent.start_char, ent.end_char, ent.label_)
|
277 |
+
|
278 |
+
ents['text'].append(ent.text)
|
279 |
+
ents['start_char'].append(ent.start_char)
|
280 |
+
ents['end_char'].append(ent.end_char)
|
281 |
+
ents['label_'].append(ent.label_)
|
282 |
+
|
283 |
+
print("=====================================================================")
|
284 |
+
|
285 |
+
ents['news'] = news_data
|
286 |
+
|
287 |
+
# ents = {'text': [], 'start_char': [], 'end_char': [], 'label_': [], 'news': []}
|
288 |
+
return jsonify(result=ents)
|
289 |
+
|
290 |
+
|
291 |
+
|
292 |
+
|
293 |
+
|
294 |
+
|
295 |
+
@app.route('/newsQuestions', methods=['GET', 'POST'])
|
296 |
+
def newsQuestions():
|
297 |
+
# Javascript ์์ ๋ฐ์ ๋ฉ์์ง
|
298 |
+
ticker = request.args.get('ticker')
|
299 |
+
date = request.args.get('date')
|
300 |
+
title = request.args.get('title')
|
301 |
+
andSymbolInTitle = request.args.get('andSymbolInTitle')
|
302 |
+
questions = request.args.get('questions')
|
303 |
+
|
304 |
+
|
305 |
+
|
306 |
+
# Title ์์ '&'๋ก ํ์๋์ด ์๋๋ฐ ๋ฐ๋ก ๊ตฌ๋ณํด์ผ ๋๋ค.
|
307 |
+
# andSymbolInTitle ์์ ๊ฐ์ ธ์จ '&' ์์น index๋ฅผ title๊ณผ ํฉ์ณ์ค๋ค.
|
308 |
+
if andSymbolInTitle != '':
|
309 |
+
andSymbolInTitle = andSymbolInTitle.split(',')
|
310 |
+
for i in range(len(andSymbolInTitle)): # String -> int
|
311 |
+
andSymbolInTitle[i] = int(andSymbolInTitle[i])
|
312 |
+
for idx in andSymbolInTitle:
|
313 |
+
title = title[0:idx] + '&' + title[idx:len(title)]
|
314 |
+
|
315 |
+
|
316 |
+
|
317 |
+
# ๋ด์ค ๋ฐ์ดํฐ ์์น ์ฐพ๊ธฐ ( in directory )
|
318 |
+
dir = os.path.join('./news', ticker, date, title+'.txt')
|
319 |
+
|
320 |
+
# ๋ด์ซ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
321 |
+
f = open(dir, 'r')
|
322 |
+
text = f.read()
|
323 |
+
f.close()
|
324 |
+
|
325 |
+
# ๋ชจ๋ธ ์ ์ฉ
|
326 |
+
answer = Tk_instruct(text, questions)
|
327 |
+
|
328 |
+
result = {}
|
329 |
+
result['answer'] = answer
|
330 |
+
|
331 |
+
return jsonify(result=result)
|
332 |
+
|
333 |
+
|
334 |
+
|
335 |
+
# Terminal : Flask : ์์ ํ๋ฉด ํฐ๋ฏธ๋ ์ฌ์คํ
|
336 |
+
# Elements : HTML : ์์ ํ๋ฉด ํฐ๋ฏธ๋ ์ฌ์คํ
|
337 |
+
# Console : javascript : ์ฌ์ดํธ ๋๊ธฐํ
|
338 |
+
# Sources : File : ์ฌ์ดํธ ๋๊ธฐํ
|
339 |
+
# CSS : ์ฌ์ดํธ ๋๊ธฐํ
|
340 |
+
|
341 |
+
|
342 |
+
|
343 |
+
if __name__ == "__main__":
|
344 |
+
# run app
|
345 |
+
# host : ๋ชจ๋ IP์ ๋ํด ์ ๊ทผ ํ์ฉ, ( Default. localhost = 127.0.0.1 )
|
346 |
+
# port : ์ ์์ open๋ http port, ( Default. port = 5000 )
|
347 |
+
app.run(host='0.0.0.0', port='7860') #http://0.0.0.0:5001
|
348 |
+
# app.run(debug=True) #http://0.0.0.0:5001
|
dataset_creation/SP500_crawling.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from bs4 import BeautifulSoup
|
2 |
+
import urllib.request
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
url = "https://en.wikipedia.org/wiki/List_of_S%26P_500_companies"
|
6 |
+
|
7 |
+
def get_SP500():
|
8 |
+
# Set List
|
9 |
+
SP500_list = []
|
10 |
+
|
11 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
12 |
+
html = urllib.request.urlopen(url).read()
|
13 |
+
soup = BeautifulSoup(html, 'html.parser')
|
14 |
+
|
15 |
+
# html์์ table ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
16 |
+
table = soup.find("table", {"class": "wikitable sortable"})
|
17 |
+
|
18 |
+
for row in table.findAll("tr")[1:]:
|
19 |
+
|
20 |
+
ticker = get_ticker(row)
|
21 |
+
name = get_name(row)
|
22 |
+
sector = get_sector(row)
|
23 |
+
industry = get_industry(row)
|
24 |
+
|
25 |
+
SP500_list.append(return_comment_form(ticker, name, sector, industry))
|
26 |
+
|
27 |
+
return SP500_list
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
|
33 |
+
def return_comment_form(ticker, name, sector, industry):
|
34 |
+
comment = {'ticker': ticker,
|
35 |
+
'name': name,
|
36 |
+
'sector' : sector,
|
37 |
+
'industry' : industry
|
38 |
+
}
|
39 |
+
return comment
|
40 |
+
|
41 |
+
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
def get_ticker(row):
|
47 |
+
return row.select('td')[0].text.strip()
|
48 |
+
|
49 |
+
def get_name(row):
|
50 |
+
return row.select('td')[1].text.strip()
|
51 |
+
|
52 |
+
def get_sector(row):
|
53 |
+
return row.select('td')[3].text.strip()
|
54 |
+
|
55 |
+
def get_industry(row):
|
56 |
+
return row.select('td')[4].text.strip()
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
if __name__ == '__main__':
|
62 |
+
a = get_SP500()
|
63 |
+
|
64 |
+
print(pd.DataFrame(a))
|
65 |
+
print("Finish")
|
66 |
+
|
67 |
+
|
dataset_creation/crawling_nasdaq_news.py
ADDED
@@ -0,0 +1,578 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from bs4 import BeautifulSoup
|
2 |
+
import urllib.request
|
3 |
+
import pandas as pd
|
4 |
+
from tqdm import trange
|
5 |
+
import datetime
|
6 |
+
import os
|
7 |
+
import pandas as pd
|
8 |
+
import matplotlib.pyplot as plt
|
9 |
+
import numpy as np
|
10 |
+
|
11 |
+
import nasdaq100_crawling
|
12 |
+
|
13 |
+
nasdaq_url = "https://www.marketscreener.com/quote/index/NASDAQ-100-4946/components/col=7&asc=0&fpage={}"
|
14 |
+
|
15 |
+
|
16 |
+
|
17 |
+
# Ticker ๋ง๋ค ๋ด์ค ๊ธฐ์ฌ ๋ชฉ๋ก ์ฌ์ดํธ ํ์ธํ๊ธฐ
|
18 |
+
def get_codezb():
|
19 |
+
# Execute "nasdaq100_crawling" Module
|
20 |
+
nasdaq_dic = pd.DataFrame(nasdaq100_crawling.get_nasdaq100())
|
21 |
+
# Get Nasdaq 100 List
|
22 |
+
nasdaq100_codezb = pd.DataFrame(index=nasdaq_dic.ticker, columns=['url'])
|
23 |
+
|
24 |
+
count=0
|
25 |
+
for page in trange(1, 4):
|
26 |
+
print("page : ", page)
|
27 |
+
url = nasdaq_url.format(page)
|
28 |
+
|
29 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
30 |
+
html = urllib.request.urlopen(url).read()
|
31 |
+
soup = BeautifulSoup(html, 'html.parser')
|
32 |
+
|
33 |
+
# ํ ํ์ด์ง์ 50๊ฐ ๋ชฉ๋ก
|
34 |
+
row = soup.select('table [class="tabBodyLV17"] tr')
|
35 |
+
for idx in trange(1, len(row)):
|
36 |
+
count+=1
|
37 |
+
# ํด๋น ํ์ฌ์ url
|
38 |
+
idx_url = "https://www.marketscreener.com" + row[idx].select('td')[1].find('a')['href']
|
39 |
+
|
40 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
41 |
+
idx_html = urllib.request.urlopen(idx_url).read()
|
42 |
+
idx_soup = BeautifulSoup(idx_html, 'html.parser')
|
43 |
+
|
44 |
+
ticker = idx_soup.select('span [class=fvTitleInfo]')[0].text
|
45 |
+
|
46 |
+
nasdaq100_codezb.loc[ticker, 'url'] = idx_url + "news-quality/"
|
47 |
+
|
48 |
+
nasdaq100_codezb.to_csv('./dataset_creation/nasdaq_url.tsv', index=True, sep='\t')
|
49 |
+
print("{} / {}".format(count, len(nasdaq_dic.ticker)))
|
50 |
+
from IPython import embed; embed()
|
51 |
+
|
52 |
+
|
53 |
+
|
54 |
+
|
55 |
+
# ๋ฌธ์์ด ๊ธธ์ด ํ์ธ ๊ทธ๋ฆฌ๊ณ ๋ด์ค ๊ธฐ์ฌ ๊ธธ์ด
|
56 |
+
def get_textLength_and_newsCount():
|
57 |
+
total_count=0
|
58 |
+
ranking = {}
|
59 |
+
newsTextLengthList = []
|
60 |
+
|
61 |
+
dir = './news'
|
62 |
+
for ticker in os.listdir(dir):
|
63 |
+
ticker_count=0
|
64 |
+
tickerTextLength_avg=0.0
|
65 |
+
|
66 |
+
dir2 = os.path.join(dir, ticker)
|
67 |
+
if ticker == ".DS_Store":
|
68 |
+
os.remove(dir2) #ํ์ผ์ญ์
|
69 |
+
continue
|
70 |
+
|
71 |
+
for date in os.listdir(dir2):
|
72 |
+
|
73 |
+
dir3 = os.path.join(dir2, date)
|
74 |
+
if date == ".DS_Store":
|
75 |
+
os.remove(dir3) #ํ์ผ์ญ์
|
76 |
+
continue
|
77 |
+
|
78 |
+
if date < "2023.01.01":
|
79 |
+
for title in os.listdir(dir3):
|
80 |
+
remove_dic = os.path.join(dir3, title)
|
81 |
+
os.remove(remove_dic) #ํ์ผ์ญ์
|
82 |
+
os.rmdir(dir3) #ํด๋น directory ์ญ์
|
83 |
+
continue
|
84 |
+
|
85 |
+
# print(date)
|
86 |
+
for title in os.listdir(dir3):
|
87 |
+
dir4=os.path.join(dir3, title)
|
88 |
+
|
89 |
+
f = open(dir4, 'r')
|
90 |
+
data = f.read()
|
91 |
+
length = len(data)
|
92 |
+
|
93 |
+
newsTextLengthList.append(length)
|
94 |
+
tickerTextLength_avg+=length
|
95 |
+
|
96 |
+
ticker_count+=1
|
97 |
+
total_count+=1
|
98 |
+
|
99 |
+
avg = 0.0
|
100 |
+
if ticker_count != 0:
|
101 |
+
avg = tickerTextLength_avg/ticker_count
|
102 |
+
ranking[ticker]=[ticker_count, avg]
|
103 |
+
|
104 |
+
print("Ranking\t| Ticker\t| # of news\t| Average of News Text Length")
|
105 |
+
sorted_pairs = sorted(ranking.items(), key=lambda x: -x[1][0])
|
106 |
+
tickers, values = [], []
|
107 |
+
for i, (ticker, element) in enumerate(sorted_pairs, start=1):
|
108 |
+
tickers.append(ticker)
|
109 |
+
values.append(element[0])
|
110 |
+
print("{}\t{}\t{}\t{:.2f}".format(i, ticker, element[0], element[1]))
|
111 |
+
|
112 |
+
|
113 |
+
|
114 |
+
# Draw Graph
|
115 |
+
x = np.arange(len(os.listdir(dir)))
|
116 |
+
|
117 |
+
plt.figure(figsize=(14, 6)) # Size of Window
|
118 |
+
|
119 |
+
plt.bar(x=x, height=values, color='C2') # ๋ง๋๊ทธ๋ํ ๊ทธ๋ฆฌ๊ธฐ
|
120 |
+
plt.xticks(ticks=x, labels=tickers, rotation=90, fontsize=5) # X๊ฐ ํ์
|
121 |
+
plt.tick_params(axis='x', direction='in', length=3, pad=6, labelcolor='blue')
|
122 |
+
|
123 |
+
plt.title("Number of News Data ( NASDAQ 100 )") # Write Title
|
124 |
+
plt.xlabel('Tickers') # Write X-axis
|
125 |
+
plt.ylabel('# of News') # Write Y-axis
|
126 |
+
plt.show()
|
127 |
+
|
128 |
+
# Show Total Number of News and News Text Length
|
129 |
+
print("======================================================")
|
130 |
+
print("{} : {}".format("์ ์ฒด ๋ด์ค ๊ฐฏ์", total_count))
|
131 |
+
|
132 |
+
df = pd.DataFrame(ranking, index=["Number of News", "Average of News Text Length"])
|
133 |
+
df = df.transpose()
|
134 |
+
df.to_excel("dataset_creation/tickers_numAndAvg.xlsx")
|
135 |
+
print(df["Number of News"].describe())
|
136 |
+
|
137 |
+
text_length_df = pd.DataFrame(newsTextLengthList, columns=["News Text Length"])
|
138 |
+
print(text_length_df.describe())
|
139 |
+
text_length_df.to_excel("dataset_creation/textLength.xlsx")
|
140 |
+
|
141 |
+
from IPython import embed; embed()
|
142 |
+
|
143 |
+
|
144 |
+
|
145 |
+
|
146 |
+
|
147 |
+
|
148 |
+
|
149 |
+
|
150 |
+
def get_news(tickers, boundary_date='2023.01.01'):
|
151 |
+
if not os.path.exists('./news'):
|
152 |
+
os.mkdir('./news')
|
153 |
+
|
154 |
+
# NASDAQ Tickers List ๊ฐ์ ธ์ค๊ธฐ
|
155 |
+
nasdaq100_codezb = pd.read_csv('./dataset_creation/nasdaq_url.tsv', sep='\t', index_col='ticker')
|
156 |
+
nasdaq_tickers = list(nasdaq100_codezb.index)
|
157 |
+
|
158 |
+
total_count=0
|
159 |
+
for ticker in tickers:
|
160 |
+
num=0
|
161 |
+
print("============================== {} ==============================".format(ticker))
|
162 |
+
# ํด๋น Ticker ์ Directory ๊ฐ ์กด์ฌํ๋์ง ํ์ธ
|
163 |
+
if not os.path.exists('./news/' + ticker):
|
164 |
+
os.mkdir('./news/' + ticker)
|
165 |
+
|
166 |
+
# ํด๋น Ticker ๊ฐ NASDAQ Tickers List ์ ์กด์ฌํ์ง ์์ ๋,
|
167 |
+
if ticker not in nasdaq_tickers:
|
168 |
+
print("[ Check NASDAQ Tickers List ]")
|
169 |
+
print(ticker, "is not in NASDAQ 100")
|
170 |
+
from IPython import embed; embed()
|
171 |
+
|
172 |
+
# ํด๋น Ticker ๊ฐ News URL ์ ๊ฐ์ง๊ณ ์์ง ์์ ๋,
|
173 |
+
if pd.isna(nasdaq100_codezb.loc[ticker, 'url']):
|
174 |
+
print("[ Check get_codezb() Method ]")
|
175 |
+
print(ticker, "has not News URL")
|
176 |
+
|
177 |
+
if ticker == "TEAM":
|
178 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/ATLASSIAN-CORPORATION-25531314/news-quality/"
|
179 |
+
elif ticker == "BKR":
|
180 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/BAKER-HUGHES-COMPANY-40311111/news-quality/"
|
181 |
+
elif ticker == "CSGP":
|
182 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/COSTAR-GROUP-INC-8923/news-quality/"
|
183 |
+
elif ticker == "FANG":
|
184 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/DIAMONDBACK-ENERGY-INC-11732858/news-quality/"
|
185 |
+
elif ticker == "ENPH":
|
186 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/ENPHASE-ENERGY-INC-10335237/news-quality/"
|
187 |
+
elif ticker == "GFS":
|
188 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/GLOBALFOUNDRIES-INC-128691269/news-quality/"
|
189 |
+
elif ticker == "RIVN":
|
190 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/RIVIAN-AUTOMOTIVE-INC-129226108/news-quality/"
|
191 |
+
elif ticker == "WBD":
|
192 |
+
nasdaq100_codezb.loc[ticker, 'url']="https://www.marketscreener.com/quote/stock/WARNER-BROS-DISCOVERY-I-136094563/news-quality/"
|
193 |
+
else: from IPython import embed; embed()
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
|
198 |
+
# ํด๋น Ticker News URL ๊ฐ์ ธ์ค๊ธฐ
|
199 |
+
url = nasdaq100_codezb.loc[ticker, 'url'] + "&&fpage={}"
|
200 |
+
|
201 |
+
page=0
|
202 |
+
stop=False
|
203 |
+
while (True):
|
204 |
+
page+=1
|
205 |
+
print("URL : {}".format(url.format(page)))
|
206 |
+
|
207 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
208 |
+
html = urllib.request.urlopen(url.format(page)).read()
|
209 |
+
soup = BeautifulSoup(html, 'html.parser')
|
210 |
+
|
211 |
+
|
212 |
+
# ํ ํ์ด์ง์ ์๋ ๋ด์ค ๋ฆฌ์คํธ
|
213 |
+
news_list = soup.select('td[class="std_txt th_inner"]')[0].select('table[class="tabBody"] tr')
|
214 |
+
for news in news_list:
|
215 |
+
# 1. Date
|
216 |
+
date = news.select('td')[0].text
|
217 |
+
|
218 |
+
if ':' in date:
|
219 |
+
today = datetime.datetime.now()
|
220 |
+
today = today.strftime("%Y.%m.%d")
|
221 |
+
date = today
|
222 |
+
elif '/' in date:
|
223 |
+
date = date.replace('/', '.')
|
224 |
+
today = datetime.datetime.now()
|
225 |
+
today = today.strftime("%Y")
|
226 |
+
date = today + "." + date
|
227 |
+
else:
|
228 |
+
# XXX : ์ ๋
๋๋ ํด๊ฒฐํ์ง ๋ชปํจ
|
229 |
+
date = date + ".12.12"#์์๋ ์ง
|
230 |
+
|
231 |
+
|
232 |
+
# boundary_date ์ดํ๋ง ํฌ๋กค๋ง
|
233 |
+
if date < boundary_date:
|
234 |
+
stop=True
|
235 |
+
break
|
236 |
+
|
237 |
+
|
238 |
+
|
239 |
+
# 2. URL
|
240 |
+
news_url = "https://www.marketscreener.com/" + news.select('td')[1].select('a')[0]['href']
|
241 |
+
|
242 |
+
|
243 |
+
# 3. Title
|
244 |
+
news_title = news.select('td')[1].text
|
245 |
+
|
246 |
+
if 'โฆ' in news_title:
|
247 |
+
news_title = news_title.replace('/โฆ', '')
|
248 |
+
if '/' in news_title:
|
249 |
+
news_title = news_title.replace('/', '|')
|
250 |
+
|
251 |
+
|
252 |
+
# 4. News Form
|
253 |
+
news_form = news.select('td')[2].text
|
254 |
+
|
255 |
+
|
256 |
+
# A) ํฌ๋กค๋งํ๊ธฐ ์ ์, Directory ์กด์ฌํ๋์ง ํ์ธํด๋ณด๊ธฐ
|
257 |
+
if not os.path.exists('./news/{}/{}'.format(ticker, date)):
|
258 |
+
os.mkdir('./news/{}/{}'.format(ticker, date))
|
259 |
+
|
260 |
+
|
261 |
+
# B) ํด๋น Ticker, Date, Title์ URL์ ๋ฐ๋ก ์ ์ฅ
|
262 |
+
save_news_url(ticker, date, news_url, news_title)
|
263 |
+
|
264 |
+
|
265 |
+
# C) ์ฌ๊ธฐ์๋ถํฐ ๋ด์ค ๋ด์ฉ ํฌ๋กค๋ง
|
266 |
+
if news_form == 'MT':
|
267 |
+
# ์ ๋ชฉ๋ง ์๊ณ ๋ด์ค ๊ธฐ์ฌ๋ ์์
|
268 |
+
# ์ผ๋จ Pass
|
269 |
+
pass
|
270 |
+
|
271 |
+
elif news_form == 'MD':
|
272 |
+
get_md(ticker, date, news_url, news_title)
|
273 |
+
num+=1
|
274 |
+
|
275 |
+
elif news_form == 'RE':
|
276 |
+
get_re(ticker, date, news_url, news_title)
|
277 |
+
num+=1
|
278 |
+
|
279 |
+
elif news_form == 'AQ':
|
280 |
+
get_aq(ticker, date, news_url, news_title)
|
281 |
+
num+=1
|
282 |
+
|
283 |
+
elif news_form == 'DJ':
|
284 |
+
# ๋ด์ค ๊ธฐ์ฌ๋ค์ ์์ฝํ ๋ด์ฉ๋ค์ ๋ชจ์๋๊ฑฐ์ฌ์
|
285 |
+
# ํฌ๋กค๋ง์ ์ํด๋ ๊ด์ฐฎ์ ๊ฒ ๊ฐ์
|
286 |
+
# => ์ผ๋จ ๋ณด๋ฅ
|
287 |
+
pass
|
288 |
+
|
289 |
+
elif (news_form == '') or (news_form == 'PR')or (news_form == 'PU'):
|
290 |
+
get_(ticker, date, news_url, news_title)
|
291 |
+
num+=1
|
292 |
+
|
293 |
+
elif news_form == 'AN':
|
294 |
+
get_an(ticker, date, news_url, news_title)
|
295 |
+
num+=1
|
296 |
+
|
297 |
+
elif news_form == 'CI':
|
298 |
+
# ๊ตฌ๋
์ ํด์ผ์ง ํ์ธํ ์ ์์
|
299 |
+
# ํฌ๋กค๋ง์ด ์๋จ
|
300 |
+
pass
|
301 |
+
|
302 |
+
elif news_form == 'BU':
|
303 |
+
get_bu(ticker, date, news_url, news_title)
|
304 |
+
num+=1
|
305 |
+
|
306 |
+
else:
|
307 |
+
print('Pass. {}'.format(news_form))
|
308 |
+
continue
|
309 |
+
|
310 |
+
if stop: break
|
311 |
+
print("\nNumber of Crawling News : {}".format(num))
|
312 |
+
total_count+=num
|
313 |
+
|
314 |
+
return total_count
|
315 |
+
|
316 |
+
|
317 |
+
|
318 |
+
|
319 |
+
|
320 |
+
|
321 |
+
|
322 |
+
|
323 |
+
|
324 |
+
|
325 |
+
|
326 |
+
|
327 |
+
|
328 |
+
|
329 |
+
# ํด๋น Ticker, Date, Title์ URL์ ๋ฐ๋ก ์ ์ฅ
|
330 |
+
def save_news_url(ticker, date, url, title):
|
331 |
+
dir = "dataset_creation/save_news_url.tsv"
|
332 |
+
|
333 |
+
if not os.path.exists(dir):
|
334 |
+
df = pd.DataFrame(columns=['ticker', 'date', 'title', 'url'])
|
335 |
+
else:
|
336 |
+
df = pd.read_csv(dir, sep='\t', index_col=0)
|
337 |
+
|
338 |
+
filt = (df['ticker'] == ticker) & (df['date'] == date) & (df['title'] == title)
|
339 |
+
|
340 |
+
# ์ฒ์์ผ๋ก ์ ์ฅํ ๋
|
341 |
+
if len(df[filt]) == 0:
|
342 |
+
df.loc[len(df)] = [ticker, date, title, url]
|
343 |
+
# ๊ธฐ์กด์ ์ ์ฅ๋์ด ์์๋ค๋ฉด, ๋ค์ ์
๋ฐ์ดํธ
|
344 |
+
else:
|
345 |
+
df.loc[filt, 'url'] = url
|
346 |
+
|
347 |
+
df.to_csv(dir, sep='\t')
|
348 |
+
|
349 |
+
|
350 |
+
|
351 |
+
|
352 |
+
|
353 |
+
|
354 |
+
|
355 |
+
|
356 |
+
|
357 |
+
|
358 |
+
|
359 |
+
|
360 |
+
|
361 |
+
|
362 |
+
|
363 |
+
|
364 |
+
|
365 |
+
|
366 |
+
|
367 |
+
|
368 |
+
|
369 |
+
|
370 |
+
|
371 |
+
|
372 |
+
|
373 |
+
|
374 |
+
|
375 |
+
|
376 |
+
def get_md(ticker, date, url, title):
|
377 |
+
# ์ ๋ชฉ์ด ์กด์ฌํ๋ค๋ฉด, Pass
|
378 |
+
if os.path.exists('./news/{}/{}/{}.txt'.format(ticker, date, title)):
|
379 |
+
return
|
380 |
+
|
381 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
382 |
+
html = urllib.request.urlopen(url).read()
|
383 |
+
soup = BeautifulSoup(html, 'html.parser')
|
384 |
+
|
385 |
+
# ๊ฒ์ฌ
|
386 |
+
a=soup.select('span[class=clearfix]')
|
387 |
+
if len(a) != 1:
|
388 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
389 |
+
from IPython import embed; embed()
|
390 |
+
return
|
391 |
+
a=soup.select('span[class=clearfix]')[0].select('div[id=grantexto]')
|
392 |
+
if len(a) != 1:
|
393 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
394 |
+
from IPython import embed; embed()
|
395 |
+
return
|
396 |
+
a=soup.select('span[class=clearfix]')[0].select('div[id=grantexto]')[0].select('p')
|
397 |
+
if len(a) != 1:
|
398 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
399 |
+
from IPython import embed; embed()
|
400 |
+
return
|
401 |
+
|
402 |
+
# ์์
|
403 |
+
text = soup.select('span[class=clearfix]')[0].select('div[id=grantexto]')[0].select('p')[0].text
|
404 |
+
|
405 |
+
file = open('./news/{}/{}/{}.txt'.format(ticker, date, title), 'w')
|
406 |
+
file.write(text)
|
407 |
+
file.close()
|
408 |
+
|
409 |
+
|
410 |
+
|
411 |
+
def get_re(ticker, date, url, title):
|
412 |
+
# ์ ๋ชฉ์ด ์กด์ฌํ๋ค๋ฉด, Pass
|
413 |
+
if os.path.exists('./news/{}/{}/{}.txt'.format(ticker, date, title)):
|
414 |
+
return
|
415 |
+
|
416 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
417 |
+
html = urllib.request.urlopen(url).read()
|
418 |
+
soup = BeautifulSoup(html, 'html.parser')
|
419 |
+
|
420 |
+
# ๊ฒ์ฌ
|
421 |
+
a=soup.select('div[id=grantexto]')
|
422 |
+
if len(a) != 1:
|
423 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
424 |
+
from IPython import embed; embed()
|
425 |
+
|
426 |
+
# ์์
|
427 |
+
text_list=soup.select('div[id=grantexto] p')
|
428 |
+
text = ''
|
429 |
+
for i in range(len(text_list)):
|
430 |
+
text = text + text_list[i].text
|
431 |
+
|
432 |
+
file = open('./news/{}/{}/{}.txt'.format(ticker, date, title), 'w')
|
433 |
+
file.write(text)
|
434 |
+
file.close()
|
435 |
+
|
436 |
+
|
437 |
+
|
438 |
+
def get_aq(ticker, date, url, title):
|
439 |
+
# ์ ๋ชฉ์ด ์กด์ฌํ๋ค๋ฉด, Pass
|
440 |
+
if os.path.exists('./news/{}/{}/{}.txt'.format(ticker, date, title)):
|
441 |
+
return
|
442 |
+
|
443 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
444 |
+
html = urllib.request.urlopen(url).read()
|
445 |
+
soup = BeautifulSoup(html, 'html.parser')
|
446 |
+
|
447 |
+
# ๊ฒ์ฌ
|
448 |
+
a=soup.select('div[id=grantexto]')
|
449 |
+
if len(a) != 1:
|
450 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
451 |
+
from IPython import embed; embed()
|
452 |
+
|
453 |
+
# ์์
|
454 |
+
text_list=soup.select('div[id=grantexto] p')
|
455 |
+
text = ''
|
456 |
+
for i in range(len(text_list)):
|
457 |
+
text = text + text_list[i].text
|
458 |
+
|
459 |
+
file = open('./news/{}/{}/{}.txt'.format(ticker, date, title), 'w')
|
460 |
+
file.write(text)
|
461 |
+
file.close()
|
462 |
+
|
463 |
+
|
464 |
+
|
465 |
+
|
466 |
+
def get_(ticker, date, url, title):
|
467 |
+
# ์ ๋ชฉ์ด ์กด์ฌํ๋ค๋ฉด, Pass
|
468 |
+
if os.path.exists('./news/{}/{}/{}.txt'.format(ticker, date, title)):
|
469 |
+
return
|
470 |
+
|
471 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
472 |
+
html = urllib.request.urlopen(url).read()
|
473 |
+
soup = BeautifulSoup(html, 'html.parser')
|
474 |
+
|
475 |
+
# ๊ฒ์ฌ
|
476 |
+
a=soup.select('div[id=grantexto]')
|
477 |
+
if len(a) != 1:
|
478 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
479 |
+
from IPython import embed; embed()
|
480 |
+
|
481 |
+
# ์์
|
482 |
+
text = a[0].text
|
483 |
+
|
484 |
+
file = open('./news/{}/{}/{}.txt'.format(ticker, date, title), 'w')
|
485 |
+
file.write(text)
|
486 |
+
file.close()
|
487 |
+
|
488 |
+
|
489 |
+
|
490 |
+
def get_an(ticker, date, url, title):
|
491 |
+
# ์ ๋ชฉ์ด ์กด์ฌํ๋ค๋ฉด, Pass
|
492 |
+
if os.path.exists('./news/{}/{}/{}.txt'.format(ticker, date, title)):
|
493 |
+
return
|
494 |
+
|
495 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
496 |
+
html = urllib.request.urlopen(url).read()
|
497 |
+
soup = BeautifulSoup(html, 'html.parser')
|
498 |
+
|
499 |
+
# ๊ฒ์ฌ
|
500 |
+
a=soup.select('div[id=grantexto]')
|
501 |
+
if len(a) != 1:
|
502 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
503 |
+
from IPython import embed; embed()
|
504 |
+
|
505 |
+
# ์์
|
506 |
+
text_list=soup.select('div[id=grantexto] p')
|
507 |
+
text = ''
|
508 |
+
for i in range(len(text_list)):
|
509 |
+
text = text + text_list[i].text
|
510 |
+
|
511 |
+
file = open('./news/{}/{}/{}.txt'.format(ticker, date, title), 'w')
|
512 |
+
file.write(text)
|
513 |
+
file.close()
|
514 |
+
|
515 |
+
|
516 |
+
def get_bu(ticker, date, url, title):
|
517 |
+
# ์ ๋ชฉ์ด ์กด์ฌํ๋ค๋ฉด, Pass
|
518 |
+
if os.path.exists('./news/{}/{}/{}.txt'.format(ticker, date, title)):
|
519 |
+
return
|
520 |
+
|
521 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
522 |
+
html = urllib.request.urlopen(url).read()
|
523 |
+
soup = BeautifulSoup(html, 'html.parser')
|
524 |
+
|
525 |
+
# ๊ฒ์ฌ
|
526 |
+
a=soup.select('div[id=grantexto]')
|
527 |
+
if len(a) != 1:
|
528 |
+
print("ticker : {}, date : {}, url : {}, title : {}".format(ticker, date, url, title))
|
529 |
+
from IPython import embed; embed()
|
530 |
+
|
531 |
+
# ์์
|
532 |
+
text_list=soup.select('div[id=grantexto] p')
|
533 |
+
text = ''
|
534 |
+
for i in range(len(text_list)):
|
535 |
+
text = text + text_list[i].text
|
536 |
+
|
537 |
+
file = open('./news/{}/{}/{}.txt'.format(ticker, date, title), 'w')
|
538 |
+
file.write(text)
|
539 |
+
file.close()
|
540 |
+
|
541 |
+
|
542 |
+
|
543 |
+
|
544 |
+
|
545 |
+
|
546 |
+
|
547 |
+
|
548 |
+
|
549 |
+
|
550 |
+
|
551 |
+
|
552 |
+
|
553 |
+
|
554 |
+
|
555 |
+
|
556 |
+
|
557 |
+
|
558 |
+
|
559 |
+
|
560 |
+
|
561 |
+
if __name__ == '__main__':
|
562 |
+
get_textLength_and_newsCount()
|
563 |
+
|
564 |
+
|
565 |
+
# get_codezb()
|
566 |
+
|
567 |
+
nasdaq_dic = pd.DataFrame(nasdaq100_crawling.get_nasdaq100())
|
568 |
+
nasdaq100_tickers = list(nasdaq_dic.ticker)
|
569 |
+
# total_count=get_news(nasdaq100_tickers)
|
570 |
+
total_count=get_news(nasdaq100_tickers, boundary_date="2023.01.01")
|
571 |
+
|
572 |
+
|
573 |
+
# tickers = ['ADP', 'AAPL', 'META']
|
574 |
+
# total_count=get_news(tickers)
|
575 |
+
|
576 |
+
|
577 |
+
print("total_count : ", total_count)
|
578 |
+
print("Finish")
|
dataset_creation/nasdaq100_crawling.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from bs4 import BeautifulSoup
|
2 |
+
import urllib.request
|
3 |
+
import pandas as pd
|
4 |
+
|
5 |
+
url = "https://en.wikipedia.org/wiki/Nasdaq-100"
|
6 |
+
|
7 |
+
def get_nasdaq100():
|
8 |
+
# Set List
|
9 |
+
SP500_list = []
|
10 |
+
|
11 |
+
# html ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
12 |
+
html = urllib.request.urlopen(url).read()
|
13 |
+
soup = BeautifulSoup(html, 'html.parser')
|
14 |
+
|
15 |
+
# html์์ table ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
16 |
+
table = soup.findAll("table", {"class": "wikitable sortable"})[2]
|
17 |
+
|
18 |
+
for row in table.findAll("tr")[1:]:
|
19 |
+
|
20 |
+
ticker = get_ticker(row)
|
21 |
+
name = get_name(row)
|
22 |
+
sector = get_sector(row)
|
23 |
+
industry = get_industry(row)
|
24 |
+
|
25 |
+
SP500_list.append(return_comment_form(ticker, name, sector, industry))
|
26 |
+
|
27 |
+
return SP500_list
|
28 |
+
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
|
33 |
+
def return_comment_form(ticker, name, sector, industry):
|
34 |
+
comment = {'ticker': ticker,
|
35 |
+
'name': name,
|
36 |
+
'sector' : sector,
|
37 |
+
'industry' : industry
|
38 |
+
}
|
39 |
+
return comment
|
40 |
+
|
41 |
+
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
def get_ticker(row):
|
47 |
+
return row.select('td')[1].text.strip()
|
48 |
+
|
49 |
+
def get_name(row):
|
50 |
+
return row.select('td')[0].text.strip()
|
51 |
+
|
52 |
+
def get_sector(row):
|
53 |
+
return row.select('td')[2].text.strip()
|
54 |
+
|
55 |
+
def get_industry(row):
|
56 |
+
return row.select('td')[3].text.strip()
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
|
61 |
+
if __name__ == '__main__':
|
62 |
+
a = get_nasdaq100()
|
63 |
+
|
64 |
+
print(pd.DataFrame(a))
|
65 |
+
print("Finish")
|
66 |
+
|
67 |
+
|
dataset_creation/nasdaq_data.py
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
from datetime import datetime
|
3 |
+
from datetime import timedelta
|
4 |
+
import yfinance as yf
|
5 |
+
from dataset_creation import nasdaq100_crawling
|
6 |
+
|
7 |
+
|
8 |
+
# Execute "nasdaq100_crawling" Module
|
9 |
+
nasdaq_dic = pd.DataFrame(nasdaq100_crawling.get_nasdaq100())
|
10 |
+
# Get Nasdaq 100 List
|
11 |
+
nasdaq100_symbols = list(nasdaq_dic.ticker)
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
|
21 |
+
# ๋ฐ๋ชจ์์ ๋ฉ๋ด์์ ํ์ํ ์ ๋ณด๋ค
|
22 |
+
# Ticker, Name, Diff, Open, Close, Sector, Industry, Date
|
23 |
+
def get_list(tickers=nasdaq100_symbols):
|
24 |
+
demo_dic = pd.DataFrame(nasdaq100_crawling.get_nasdaq100())
|
25 |
+
|
26 |
+
for i in range(len(demo_dic.index)):
|
27 |
+
ticker = demo_dic.loc[i, 'ticker']
|
28 |
+
|
29 |
+
# ticker์ ์ฃผ์ ์ ๋ณด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์จ๋ค.
|
30 |
+
data = get_data(tickers=[ticker], numOfDay=3)[0]
|
31 |
+
|
32 |
+
|
33 |
+
yesterday = data.iloc[-2, 3]
|
34 |
+
today = data.iloc[-1, 3]
|
35 |
+
demo_dic.loc[i, 'diff'] = round(((today-yesterday)/today) * 100.0, 2)
|
36 |
+
demo_dic.loc[i, 'open'] = round(data.iloc[-1, 0], 2) # Open
|
37 |
+
demo_dic.loc[i, 'close'] = round(data.iloc[-1, 3], 2) # Close
|
38 |
+
date = str(data.index[-1].year) + "-" + str(data.index[-1].month) + "-" + str(data.index[-1].day)
|
39 |
+
demo_dic.loc[i, 'date'] = date # Date
|
40 |
+
|
41 |
+
return demo_dic
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
# ์ฃผ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
46 |
+
def get_data(tickers=nasdaq100_symbols, numOfDay=2):#numOfDay: ๋ ์ง ๊ฐ๊ฒฉ
|
47 |
+
output = []
|
48 |
+
|
49 |
+
# ์์ ๋ ์ง ~ ์ต๊ทผ๊น์ง ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ
|
50 |
+
now = datetime.now() # ์ค๋ ๋ ์ง
|
51 |
+
date = now.weekday() # ์์ผ ํ์ธ
|
52 |
+
|
53 |
+
if date == 5: # Saturday
|
54 |
+
start_date = datetime(now.year, now.month, now.day, 0, 0) - timedelta(days=numOfDay+4)
|
55 |
+
end_date = datetime(now.year, now.month, now.day, 0, 0)
|
56 |
+
elif date == 6 or date == 0 or date == 1: # Sunday or Monday or Tuesday
|
57 |
+
start_date = datetime(now.year, now.month, now.day, 0, 0) - timedelta(days=numOfDay+5)
|
58 |
+
end_date = datetime(now.year, now.month, now.day, 0, 0)
|
59 |
+
else: # Others
|
60 |
+
start_date = datetime(now.year, now.month, now.day, 0, 0) - timedelta(days=numOfDay+3)
|
61 |
+
end_date = datetime(now.year, now.month, now.day, 0, 0)
|
62 |
+
|
63 |
+
|
64 |
+
# Check if it is included in the Nasdaq_100
|
65 |
+
for ticker in tickers:
|
66 |
+
ticker = ticker.upper()
|
67 |
+
if ticker not in nasdaq100_symbols:
|
68 |
+
print("Nasdaq 100 ์์ ํฌํจ๋์ง ์์ต๋๋ค ")
|
69 |
+
else:
|
70 |
+
print("[ {} Finance Data ]".format(ticker))
|
71 |
+
ticker_yf = yf.Tickers(ticker)
|
72 |
+
|
73 |
+
|
74 |
+
abc = ticker_yf.tickers[ticker].history(start=start_date, end=end_date, period='max')
|
75 |
+
|
76 |
+
# abc = ticker_yf.tickers[ticker].history(period='max', interval='1m')
|
77 |
+
# abc = ticker_yf.tickers[ticker].history(start=start_date, end=end_date, interval='1m')
|
78 |
+
# abc = ticker_yf.tickers[ticker].history(start=start_date, end=end_date)
|
79 |
+
|
80 |
+
# print(abc)
|
81 |
+
# from IPython import embed; embed()
|
82 |
+
output.append(abc)
|
83 |
+
|
84 |
+
# print("Output : ", output)
|
85 |
+
return output
|
86 |
+
|
87 |
+
|
88 |
+
|
89 |
+
|
90 |
+
|
91 |
+
|
92 |
+
|
93 |
+
|
94 |
+
|
95 |
+
|
96 |
+
|
97 |
+
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
|
106 |
+
|
107 |
+
|
108 |
+
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
|
117 |
+
|
118 |
+
|
119 |
+
if __name__ == '__main__':
|
120 |
+
print(get_list())
|
121 |
+
|
122 |
+
start_date = datetime(2021,1,1)
|
123 |
+
end_date = datetime(2023,2,3)
|
124 |
+
|
125 |
+
get_data(['meta'], numOfDay=2)
|
126 |
+
get_data(tickers=nasdaq100_symbols, numOfDay=2)
|
dataset_creation/nasdaq_url.tsv
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ticker url
|
2 |
+
ATVI https://www.marketscreener.com/quote/stock/ACTIVISION-BLIZZARD-INC-3780631/news-quality/
|
3 |
+
ADBE https://www.marketscreener.com/quote/stock/ADOBE-INC-4844/news-quality/
|
4 |
+
ADP https://www.marketscreener.com/quote/stock/AUTOMATIC-DATA-PROCESSING-11713/news-quality/
|
5 |
+
ABNB https://www.marketscreener.com/quote/stock/AIRBNB-INC-116310849/news-quality/
|
6 |
+
ALGN https://www.marketscreener.com/quote/stock/ALIGN-TECHNOLOGY-INC-8316/news-quality/
|
7 |
+
GOOGL https://www.marketscreener.com/quote/stock/ALPHABET-INC-24203373/news-quality/
|
8 |
+
GOOG https://www.marketscreener.com/quote/stock/ALPHABET-INC-24203385/news-quality/
|
9 |
+
AMZN https://www.marketscreener.com/quote/stock/AMAZON-COM-INC-12864605/news-quality/
|
10 |
+
AMD https://www.marketscreener.com/quote/stock/ADVANCED-MICRO-DEVICES-I-19475876/news-quality/
|
11 |
+
AEP https://www.marketscreener.com/quote/stock/AMERICAN-ELECTRIC-POWER-C-11546/news-quality/
|
12 |
+
AMGN https://www.marketscreener.com/quote/stock/AMGEN-INC-4847/news-quality/
|
13 |
+
ADI https://www.marketscreener.com/quote/stock/ANALOG-DEVICES-INC-10345717/news-quality/
|
14 |
+
ANSS https://www.marketscreener.com/quote/stock/ANSYS-INC-40311135/news-quality/
|
15 |
+
AAPL https://www.marketscreener.com/quote/stock/APPLE-INC-4849/news-quality/
|
16 |
+
AMAT https://www.marketscreener.com/quote/stock/APPLIED-MATERIALS-INC-4850/news-quality/
|
17 |
+
ASML https://www.marketscreener.com/quote/stock/ASML-HOLDING-N-V-12012067/news-quality/
|
18 |
+
AZN https://www.marketscreener.com/quote/stock/ASTRAZENECA-PLC-11745/news-quality/
|
19 |
+
TEAM
|
20 |
+
ADSK https://www.marketscreener.com/quote/stock/AUTODESK-INC-40246776/news-quality/
|
21 |
+
BKR
|
22 |
+
BIIB https://www.marketscreener.com/quote/stock/BIOGEN-INC-4853/news-quality/
|
23 |
+
BKNG https://www.marketscreener.com/quote/stock/BOOKING-HOLDINGS-INC-41613106/news-quality/
|
24 |
+
AVGO https://www.marketscreener.com/quote/stock/BROADCOM-INC-42668543/news-quality/
|
25 |
+
CDNS https://www.marketscreener.com/quote/stock/CADENCE-DESIGN-SYSTEMS-I-8724/news-quality/
|
26 |
+
CHTR https://www.marketscreener.com/quote/stock/CHARTER-COMMUNICATIONS-I-27738754/news-quality/
|
27 |
+
CTAS https://www.marketscreener.com/quote/stock/CINTAS-CORPORATION-4861/news-quality/
|
28 |
+
CSCO https://www.marketscreener.com/quote/stock/CISCO-SYSTEMS-INC-4862/news-quality/
|
29 |
+
CTSH https://www.marketscreener.com/quote/stock/COGNIZANT-TECHNOLOGY-SOLU-23219296/news-quality/
|
30 |
+
CMCSA https://www.marketscreener.com/quote/stock/COMCAST-CORPORATION-4864/news-quality/
|
31 |
+
CEG https://www.marketscreener.com/quote/stock/CONSTELLATION-ENERGY-CORP-131860757/news-quality/
|
32 |
+
CPRT https://www.marketscreener.com/quote/stock/COPART-INC-8879/news-quality/
|
33 |
+
CSGP
|
34 |
+
COST https://www.marketscreener.com/quote/stock/COSTCO-WHOLESALE-CORPORAT-4866/news-quality/
|
35 |
+
CRWD https://www.marketscreener.com/quote/stock/CROWDSTRIKE-HOLDINGS-INC-59783691/news-quality/
|
36 |
+
CSX https://www.marketscreener.com/quote/stock/CSX-CORPORATION-25500636/news-quality/
|
37 |
+
DDOG https://www.marketscreener.com/quote/stock/DATADOG-INC-65956839/news-quality/
|
38 |
+
DXCM https://www.marketscreener.com/quote/stock/DEXCOM-INC-9115/news-quality/
|
39 |
+
FANG
|
40 |
+
DLTR https://www.marketscreener.com/quote/stock/DOLLAR-TREE-INC-4868/news-quality/
|
41 |
+
EBAY https://www.marketscreener.com/quote/stock/EBAY-INC-4869/news-quality/
|
42 |
+
EA https://www.marketscreener.com/quote/stock/ELECTRONIC-ARTS-INC-9664624/news-quality/
|
43 |
+
ENPH
|
44 |
+
EXC https://www.marketscreener.com/quote/stock/EXELON-CORPORATION-13963/news-quality/
|
45 |
+
FAST https://www.marketscreener.com/quote/stock/FASTENAL-COMPANY-4901/news-quality/
|
46 |
+
FISV https://www.marketscreener.com/quote/stock/FISERV-INC-4873/news-quality/
|
47 |
+
FTNT https://www.marketscreener.com/quote/stock/FORTINET-INC-60103137/news-quality/
|
48 |
+
GILD https://www.marketscreener.com/quote/stock/GILEAD-SCIENCES-INC-4876/news-quality/
|
49 |
+
GFS
|
50 |
+
HON https://www.marketscreener.com/quote/stock/HONEYWELL-INTERNATIONAL-I-4827/news-quality/
|
51 |
+
IDXX https://www.marketscreener.com/quote/stock/IDEXX-LABORATORIES-INC-9641/news-quality/
|
52 |
+
ILMN https://www.marketscreener.com/quote/stock/ILLUMINA-INC-9659/news-quality/
|
53 |
+
INTC https://www.marketscreener.com/quote/stock/INTEL-CORPORATION-4829/news-quality/
|
54 |
+
INTU https://www.marketscreener.com/quote/stock/INTUIT-INC-23277275/news-quality/
|
55 |
+
ISRG https://www.marketscreener.com/quote/stock/INTUITIVE-SURGICAL-INC-9740/news-quality/
|
56 |
+
JD https://www.marketscreener.com/quote/stock/JD-COM-INC-16538052/news-quality/
|
57 |
+
KDP https://www.marketscreener.com/quote/stock/KEURIG-DR-PEPPER-INC-44644712/news-quality/
|
58 |
+
KLAC https://www.marketscreener.com/quote/stock/KLA-CORPORATION-40328827/news-quality/
|
59 |
+
KHC https://www.marketscreener.com/quote/stock/KRAFT-HEINZ-22816979/news-quality/
|
60 |
+
LRCX https://www.marketscreener.com/quote/stock/LAM-RESEARCH-CORPORATION-4877/news-quality/
|
61 |
+
LCID https://www.marketscreener.com/quote/stock/LUCID-GROUP-INC-112589428/news-quality/
|
62 |
+
LULU https://www.marketscreener.com/quote/stock/LULULEMON-ATHLETICA-INC-40449575/news-quality/
|
63 |
+
MAR https://www.marketscreener.com/quote/stock/MARRIOTT-INTERNATIONAL-I-14633490/news-quality/
|
64 |
+
MRVL https://www.marketscreener.com/quote/stock/MARVELL-TECHNOLOGY-GROUP-4934/news-quality/
|
65 |
+
MELI https://www.marketscreener.com/quote/stock/MERCADOLIBRE-INC-58469/news-quality/
|
66 |
+
META https://www.marketscreener.com/quote/stock/META-PLATFORMS-INC-10547141/news-quality/
|
67 |
+
MCHP https://www.marketscreener.com/quote/stock/MICROCHIP-TECHNOLOGY-INC-4887/news-quality/
|
68 |
+
MU https://www.marketscreener.com/quote/stock/MICRON-TECHNOLOGY-INC-13639/news-quality/
|
69 |
+
MSFT https://www.marketscreener.com/quote/stock/MICROSOFT-CORPORATION-4835/news-quality/
|
70 |
+
MRNA https://www.marketscreener.com/quote/stock/MODERNA-INC-47437573/news-quality/
|
71 |
+
MDLZ https://www.marketscreener.com/quote/stock/MONDELEZ-INTERNATIONAL-I-11499018/news-quality/
|
72 |
+
MNST https://www.marketscreener.com/quote/stock/MONSTER-BEVERAGE-CORPORAT-22497283/news-quality/
|
73 |
+
NFLX https://www.marketscreener.com/quote/stock/NETFLIX-INC-44292425/news-quality/
|
74 |
+
NVDA https://www.marketscreener.com/quote/stock/NVIDIA-CORPORATION-57355629/news-quality/
|
75 |
+
NXPI https://www.marketscreener.com/quote/stock/NXP-SEMICONDUCTORS-N-V-6467512/news-quality/
|
76 |
+
ORLY https://www.marketscreener.com/quote/stock/O-REILLY-AUTOMOTIVE-INC-10363/news-quality/
|
77 |
+
ODFL https://www.marketscreener.com/quote/stock/OLD-DOMINION-FREIGHT-LINE-10317/news-quality/
|
78 |
+
PCAR https://www.marketscreener.com/quote/stock/PACCAR-INC-4893/news-quality/
|
79 |
+
PANW https://www.marketscreener.com/quote/stock/PALO-ALTO-NETWORKS-INC-11067980/news-quality/
|
80 |
+
PAYX https://www.marketscreener.com/quote/stock/PAYCHEX-INC-4894/news-quality/
|
81 |
+
PYPL https://www.marketscreener.com/quote/stock/PAYPAL-HOLDINGS-INC-23377703/news-quality/
|
82 |
+
PEP https://www.marketscreener.com/quote/stock/PEPSICO-INC-39085159/news-quality/
|
83 |
+
PDD https://www.marketscreener.com/quote/stock/PINDUODUO-INC-45049866/news-quality/
|
84 |
+
QCOM https://www.marketscreener.com/quote/stock/QUALCOMM-INC-4897/news-quality/
|
85 |
+
REGN https://www.marketscreener.com/quote/stock/REGENERON-PHARMACEUTICALS-10649/news-quality/
|
86 |
+
RIVN
|
87 |
+
ROST https://www.marketscreener.com/quote/stock/ROSS-STORES-INC-4927/news-quality/
|
88 |
+
SGEN https://www.marketscreener.com/quote/stock/SEAGEN-INC-10808/news-quality/
|
89 |
+
SIRI https://www.marketscreener.com/quote/stock/SIRIUS-XM-HOLDINGS-INC-14962202/news-quality/
|
90 |
+
SBUX https://www.marketscreener.com/quote/stock/STARBUCKS-CORPORATION-4905/news-quality/
|
91 |
+
SNPS https://www.marketscreener.com/quote/stock/SYNOPSYS-INC-4908/news-quality/
|
92 |
+
TMUS https://www.marketscreener.com/quote/stock/T-MOBILE-US-24717887/news-quality/
|
93 |
+
TSLA https://www.marketscreener.com/quote/stock/TESLA-INC-6344549/news-quality/
|
94 |
+
TXN https://www.marketscreener.com/quote/stock/TEXAS-INSTRUMENTS-9730651/news-quality/
|
95 |
+
VRSK https://www.marketscreener.com/quote/stock/VERISK-ANALYTICS-INC-5628469/news-quality/
|
96 |
+
VRTX https://www.marketscreener.com/quote/stock/VERTEX-PHARMACEUTICALS-11321/news-quality/
|
97 |
+
WBA https://www.marketscreener.com/quote/stock/WALGREENS-BOOTS-ALLIANCE-19356230/news-quality/
|
98 |
+
WBD
|
99 |
+
WDAY https://www.marketscreener.com/quote/stock/WORKDAY-INC-37866670/news-quality/
|
100 |
+
XEL https://www.marketscreener.com/quote/stock/XCEL-ENERGY-39742648/news-quality/
|
101 |
+
ZM https://www.marketscreener.com/quote/stock/ZOOM-VIDEO-COMMUNICATIONS-57086220/news-quality/
|
102 |
+
ZS https://www.marketscreener.com/quote/stock/ZSCALER-INC-42379366/news-quality/
|
103 |
+
BIDU https://www.marketscreener.com/quote/stock/BAIDU-INC-8563/news-quality/
|
104 |
+
MTCH https://www.marketscreener.com/quote/stock/MATCH-GROUP-INC-24949016/news-quality/
|
105 |
+
VRSN https://www.marketscreener.com/quote/stock/VERISIGN-INC-4912/news-quality/
|
106 |
+
NTES https://www.marketscreener.com/quote/stock/NETEASE-INC-10259/news-quality/
|
107 |
+
SWKS https://www.marketscreener.com/quote/stock/SKYWORKS-SOLUTIONS-INC-11014/news-quality/
|
108 |
+
SPLK https://www.marketscreener.com/quote/stock/SPLUNK-INC-10454129/news-quality/
|
109 |
+
OKTA https://www.marketscreener.com/quote/stock/OKTA-INC-34515216/news-quality/
|
110 |
+
DOCU https://www.marketscreener.com/quote/stock/DOCUSIGN-INC-43180302/news-quality/
|
dataset_creation/save_news_url.tsv
ADDED
The diff for this file is too large to render.
See raw diff
|
|
dataset_creation/textLength.xlsx
ADDED
Binary file (92.6 kB). View file
|
|
dataset_creation/tickers_numAndAvg.xlsx
ADDED
Binary file (7.54 kB). View file
|
|
modules/inference.py
ADDED
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import torch
|
3 |
+
import numpy as np
|
4 |
+
|
5 |
+
import spacy
|
6 |
+
from spacy.tokens import Span
|
7 |
+
from spacy.attrs import ENT_IOB, ENT_TYPE
|
8 |
+
from spacy import displacy
|
9 |
+
|
10 |
+
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
11 |
+
from transformers import pipeline
|
12 |
+
|
13 |
+
|
14 |
+
if torch.cuda.is_available():
|
15 |
+
device = 'cuda'
|
16 |
+
elif torch.backends.mps.is_available():
|
17 |
+
device = 'mps'
|
18 |
+
else:
|
19 |
+
device = 'cpu'
|
20 |
+
print(f"inference.py -> DEVICE : {device}")
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
summarizer = pipeline(
|
25 |
+
"summarization",
|
26 |
+
"pszemraj/long-t5-tglobal-base-16384-book-summary",
|
27 |
+
device=0 if torch.cuda.is_available() else -1,
|
28 |
+
)
|
29 |
+
long_text = "Here is a lot of text I don't want to read. Replace me"
|
30 |
+
|
31 |
+
|
32 |
+
|
33 |
+
# [ Practice ]
|
34 |
+
# result = summarizer(long_text)
|
35 |
+
# print(result[0]["summary_text"])
|
36 |
+
|
37 |
+
|
38 |
+
|
39 |
+
tokenizer = AutoTokenizer.from_pretrained("allenai/tk-instruct-base-def-pos")
|
40 |
+
model = AutoModelForSeq2SeqLM.from_pretrained("allenai/tk-instruct-base-def-pos")
|
41 |
+
# k = pipeline("text2text-generation", model="allenai/tk-instruct-3b-def")
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
# [ Practice ]
|
46 |
+
# input_ids = tokenizer.encode("Definition: return the currency of the given country. Now complete the following example - Input: India. Output:",
|
47 |
+
# return_tensors="pt")
|
48 |
+
# output = model.generate(input_ids, max_length=10)
|
49 |
+
# output = tokenizer.decode(output[0], skip_special_tokens=True) # model should output 'Indian Rupee'
|
50 |
+
# print(output)
|
51 |
+
|
52 |
+
# input_ids = tokenizer.encode("Definition: negate the following sentence. Input: John went to school. Output:",
|
53 |
+
# return_tensors="pt")
|
54 |
+
# output = model.generate(input_ids, max_length=10)
|
55 |
+
# output = tokenizer.decode(output[0], skip_special_tokens=True) # model should output 'John did not go to shool.'
|
56 |
+
# print(output)
|
57 |
+
|
58 |
+
|
59 |
+
|
60 |
+
# text = "Alphabet's results also missed forecasts on revenue and earnings per share, as advertising declined year-over-year. The numbers come after the company laid off about 12,000 employees in January, a move CEO Sundar Pichai blamed on Alphabet overhiring during the pandemic boom. \
|
61 |
+
# Q: Why did Alphabet's stock go down?"
|
62 |
+
# input_ids = tokenizer.encode(text, return_tensors="pt")
|
63 |
+
# output = model.generate(input_ids, max_length=10)
|
64 |
+
# output = tokenizer.decode(output[0], skip_special_tokens=True) # model should output 'John did not go to shool.'
|
65 |
+
# print(output)
|
66 |
+
|
67 |
+
|
68 |
+
|
69 |
+
def Tk_instruct(text, questions):
|
70 |
+
# Summary ํ๋์ง ์ํ๋์ง
|
71 |
+
summarized = False
|
72 |
+
summarized_data = ""
|
73 |
+
|
74 |
+
text = text + "\n\nQ: " + questions
|
75 |
+
print("Model's input : ", text)
|
76 |
+
|
77 |
+
|
78 |
+
if len(text) >= 512:
|
79 |
+
print(f"===================== Apply Summarization : length = {len(text)} =====================")
|
80 |
+
text = summarizer(text)[0]["summary_text"]
|
81 |
+
print(f"===================== Summary text : {text} =====================")
|
82 |
+
summarized = True
|
83 |
+
summarized_data = text
|
84 |
+
|
85 |
+
|
86 |
+
|
87 |
+
input_ids = tokenizer.encode(text, return_tensors="pt")
|
88 |
+
output = model.generate(input_ids, max_length=10)
|
89 |
+
output = tokenizer.decode(output[0], skip_special_tokens=True)
|
90 |
+
|
91 |
+
|
92 |
+
if summarized:
|
93 |
+
output = "Summary News : " + summarized_data + "\n\n" + "Answer : " + output
|
94 |
+
|
95 |
+
|
96 |
+
return output
|
97 |
+
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
|
106 |
+
|
107 |
+
|
108 |
+
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
|
117 |
+
|
118 |
+
|
119 |
+
|
120 |
+
|
121 |
+
|
122 |
+
|
123 |
+
|
124 |
+
|
125 |
+
|
126 |
+
|
127 |
+
|
128 |
+
|
129 |
+
|
130 |
+
|
131 |
+
|
132 |
+
|
133 |
+
# NER ์ฐ์ต
|
134 |
+
def practice1():
|
135 |
+
print(f"======================={ 1. }=======================")
|
136 |
+
nlp = spacy.load("en_core_web_sm")
|
137 |
+
doc = nlp("Apple is looking at buying U.K. startup for $1 billion")
|
138 |
+
|
139 |
+
print(doc)
|
140 |
+
print(doc.ents)
|
141 |
+
|
142 |
+
for ent in doc.ents:
|
143 |
+
print(ent.text, ent.start_char, ent.end_char, ent.label_)
|
144 |
+
|
145 |
+
|
146 |
+
title = "2. Accessing entity annotations and labels"
|
147 |
+
print(f"======================={ title }=======================")
|
148 |
+
nlp = spacy.load("en_core_web_sm")
|
149 |
+
doc = nlp("San Francisco considers banning sidewalk delivery robots")
|
150 |
+
|
151 |
+
# document level
|
152 |
+
ents = [(e.text, e.start_char, e.end_char, e.label_) for e in doc.ents]
|
153 |
+
print(ents)
|
154 |
+
|
155 |
+
# I - Token is inside an entity.
|
156 |
+
# O - Token is outside an entity.
|
157 |
+
# B - Token is the beginning of an entity.
|
158 |
+
|
159 |
+
# token level
|
160 |
+
ent_san = [doc[0].text, doc[0].ent_iob_, doc[0].ent_type_]
|
161 |
+
ent_francisco = [doc[1].text, doc[1].ent_iob_, doc[1].ent_type_]
|
162 |
+
print(ent_san)
|
163 |
+
print(ent_francisco)
|
164 |
+
|
165 |
+
|
166 |
+
|
167 |
+
title = "3. Setting entity annotations"
|
168 |
+
print(f"======================={ title }=======================")
|
169 |
+
nlp = spacy.load("en_core_web_sm")
|
170 |
+
doc = nlp("fb is hiring a new vice president of global policy")
|
171 |
+
ents = [(e.text, e.start_char, e.end_char, e.label_) for e in doc.ents]
|
172 |
+
print('Before', ents)
|
173 |
+
# The model didn't recognize "fb" as an entity :(
|
174 |
+
|
175 |
+
# Create a span for the new entity
|
176 |
+
fb_ent = Span(doc, 0, 1, label="ORG"); print(fb_ent)
|
177 |
+
orig_ents = list(doc.ents)
|
178 |
+
|
179 |
+
# Option 1: Modify the provided entity spans, leaving the rest unmodified
|
180 |
+
doc.set_ents([fb_ent], default="unmodified")
|
181 |
+
|
182 |
+
# Option 2: Assign a complete list of ents to doc.ents
|
183 |
+
doc.ents = orig_ents + [fb_ent]
|
184 |
+
|
185 |
+
ents = [(e.text, e.start, e.end, e.label_) for e in doc.ents]
|
186 |
+
print('After', ents)
|
187 |
+
# [('fb', 0, 1, 'ORG')]
|
188 |
+
|
189 |
+
|
190 |
+
|
191 |
+
title = "4. Setting entity annotations from array"
|
192 |
+
print(f"======================={ title }=======================")
|
193 |
+
nlp = spacy.load("en_core_web_sm")
|
194 |
+
doc = nlp.make_doc("London is a big city in the United Kingdom.")
|
195 |
+
print("Before", doc.ents) # []
|
196 |
+
|
197 |
+
header = [ENT_IOB, ENT_TYPE]; print(header)
|
198 |
+
attr_array = np.zeros((len(doc), len(header)), dtype="uint64"); print(attr_array)
|
199 |
+
attr_array[0, 0] = 3 # B
|
200 |
+
attr_array[0, 1] = doc.vocab.strings["GPE"]
|
201 |
+
doc.from_array(header, attr_array); print(attr_array)
|
202 |
+
print("After", doc.ents) # [London]
|
203 |
+
|
204 |
+
|
205 |
+
|
206 |
+
title = "5. Visualizing named entities"
|
207 |
+
print(f"======================={ title }=======================")
|
208 |
+
text = "When Sebastian Thrun started working on self-driving cars at Google in 2007, few people outside of the company took him seriously."
|
209 |
+
|
210 |
+
nlp = spacy.load("en_core_web_sm")
|
211 |
+
doc = nlp(text)
|
212 |
+
# displacy.serve(doc, style="ent")
|
213 |
+
displacy.serve(doc, port=3, style="ent")
|
214 |
+
|
215 |
+
|
216 |
+
|
217 |
+
|
218 |
+
|
219 |
+
|
220 |
+
|
221 |
+
|
222 |
+
|
223 |
+
|
224 |
+
|
225 |
+
|
226 |
+
|
227 |
+
############################################################################
|
228 |
+
|
229 |
+
# news_analysis.html + ner.html => news.html ๋ง๋๋ ์ฐ์ต
|
230 |
+
|
231 |
+
|
232 |
+
from flask import Flask, jsonify, request, render_template
|
233 |
+
from bs4 import BeautifulSoup
|
234 |
+
app = Flask(__name__)
|
235 |
+
|
236 |
+
@app.route('/')
|
237 |
+
def practice2():
|
238 |
+
title = "1. Rendering HTML"
|
239 |
+
print(f"======================={ title }=======================")
|
240 |
+
nlp = spacy.load("en_core_web_sm")
|
241 |
+
doc1 = nlp("This is a sentence.")
|
242 |
+
doc2 = nlp("This is another sentence.")
|
243 |
+
ner_html = displacy.render([doc1, doc2], style="dep", page=True)
|
244 |
+
|
245 |
+
print("ner_html : ", ner_html)
|
246 |
+
|
247 |
+
|
248 |
+
# NER html code
|
249 |
+
soup = BeautifulSoup(ner_html, 'html.parser')
|
250 |
+
ner_figure_list = soup.select('figure')
|
251 |
+
ner_html = ""
|
252 |
+
for i in range(len(ner_figure_list)):
|
253 |
+
ner_html = ner_html + str(ner_figure_list[i])
|
254 |
+
|
255 |
+
|
256 |
+
|
257 |
+
f = open("./templates/news_analysis.html", 'r')
|
258 |
+
f2 = open("./modules/templates/example.html", 'w')# read and write
|
259 |
+
|
260 |
+
html = f.read()
|
261 |
+
idx = html.find("ner-box") + 9 # NER html ์ฝ์
๋๋ ๋ถ๋ถ
|
262 |
+
|
263 |
+
html = html[:idx] + ner_html + html[idx:]
|
264 |
+
|
265 |
+
f2.seek(0) # openํ๋ฉด
|
266 |
+
f2.write(html)
|
267 |
+
# f2.seek(0)
|
268 |
+
# print(f2.read())
|
269 |
+
|
270 |
+
# from IPython import embed; embed()
|
271 |
+
|
272 |
+
# f2.write(f.read())
|
273 |
+
# f2.seek(0) # ๊ฐ์ฅ ์์ผ๋ก
|
274 |
+
|
275 |
+
return render_template("example.html")
|
276 |
+
|
277 |
+
|
278 |
+
|
279 |
+
|
280 |
+
|
281 |
+
|
282 |
+
|
283 |
+
|
284 |
+
if __name__ == "__main__":
|
285 |
+
# app.run(host='0.0.0.0', port='777')
|
286 |
+
|
287 |
+
practice1()
|
modules/templates/example.html
ADDED
File without changes
|
requirements.txt
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
pandas
|
2 |
+
beautifulsoup4
|
3 |
+
Flask
|
4 |
+
torch
|
5 |
+
transformers
|
6 |
+
accelerate
|
7 |
+
bitsandbytes
|
8 |
+
spacy
|
9 |
+
yfinance
|
10 |
+
https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.5.0/en_core_web_sm-3.5.0-py3-none-any.whl
|
11 |
+
https://github.com/explosion/spacy-models/releases/download/en_core_web_md-3.5.0/en_core_web_md-3.5.0-py3-none-any.whl
|
static/css/chartStyle.css
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* id : "#" */
|
2 |
+
.myChart-container {
|
3 |
+
/* ์์ฑ์ ์์ ๋๋น๋ฅผ ์ง์ */
|
4 |
+
width: 60vw;
|
5 |
+
|
6 |
+
/* ์์ฑ์ ์์์ ๋์ด๋ฅผ ์ง์ */
|
7 |
+
height: 30vh;
|
8 |
+
|
9 |
+
/*
|
10 |
+
[ margin ํ๊ทธ ]
|
11 |
+
margin-top (์๋จ ์ฌ๋ฐฑ)
|
12 |
+
margin-right (์ค๋ฅธ์ชฝ ์ฌ๋ฐฑ)
|
13 |
+
margin-bottom (์๋ ์ฌ๋ฐฑ)
|
14 |
+
margin-left (์ผ์ชฝ ์ฌ๋ฐฑ)
|
15 |
+
|
16 |
+
์ง์ ๊ฐ์ px, cm, %๋ก ์ง์ ํ ์ ์๋ค.
|
17 |
+
์์๊ฐ๋ ์ง์ ๊ฐ๋ฅ(ex. -10px)
|
18 |
+
|
19 |
+
* 4๋ฉด ํ๊บผ๋ฒ์ margin ์ง์ ํ๊ธฐ
|
20 |
+
ex) margin: 5px 7px 3px 0px;
|
21 |
+
(์, ์ค๋ฅธ์ชฝ, ์๋, ์ผ์ชฝ)
|
22 |
+
* 4๋ฉด์ด ๋ชจ๋ ๊ฐ์ ๋ margin ์ง์ ํ๊ธฐ
|
23 |
+
ex) margin: 5px;
|
24 |
+
* ์, ์ค๋ฅธ์ชฝ&์ผ์ชฝ, ์๋ margin ์ง์ ํ๊ธฐ
|
25 |
+
ex) margin: 5px 10px 0px;
|
26 |
+
* ์&์๋, ์ค๋ฅธ์ชฝ&์ผ์ชฝ margin ์ง์ ํ๊ธฐ
|
27 |
+
ex) margin: 5px 10px;
|
28 |
+
* margin ์๋ ์ง์ ํ๊ธฐ
|
29 |
+
ex) margin: auto 0;
|
30 |
+
(์์๋ ๊ฐ์ด ์๋, ์ข์ฐ๊ฐ 0px)
|
31 |
+
ex) margin-left: auto;
|
32 |
+
|
33 |
+
*/
|
34 |
+
margin: 40px auto;
|
35 |
+
padding-bottom: 13%;
|
36 |
+
}
|
37 |
+
|
38 |
+
|
39 |
+
.table {
|
40 |
+
/*
|
41 |
+
[ align-items ํ๊ทธ ]
|
42 |
+
flex-box ์์์ ์์ง ๋ฐฉํฅ ์ ๋ ฌ ๋ฐฉ์์ ์ค์
|
43 |
+
ex. flex-start, flex-end, center
|
44 |
+
*/
|
45 |
+
align-items: center;
|
46 |
+
|
47 |
+
/*
|
48 |
+
[ justify-content ํ๊ทธ ]
|
49 |
+
flex-box ์์์ ์ํ ๋ฐฉํฅ ์ ๋ ฌ ๋ฐฉ์์ ์ค์
|
50 |
+
ex. flex-start, flex-end, center
|
51 |
+
*/
|
52 |
+
justify-content: center;
|
53 |
+
|
54 |
+
|
55 |
+
/*
|
56 |
+
[ margin ํ๊ทธ ]
|
57 |
+
margin-top (์๋จ ์ฌ๋ฐฑ)
|
58 |
+
margin-right (์ค๋ฅธ์ชฝ ์ฌ๋ฐฑ)
|
59 |
+
margin-bottom (์๋ ์ฌ๋ฐฑ)
|
60 |
+
margin-left (์ผ์ชฝ ์ฌ๋ฐฑ)
|
61 |
+
|
62 |
+
์ง์ ๊ฐ์ px, cm, %๋ก ์ง์ ํ ์ ์๋ค.
|
63 |
+
์์๊ฐ๋ ์ง์ ๊ฐ๋ฅ(ex. -10px)
|
64 |
+
|
65 |
+
* 4๋ฉด ํ๊บผ๋ฒ์ margin ์ง์ ํ๊ธฐ
|
66 |
+
ex) margin: 5px 7px 3px 0px;
|
67 |
+
(์, ์ค๋ฅธ์ชฝ, ์๋, ์ผ์ชฝ)
|
68 |
+
* 4๋ฉด์ด ๋ชจ๋ ๊ฐ์ ๋ margin ์ง์ ํ๊ธฐ
|
69 |
+
ex) margin: 5px;
|
70 |
+
* ์, ์ค๋ฅธ์ชฝ&์ผ์ชฝ, ์๋ margin ์ง์ ํ๊ธฐ
|
71 |
+
ex) margin: 5px 10px 0px;
|
72 |
+
* ์&์๋, ์ค๋ฅธ์ชฝ&์ผ์ชฝ margin ์ง์ ํ๊ธฐ
|
73 |
+
ex) margin: 5px 10px;
|
74 |
+
* margin ์๋ ์ง์ ํ๊ธฐ
|
75 |
+
ex) margin: auto 0;
|
76 |
+
(์์๋ ๊ฐ์ด ์๋, ์ข์ฐ๊ฐ 0px)
|
77 |
+
ex) margin-left: auto;
|
78 |
+
|
79 |
+
*/
|
80 |
+
margin: 20px auto;
|
81 |
+
|
82 |
+
|
83 |
+
/*
|
84 |
+
[ text-align ํ๊ทธ ]
|
85 |
+
ํ
์คํธ์ ์ ๋ ฌ ๋ฐฉํฅ์ ์ค์
|
86 |
+
|
87 |
+
left: ์ผ์ชฝ ์ ๋ ฌ
|
88 |
+
right: ์ค๋ฅธ์ชฝ ์ ๋ ฌ
|
89 |
+
center: ์ค์ ์ ๋ ฌ
|
90 |
+
justify: ์์ชฝ ์ ๋ ฌ (์๋ ์ค๋ฐ๊ฟ์ ์ค๋ฅธ์ชฝ ๊ฒฝ๊ณ์ ๋ถ๋ถ ์ ๋ฆฌ)
|
91 |
+
*/
|
92 |
+
text-align: center;
|
93 |
+
|
94 |
+
|
95 |
+
padding-top: 50px;
|
96 |
+
}
|
97 |
+
|
98 |
+
|
99 |
+
.table .title-width {
|
100 |
+
width: 10px;
|
101 |
+
text-align: center;
|
102 |
+
}
|
103 |
+
|
104 |
+
|
105 |
+
.table .table-title {
|
106 |
+
font-size: 50px;
|
107 |
+
}
|
108 |
+
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
|
113 |
+
|
114 |
+
|
115 |
+
|
116 |
+
|
117 |
+
|
118 |
+
|
119 |
+
|
120 |
+
/* h1, h2 ํ๊ทธ ๋ถ๋ถ */
|
121 |
+
.gohome, .goticker {
|
122 |
+
text-decoration: none;
|
123 |
+
}
|
124 |
+
|
125 |
+
|
126 |
+
|
127 |
+
.table .news-table .news.diff.up {
|
128 |
+
color: #ed2a61;
|
129 |
+
}
|
130 |
+
.table .news-table .news.diff.down {
|
131 |
+
color: #3c6ffa;
|
132 |
+
}
|
static/css/news.css
ADDED
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* ner ๊ด๋ จ */
|
2 |
+
|
3 |
+
.ner-box {
|
4 |
+
width: calc(92%); /* ์์ฑ์ ์์ ๋๋น๋ฅผ ์ง์ */
|
5 |
+
height: 500px; /* ์์ฑ์ ์์์ ๋์ด๋ฅผ ์ง์ */
|
6 |
+
|
7 |
+
|
8 |
+
/*
|
9 |
+
[ align-items ํ๊ทธ ]
|
10 |
+
flex-box ์์์ ์์ง ๋ฐฉํฅ ์ ๋ ฌ ๋ฐฉ์์ ์ค์
|
11 |
+
ex. flex-start, flex-end, center
|
12 |
+
*/
|
13 |
+
align-items: center;
|
14 |
+
|
15 |
+
/*
|
16 |
+
[ justify-content ํ๊ทธ ]
|
17 |
+
flex-box ์์์ ์ํ ๋ฐฉํฅ ์ ๋ ฌ ๋ฐฉ์์ ์ค์
|
18 |
+
ex. flex-start, flex-end, center
|
19 |
+
*/
|
20 |
+
justify-content: center;
|
21 |
+
|
22 |
+
/*
|
23 |
+
[ text-align ํ๊ทธ ]
|
24 |
+
ํ
์คํธ์ ์ ๋ ฌ ๋ฐฉํฅ์ ์ค์
|
25 |
+
|
26 |
+
left: ์ผ์ชฝ ์ ๋ ฌ
|
27 |
+
right: ์ค๋ฅธ์ชฝ ์ ๋ ฌ
|
28 |
+
center: ์ค์ ์ ๋ ฌ
|
29 |
+
justify: ์์ชฝ ์ ๋ ฌ (์๋ ์ค๋ฐ๊ฟ์ ์ค๋ฅธ์ชฝ ๊ฒฝ๊ณ์ ๋ถ๋ถ ์ ๋ฆฌ)
|
30 |
+
*/
|
31 |
+
text-align: center;
|
32 |
+
|
33 |
+
|
34 |
+
/*
|
35 |
+
[ margin ํ๊ทธ ]
|
36 |
+
margin-top (์๋จ ์ฌ๋ฐฑ)
|
37 |
+
margin-right (์ค๋ฅธ์ชฝ ์ฌ๋ฐฑ)
|
38 |
+
margin-bottom (์๋ ์ฌ๋ฐฑ)
|
39 |
+
margin-left (์ผ์ชฝ ์ฌ๋ฐฑ)
|
40 |
+
|
41 |
+
์ง์ ๊ฐ์ px, cm, %๋ก ์ง์ ํ ์ ์๋ค.
|
42 |
+
์์๊ฐ๋ ์ง์ ๊ฐ๋ฅ(ex. -10px)
|
43 |
+
|
44 |
+
* 4๋ฉด ํ๊บผ๋ฒ์ margin ์ง์ ํ๊ธฐ
|
45 |
+
ex) margin: 5px 7px 3px 0px;
|
46 |
+
(์, ์ค๋ฅธ์ชฝ, ์๋, ์ผ์ชฝ)
|
47 |
+
* 4๋ฉด์ด ๋ชจ๋ ๊ฐ์ ๋ margin ์ง์ ํ๊ธฐ
|
48 |
+
ex) margin: 5px;
|
49 |
+
* ์, ์ค๋ฅธ์ชฝ&์ผ์ชฝ, ์๋ margin ์ง์ ํ๊ธฐ
|
50 |
+
ex) margin: 5px 10px 0px;
|
51 |
+
* ์&์๋, ์ค๋ฅธ์ชฝ&์ผ์ชฝ margin ์ง์ ํ๊ธฐ
|
52 |
+
ex) margin: 5px 10px;
|
53 |
+
* margin ์๋ ์ง์ ํ๊ธฐ
|
54 |
+
ex) margin: auto 0;
|
55 |
+
(์์๋ ๊ฐ์ด ์๋, ์ข์ฐ๊ฐ 0px)
|
56 |
+
ex) margin-left: auto;
|
57 |
+
|
58 |
+
*/
|
59 |
+
margin: 1rem;
|
60 |
+
|
61 |
+
|
62 |
+
min-height: 1.2rem;
|
63 |
+
border: 0.5px solid grey;
|
64 |
+
padding: 0.5rem 1rem;
|
65 |
+
}
|
66 |
+
|
67 |
+
|
68 |
+
|
69 |
+
/* NER label_ */
|
70 |
+
.entities .entity_person {
|
71 |
+
background-color: #aa9cfc;
|
72 |
+
}
|
73 |
+
|
74 |
+
.entities .entity_org {
|
75 |
+
background-color: #7aecec;
|
76 |
+
}
|
77 |
+
|
78 |
+
.entities .entity_fac {
|
79 |
+
background-color: #9cc9cc;
|
80 |
+
}
|
81 |
+
|
82 |
+
.entities .entity_gpe {
|
83 |
+
background-color: #feca74;
|
84 |
+
}
|
85 |
+
|
86 |
+
.entities .entity_product {
|
87 |
+
background-color: #bfeeb7;
|
88 |
+
}
|
89 |
+
|
90 |
+
.entities .none {
|
91 |
+
background-color: transparent;
|
92 |
+
}
|
93 |
+
|
94 |
+
/* ๋ง์ฐ์ค ์ฌ๋ ธ์ ๋, ๋ณด์ด๊ฒ ํ๋ ๊ฒ */
|
95 |
+
.entities .show-label {
|
96 |
+
display: none;
|
97 |
+
}
|
98 |
+
|
99 |
+
.entities .entity_person:hover .show-label,
|
100 |
+
.entities .entity_org:hover .show-label,
|
101 |
+
.entities .entity_fac:hover .show-label,
|
102 |
+
.entities .entity_gpe:hover .show-label,
|
103 |
+
.entities .entity_product:hover .show-label {
|
104 |
+
display: block;
|
105 |
+
}
|
106 |
+
|
107 |
+
|
108 |
+
|
109 |
+
|
110 |
+
|
111 |
+
/* Model ๊ด๋ จ */
|
112 |
+
|
113 |
+
/* id : "#" */
|
114 |
+
#model {
|
115 |
+
text-align: center;
|
116 |
+
}
|
117 |
+
|
118 |
+
/* id : "#" */
|
119 |
+
#text-input {
|
120 |
+
width: calc(100% / 2); /* ์์ฑ์ ์์ ๋๋น */
|
121 |
+
height: 78px;
|
122 |
+
word-break: break-all;
|
123 |
+
}
|
124 |
+
|
125 |
+
|
126 |
+
.text-output {
|
127 |
+
width: calc(100% * (2/3)); /* ์์ฑ์ ์์ ๋๋น */
|
128 |
+
min-height: 10rem;
|
129 |
+
|
130 |
+
|
131 |
+
|
132 |
+
/*
|
133 |
+
[ margin ํ๊ทธ ]
|
134 |
+
margin-top (์๋จ ์ฌ๋ฐฑ)
|
135 |
+
margin-right (์ค๋ฅธ์ชฝ ์ฌ๋ฐฑ)
|
136 |
+
margin-bottom (์๋ ์ฌ๋ฐฑ)
|
137 |
+
margin-left (์ผ์ชฝ ์ฌ๋ฐฑ)
|
138 |
+
|
139 |
+
์ง์ ๊ฐ์ px, cm, %๋ก ์ง์ ํ ์ ์๋ค.
|
140 |
+
์์๊ฐ๋ ์ง์ ๊ฐ๋ฅ(ex. -10px)
|
141 |
+
|
142 |
+
* 4๋ฉด ํ๊บผ๋ฒ์ margin ์ง์ ํ๊ธฐ
|
143 |
+
ex) margin: 5px 7px 3px 0px;
|
144 |
+
(์, ์ค๋ฅธ์ชฝ, ์๋, ์ผ์ชฝ)
|
145 |
+
* 4๋ฉด์ด ๋ชจ๋ ๊ฐ์ ๋ margin ์ง์ ํ๊ธฐ
|
146 |
+
ex) margin: 5px;
|
147 |
+
* ์, ์ค๋ฅธ์ชฝ&์ผ์ชฝ, ์๋ margin ์ง์ ํ๊ธฐ
|
148 |
+
ex) margin: 5px 10px 0px;
|
149 |
+
* ์&์๋, ์ค๋ฅธ์ชฝ&์ผ์ชฝ margin ์ง์ ํ๊ธฐ
|
150 |
+
ex) margin: 5px 10px;
|
151 |
+
* margin ์๋ ์ง์ ํ๊ธฐ
|
152 |
+
ex) margin: auto 0;
|
153 |
+
(์์๋ ๊ฐ์ด ์๋, ์ข์ฐ๊ฐ 0px)
|
154 |
+
ex) margin-left: auto;
|
155 |
+
|
156 |
+
*/
|
157 |
+
margin: 20px auto;
|
158 |
+
|
159 |
+
/*
|
160 |
+
[ border ํ๊ทธ ]
|
161 |
+
ํด๋น ํ๊ทธ์ ํ
๋๋ฆฌ๋ฅผ ์ค์
|
162 |
+
width - style - color
|
163 |
+
border-width - border-style - border-color
|
164 |
+
|
165 |
+
border-width : ํ
๋๋ฆฌ์ ๋๊ป๋ก, ์ฃผ๋ก px ๋จ์๋ฅผ ์ฌ์ฉ
|
166 |
+
border-style : ํ
๋๋ฆฌ์ ์คํ์ผ๋ก ์ค์ , ์ ์ , ์ด์ค์ ๋ฑ์ ์ต์
์ด ์กด์ฌ
|
167 |
+
border-color : ํ
๋๋ฆฌ์ ์์์ผ๋ก, ๊ฐ์ color ์์ฑ์ ํฌ๋งท์ ์ฌ์ฉ
|
168 |
+
*/
|
169 |
+
border: 0.5px solid grey;
|
170 |
+
|
171 |
+
/*
|
172 |
+
[ padding ํ๊ทธ ]
|
173 |
+
์ง์ ๊ฐ์ px, cm, %๋ก ์ง์ ํ ์ ์๋ค.
|
174 |
+
margin์ ์์๊ฐ์ด ์ง์ ๊ฐ๋ฅํ์ง๋ง padding์ ์์๊ฐ ์ง์ ์ด ์๋๋ค.
|
175 |
+
|
176 |
+
padding ํ๊ทธ์ ๋น์ทํ ํ๊ทธ
|
177 |
+
: padding-top, padding-right, padding-bottom, padding-left
|
178 |
+
|
179 |
+
* 4๋ฉด ํ๊บผ๋ฒ์ padding ์ง์ ํ๊ธฐ
|
180 |
+
ex) padding: 5px, 7px, 3px, 0px;
|
181 |
+
(์, ์ค๋ฅธ์ชฝ, ์๋, ์ผ์ชฝ)
|
182 |
+
* 4๋ฉด ๋ชจ๋ ๊ฐ์ ๋ padding ์ง์ ํ๊ธฐ
|
183 |
+
ex) padding: 5px;
|
184 |
+
* ์, ์ค๋ฅธ์ชฝ&์ผ์ชฝ, ์๋ padding ์ง์ ํ๊ธฐ
|
185 |
+
ex) padding: 5px 10px 0px;
|
186 |
+
* ์&์๋, ์ค๋ฅธ์ชฝ&์ผ์ชฝ padding ์ง์ ํ๊ธฐ
|
187 |
+
ex) padding: 5px, 10px;
|
188 |
+
|
189 |
+
*/
|
190 |
+
padding: 0.5rem 1rem;
|
191 |
+
}
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
|
198 |
+
|
199 |
+
|
200 |
+
|
201 |
+
|
202 |
+
|
203 |
+
|
204 |
+
/* h1, h2 ํ๊ทธ ๋ถ๋ถ */
|
205 |
+
.gohome, .goticker {
|
206 |
+
text-decoration: none;
|
207 |
+
}
|
static/css/style.css
ADDED
@@ -0,0 +1,287 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
[ CSS ๊ธฐ๋ณธ ๋ฌธ๋ฒ ]
|
3 |
+
AND ์ฐ์ฐ์ : ์ ํ์ ์ฌ์ด์ ๊ณต๋ฐฑ์ด ์ ๊ฑฐ๋๋ ๊ฒฝ์ฐ ์ฌ๋ฌ ์ ํ์๋ฅผ ๋์์ ๋ง์กฑํ๋ ํ๊ทธ์ ์คํ์ผ์ ์ ์ฉ
|
4 |
+
OR ์ฐ์ฐ์ : ๋ ์ ํ์ ์ค ํ๋๋ผ๋ ๋ง์กฑ์ ์ ์ฉ๋๋ ์กฐ๊ฑด (์ผํ๋ฅผ ํตํด ๋ ์ ํ์ ์ค ํ๋๋ผ๋ ๋ง์กฑ์ ์ ์ฉ)
|
5 |
+
|
6 |
+
|
7 |
+
".a .b .c" : aํด๋์ค ๋ด๋ถ์ bํด๋์ค ๋ด๋ถ์ cํด๋์ค ์์์๋ง ์คํ์ผ ์ ์ฉ
|
8 |
+
".a.b.c" : ํด๋์ค ์์ฑ ๋ด์ a, b, c ๋ชจ๋ ์ค์ ๋ ๋ชจ๋ ์์๋ค์ ์ ํ
|
9 |
+
".a, .b, .c" : ์ผ์นํ๋ ๋ชจ๋ ์์ค๋ค์ ์ ํ
|
10 |
+
*/
|
11 |
+
|
12 |
+
|
13 |
+
.sec_cal {
|
14 |
+
width: 360px; /* ์์ฑ์ ์์ ๋๋น */
|
15 |
+
margin: 0 auto;
|
16 |
+
font-family: "NotoSansR";
|
17 |
+
}
|
18 |
+
|
19 |
+
/* ".a .b .c" : aํด๋์ค ๋ด๋ถ์ bํด๋์ค ๋ด๋ถ์ cํด๋์ค ์์์๋ง ์คํ์ผ ์ ์ฉ */
|
20 |
+
.sec_cal .cal_nav {
|
21 |
+
display: flex;
|
22 |
+
justify-content: center; /* ๊ฐ๋ก ์ถ์ ๊ธฐ์ค์ผ๋ก ์ข์ฐ์ ๋ํ ์ ๋ ฌ */
|
23 |
+
align-items: center; /* ์ธ๋ก ์ถ์ ๊ธฐ์ค์ผ๋ก ์ ๋ ฌ ์์๋์ ๋ํ ์ ๋ ฌ */
|
24 |
+
font-weight: 700; /* ํฐํธ(font)์ ๊ฐ์ค์น(weight)๋ ๊ตต๊ธฐ(boldness)๋ฅผ ๋ช
์ */
|
25 |
+
font-size: 48px; /* ํฐํธ(font)์ ํฌ๊ธฐ๋ฅผ ์ง์ */
|
26 |
+
line-height: 78px; /* Sets the height of a line box (์ค๊ฐ๊ฒฉ) */
|
27 |
+
}
|
28 |
+
|
29 |
+
.sec_cal .cal_nav .year-month {
|
30 |
+
width: 300px; /* ์์ฑ์ ์์ ๋๋น */
|
31 |
+
text-align: center;
|
32 |
+
line-height: 1;
|
33 |
+
}
|
34 |
+
|
35 |
+
.sec_cal .cal_nav .nav {
|
36 |
+
display: flex;
|
37 |
+
border: 1px solid #333333;
|
38 |
+
border-radius: 5px;
|
39 |
+
}
|
40 |
+
|
41 |
+
.sec_cal .cal_nav .go-prev,
|
42 |
+
.sec_cal .cal_nav .go-next {
|
43 |
+
display: block;
|
44 |
+
width: 50px; /* ์์ฑ์ ์์ ๋๋น */
|
45 |
+
height: 78px;
|
46 |
+
font-size: 0;
|
47 |
+
display: flex;
|
48 |
+
justify-content: center;
|
49 |
+
align-items: center;
|
50 |
+
}
|
51 |
+
|
52 |
+
.sec_cal .cal_nav .go-prev::before,
|
53 |
+
.sec_cal .cal_nav .go-next::before {
|
54 |
+
content: "";
|
55 |
+
display: block;
|
56 |
+
width: 20px; /* ์์ฑ์ ์์ ๋๋น */
|
57 |
+
height: 20px;
|
58 |
+
border: 3px solid #000;
|
59 |
+
border-width: 3px 3px 0 0;
|
60 |
+
transition: border 0.1s;
|
61 |
+
}
|
62 |
+
|
63 |
+
.sec_cal .cal_nav .go-prev:hover::before,
|
64 |
+
.sec_cal .cal_nav .go-next:hover::before {
|
65 |
+
border-color: #ed2a61;
|
66 |
+
}
|
67 |
+
|
68 |
+
.sec_cal .cal_nav .go-prev::before {
|
69 |
+
transform: rotate(-135deg);
|
70 |
+
}
|
71 |
+
|
72 |
+
.sec_cal .cal_nav .go-next::before {
|
73 |
+
transform: rotate(45deg);
|
74 |
+
}
|
75 |
+
|
76 |
+
.sec_cal .cal_wrap {
|
77 |
+
padding-top: 40px;
|
78 |
+
position: relative;
|
79 |
+
margin: 0 auto;
|
80 |
+
}
|
81 |
+
|
82 |
+
.sec_cal .cal_wrap .days {
|
83 |
+
display: flex;
|
84 |
+
margin-bottom: 20px; /* ์์ ํ๋จ์ margin ํ๋จ์ ์์ญ์ ์ค์ (์ฌ์ ๊ณต๊ฐ ์ค์ ) */
|
85 |
+
padding-bottom: 20px; /* ์์์ ๋ฐ๋ฅ์์ ํจ๋ฉ ์์ญ์ ๋์ด๋ฅผ ์ค์ */
|
86 |
+
border-bottom: 1px solid #ddd;
|
87 |
+
}
|
88 |
+
|
89 |
+
.sec_cal .cal_wrap::after {
|
90 |
+
top: 368px;
|
91 |
+
}
|
92 |
+
|
93 |
+
.sec_cal .cal_wrap .day {
|
94 |
+
display: flex;
|
95 |
+
align-items: center;
|
96 |
+
justify-content: center;
|
97 |
+
width: calc(100% / 7); /* ์์ฑ์ ์์ ๋๋น */
|
98 |
+
text-align: left;
|
99 |
+
color: #999;
|
100 |
+
font-size: 12px;
|
101 |
+
text-align: center;
|
102 |
+
border-radius: 5px;
|
103 |
+
}
|
104 |
+
|
105 |
+
|
106 |
+
/* <div class="day current today">1</div> */
|
107 |
+
.current.today {
|
108 |
+
/* background: rgb(3, 179, 65); */
|
109 |
+
background: rgb(242 242 242);
|
110 |
+
}
|
111 |
+
|
112 |
+
.sec_cal .cal_wrap .dates {
|
113 |
+
display: flex;
|
114 |
+
flex-flow: wrap;
|
115 |
+
height: 290px;
|
116 |
+
}
|
117 |
+
|
118 |
+
/* Pseduo-Classes ( ๊ทธ ์ค์์ Structural pseudo-classes ) */
|
119 |
+
.sec_cal .cal_wrap .day:nth-child(7n -1) {
|
120 |
+
color: #3c6ffa;
|
121 |
+
}
|
122 |
+
|
123 |
+
/* Pseduo-Classes ( ๊ทธ ์ค์์ Structural pseudo-classes ) */
|
124 |
+
.sec_cal .cal_wrap .day:nth-child(7n) {
|
125 |
+
color: #ed2a61;
|
126 |
+
}
|
127 |
+
|
128 |
+
.sec_cal .cal_wrap .day.disable {
|
129 |
+
color: #ddd;
|
130 |
+
}
|
131 |
+
|
132 |
+
|
133 |
+
|
134 |
+
|
135 |
+
|
136 |
+
|
137 |
+
|
138 |
+
|
139 |
+
|
140 |
+
|
141 |
+
|
142 |
+
|
143 |
+
|
144 |
+
|
145 |
+
|
146 |
+
/* Model ๊ด๋ จ */
|
147 |
+
|
148 |
+
/* id : "#" */
|
149 |
+
#model {
|
150 |
+
text-align: center;
|
151 |
+
}
|
152 |
+
|
153 |
+
/* id : "#" */
|
154 |
+
#text-input {
|
155 |
+
width: calc(100% / 2); /* ์์ฑ์ ์์ ๋๋น */
|
156 |
+
height: 78px;
|
157 |
+
word-break: break-all;
|
158 |
+
}
|
159 |
+
|
160 |
+
.text-output {
|
161 |
+
width: calc(100% / 2); /* ์์ฑ์ ์์ ๋๋น */
|
162 |
+
min-height: 1.2rem;
|
163 |
+
margin: 1rem;
|
164 |
+
border: 0.5px solid grey;
|
165 |
+
padding: 0.5rem 1rem;
|
166 |
+
}
|
167 |
+
|
168 |
+
|
169 |
+
|
170 |
+
|
171 |
+
|
172 |
+
|
173 |
+
|
174 |
+
|
175 |
+
|
176 |
+
|
177 |
+
|
178 |
+
|
179 |
+
|
180 |
+
|
181 |
+
|
182 |
+
|
183 |
+
|
184 |
+
|
185 |
+
/* Stocks ๊ด๋ จ */
|
186 |
+
|
187 |
+
|
188 |
+
/* .sec_cal { */
|
189 |
+
.stocks_wrap {
|
190 |
+
width: 580px; /* ์์ฑ์ ์์ ๋๋น */
|
191 |
+
margin: 0 auto;
|
192 |
+
font-family: "NotoSansR";
|
193 |
+
}
|
194 |
+
|
195 |
+
|
196 |
+
/* .sec_cal .cal_wrap { */
|
197 |
+
.stocks_wrap {
|
198 |
+
padding-top: 40px;
|
199 |
+
position: relative;
|
200 |
+
margin: 0 auto;
|
201 |
+
}
|
202 |
+
|
203 |
+
|
204 |
+
/* .sec_cal .cal_wrap .days { */
|
205 |
+
.stocks_wrap .stocks_columns {
|
206 |
+
display: flex;
|
207 |
+
margin-bottom: 20px; /* ์์ ํ๋จ์ margin ํ๋จ์ ์์ญ์ ์ค์ */
|
208 |
+
padding-bottom: 20px; /* ์์์ ๋ฐ๋ฅ์์ ํจ๋ฉ ์์ญ์ ๋์ด๋ฅผ ์ค์ */
|
209 |
+
border-bottom: 1px solid #ddd;
|
210 |
+
}
|
211 |
+
|
212 |
+
|
213 |
+
/* .sec_cal .cal_wrap .day { */
|
214 |
+
.stocks_wrap .stocks_columns .column,
|
215 |
+
.stocks_wrap .stocks .stock {
|
216 |
+
display: flex;
|
217 |
+
align-items: center;
|
218 |
+
justify-content: center;
|
219 |
+
width: 50px;
|
220 |
+
text-align: left;
|
221 |
+
color: #999;
|
222 |
+
font-size: 12px;
|
223 |
+
text-align: center;
|
224 |
+
border-radius: 5px; /* rounds the corners of an element's outer border edge. */
|
225 |
+
}
|
226 |
+
|
227 |
+
|
228 |
+
|
229 |
+
|
230 |
+
.stocks_wrap .stocks_columns .column {
|
231 |
+
font-size: 17px;
|
232 |
+
/* width: 70px; */
|
233 |
+
}
|
234 |
+
.stocks_wrap .stocks .stock {
|
235 |
+
font-size: 13px;
|
236 |
+
/* width: 35px; */
|
237 |
+
}
|
238 |
+
|
239 |
+
|
240 |
+
.name {
|
241 |
+
margin-right: 30px;
|
242 |
+
margin-left: 30px;
|
243 |
+
}
|
244 |
+
.sector, .industry{
|
245 |
+
margin-right: 18px;
|
246 |
+
margin-left: 48px;
|
247 |
+
}
|
248 |
+
.dff, .open, .close {
|
249 |
+
margin-right: 5px;
|
250 |
+
margin-left: 5px;
|
251 |
+
}
|
252 |
+
|
253 |
+
.stocks_wrap .stocks .ticker {
|
254 |
+
color: #04b70d;
|
255 |
+
text-decoration: underline;
|
256 |
+
}
|
257 |
+
.stocks_wrap .stocks .up {
|
258 |
+
color: #ed2a61;
|
259 |
+
}
|
260 |
+
.stocks_wrap .stocks .down {
|
261 |
+
color: #3c6ffa;
|
262 |
+
}
|
263 |
+
|
264 |
+
|
265 |
+
|
266 |
+
/* .sec_cal .cal_wrap .dates { */
|
267 |
+
.stocks_wrap .stocks {
|
268 |
+
display: flex;
|
269 |
+
flex-flow: wrap;
|
270 |
+
height: 5000px; /* ๋์ด ๊ฐ๊ฒฉ */
|
271 |
+
}
|
272 |
+
|
273 |
+
|
274 |
+
|
275 |
+
|
276 |
+
|
277 |
+
|
278 |
+
|
279 |
+
|
280 |
+
|
281 |
+
|
282 |
+
|
283 |
+
|
284 |
+
/* h1 ํ๊ทธ ๋ถ๋ถ */
|
285 |
+
.gohome {
|
286 |
+
text-decoration: none;
|
287 |
+
}
|
static/js/chartIndex.js
ADDED
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// jQuery
|
3 |
+
// $(document).ready(function() { });
|
4 |
+
$(function() {
|
5 |
+
chartInit();
|
6 |
+
});
|
7 |
+
|
8 |
+
|
9 |
+
|
10 |
+
|
11 |
+
function handleReturn(output) {
|
12 |
+
return output;
|
13 |
+
}
|
14 |
+
|
15 |
+
|
16 |
+
|
17 |
+
|
18 |
+
|
19 |
+
function chartInit() {
|
20 |
+
// Ticker ์ด๋ฆ ๊ฐ์ ธ์ค๊ธฐ
|
21 |
+
// ํด๋น class์ text ๊ฐ์ ธ์ค๊ธฐ
|
22 |
+
let ticker = document.querySelector('.tickerName').textContent;
|
23 |
+
// console.log(ticker.indexOf(' '));
|
24 |
+
idx = ticker.indexOf(' ');
|
25 |
+
ticker = ticker.substring(0, idx);
|
26 |
+
|
27 |
+
|
28 |
+
// Javascript -> Flask (Python) -> Javascript
|
29 |
+
chart_data = sendAjax_async("/chart", {"ticker": ticker}, "json", handleReturn);
|
30 |
+
console.log(chart_data);
|
31 |
+
console.log(Object.keys(chart_data.Close));
|
32 |
+
|
33 |
+
|
34 |
+
// x์ถ๊ณผ data ์ค์
|
35 |
+
// data: [{'x': date, 'o': open, 'h': high, 'l': low, 'c': close}, { }, { }, ... ]
|
36 |
+
data = [];
|
37 |
+
key_list = Object.keys(chart_data.Close);
|
38 |
+
for (var i=key_list.length-15; i<key_list.length; i++) {
|
39 |
+
key = key_list[i];
|
40 |
+
const [year, month, day] = key.split("-");
|
41 |
+
const x = new Date(parseInt(year), parseInt(month), parseInt(day), 9, 0, 0, 0).getTime();
|
42 |
+
data.push({'x': x, 'o': chart_data.Open[key].toFixed(2), 'h': chart_data.High[key].toFixed(2), 'l': chart_data.Low[key].toFixed(2), 'c': chart_data.Close[key].toFixed(2)})
|
43 |
+
}
|
44 |
+
console.log("data : ", data);
|
45 |
+
|
46 |
+
|
47 |
+
|
48 |
+
|
49 |
+
|
50 |
+
|
51 |
+
// Javascript chart.js candlestick
|
52 |
+
let mychart = document.getElementById('myChart');
|
53 |
+
new Chart(mychart, {
|
54 |
+
type: 'candlestick',
|
55 |
+
data: {
|
56 |
+
datasets: [{
|
57 |
+
label: 'CHRT - '.concat(ticker),
|
58 |
+
data: data
|
59 |
+
}]
|
60 |
+
}
|
61 |
+
});
|
62 |
+
|
63 |
+
|
64 |
+
|
65 |
+
|
66 |
+
|
67 |
+
//////////////////////////////////////////////////////////////////
|
68 |
+
|
69 |
+
// Javascript๋ฅผ ์ด์ฉํด HTML์ ๋์ ์ผ๋ก ํ๊ทธ ์ถ๊ฐ
|
70 |
+
|
71 |
+
// a ํ๊ทธ onclick ์ ์ฉ
|
72 |
+
const goTicker = document.querySelector('.goticker');
|
73 |
+
let goTickerURL = '/'.concat(ticker)
|
74 |
+
goTicker.setAttribute('href', goTickerURL);
|
75 |
+
|
76 |
+
//////////////////////////////////////////////////////////////////
|
77 |
+
|
78 |
+
|
79 |
+
|
80 |
+
|
81 |
+
|
82 |
+
//////////////////////////////////////////////////////////////////
|
83 |
+
|
84 |
+
// table title ํ์
|
85 |
+
// ํด๋น class์ text ์ง์ด๋ฃ๊ธฐ
|
86 |
+
const table_title = document.querySelector('.table-title');
|
87 |
+
$('.table .table-title').text(ticker.concat(' News'));
|
88 |
+
|
89 |
+
|
90 |
+
|
91 |
+
|
92 |
+
// Javascript -> Flask (Python) -> Javascript
|
93 |
+
news = sendAjax_async(url="/news", data={"ticker": ticker}, dataType="json", handle=handleReturn);
|
94 |
+
|
95 |
+
news_table = document.querySelector('.table .news-table');
|
96 |
+
// console.log(news_table.innerHTML);
|
97 |
+
|
98 |
+
|
99 |
+
// console.log(news);
|
100 |
+
// console.log(Object.keys(news)); // key ๋ฐฐ์ด ๋ง๋ค๊ธฐ
|
101 |
+
// console.log(typeof Object.keys(news));
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
|
106 |
+
/*
|
107 |
+
[ ๋ ์ง ์ ๋ ฌ ]
|
108 |
+
"news"์์ index์ ๋ํด์ ์ ๋ ฌ์ ํ๊ณ reduce() ํจ์๋ฅผ ์ ์ฉ.
|
109 |
+
|
110 |
+
reduce() : ๋ฐฐ์ด์ ๊ฐ ์์์ ๋ํด ์ฃผ์ด์ง reducer ํจ์๋ฅผ ์คํํ๊ณ , ํ๋์ ๊ฒฐ๊ณผ๊ฐ์ ๋ฐํ
|
111 |
+
*/
|
112 |
+
sorted_news = {}
|
113 |
+
sorted_news = Object.keys(news).sort(function (a, b) {
|
114 |
+
if (a < b) { return 1; }
|
115 |
+
else if (a > b) { return -1; }
|
116 |
+
else { return 0; }
|
117 |
+
}).reduce((sorted_news, key) => {
|
118 |
+
sorted_news[key] = news[key];
|
119 |
+
return sorted_news;
|
120 |
+
}, {});
|
121 |
+
// console.log(sorted_news);
|
122 |
+
|
123 |
+
var key_list = Object.keys(chart_data.Open);
|
124 |
+
var open_list = Object.values(chart_data.Open);
|
125 |
+
var close_list = Object.values(chart_data.Close);
|
126 |
+
|
127 |
+
for (var i=0; i<key_list.length; i++) {
|
128 |
+
const [year, month, day] = key_list[i].split("-");
|
129 |
+
key_list[i] = year + '.' + month + '.' + day;
|
130 |
+
}
|
131 |
+
console.log(key_list);
|
132 |
+
|
133 |
+
// List ์์ value๋ฅผ ๋ฝ์ ๋, (Python) => for item in list:
|
134 |
+
Object.keys(sorted_news).forEach(key => {
|
135 |
+
var idx = key_list.indexOf(String(key));
|
136 |
+
|
137 |
+
if (idx != -1) { var diff = ((open_list[idx]-close_list[idx-1])/(open_list[idx]) * 100.0).toFixed(2); }
|
138 |
+
else { var diff = '.'; }
|
139 |
+
|
140 |
+
if (diff == '.') {
|
141 |
+
var diff_html = '<th class="news diff">' + diff + '</th>';
|
142 |
+
}
|
143 |
+
else if (diff > 0) {
|
144 |
+
var diff_html = '<th class="news diff up">+' + diff + ' %</th>';
|
145 |
+
}
|
146 |
+
else {
|
147 |
+
var diff_html = '<th class="news diff down">' + diff + ' %</th>';
|
148 |
+
}
|
149 |
+
var html = '<tr align="center" bgcolor="white"><th>+</th><th>' + key + '</th>' + diff_html + '<td style="text-align: left;">';
|
150 |
+
|
151 |
+
for (var i = 0; i < sorted_news[key].length; i++) {
|
152 |
+
var title = sorted_news[key][i].substring(0, sorted_news[key][i].length-4);
|
153 |
+
var sendTitle = title; // Javascript -> Python ๋ณด๋ด๊ธฐ ์ํ title
|
154 |
+
|
155 |
+
|
156 |
+
// title์์ & ํ์๊ฐ ์์ ์ ์์.
|
157 |
+
// Title ์์ '&'๋ก ํ์๋์ด ์๋๋ฐ ๋ฐ๋ก ๊ตฌ๋ณํด์ผ ๋๋ค.
|
158 |
+
// andSymbolInTitle ์์ ๊ฐ์ ธ์จ '&' ์์น index๋ฅผ title๊ณผ ํฉ์ณ์ค๋ค.
|
159 |
+
andSymbolInTitle = [];
|
160 |
+
let idx = 0;
|
161 |
+
// title = "asdf&asdf&AS&DF&&";
|
162 |
+
// sendTitle = title;
|
163 |
+
|
164 |
+
while (true) {
|
165 |
+
idx = sendTitle.indexOf('&', idx);
|
166 |
+
if (idx == -1) { break; }
|
167 |
+
sendTitle = sendTitle.substring(0, idx) + sendTitle.substring(idx+1, sendTitle.length);
|
168 |
+
// console.log(sendTitle);
|
169 |
+
andSymbolInTitle.push(idx + andSymbolInTitle.length);
|
170 |
+
}
|
171 |
+
|
172 |
+
var link = String('"/info?ticker='.concat(ticker, '&date=', key, '&title=', sendTitle, '&andSymbolInTitle=', andSymbolInTitle, '"'));
|
173 |
+
// console.log(link);
|
174 |
+
html = html + '<a href=' + link + '>' + title + '</a><br>';
|
175 |
+
}
|
176 |
+
html = html + '</td>';
|
177 |
+
|
178 |
+
news_table.innerHTML = news_table.innerHTML + html;
|
179 |
+
});
|
180 |
+
}
|
181 |
+
|
182 |
+
|
183 |
+
|
184 |
+
|
185 |
+
|
186 |
+
|
187 |
+
|
188 |
+
|
189 |
+
|
190 |
+
|
191 |
+
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
/**
|
198 |
+
*
|
199 |
+
* @param {string} url from javascript to flask(python) with route
|
200 |
+
* @param {dictionary} data from javascript to flask(python) with data
|
201 |
+
* @param {function} handle ํฐ ์๋ฏธ ์์
|
202 |
+
*/
|
203 |
+
function sendAjax(url, data, handle) {
|
204 |
+
/*
|
205 |
+
jQuery.getJSON(url, [, data], [, success])
|
206 |
+
|
207 |
+
Load JSON-encoded data from the server using a GET HTTP request.
|
208 |
+
*/
|
209 |
+
|
210 |
+
$.getJSON(url, data,
|
211 |
+
function(response) {
|
212 |
+
handle(response.result);
|
213 |
+
});
|
214 |
+
}
|
215 |
+
|
216 |
+
|
217 |
+
/**
|
218 |
+
*
|
219 |
+
* @param {string} url from javascript to flask(python) with route
|
220 |
+
* @param {dictionary} data from javascript to flask(python) with data
|
221 |
+
* @param {string} dataType The type of data that you're expecting back from the server. (ex. "json")
|
222 |
+
* @param {function} handle ํฐ ์๋ฏธ ์์
|
223 |
+
* @returns from flask(python) to javascript with data
|
224 |
+
*/
|
225 |
+
function sendAjax_async(url, data, dataType, handle) {
|
226 |
+
/*
|
227 |
+
jQuery.ajax(url, [, settings])
|
228 |
+
|
229 |
+
jQuery.getJSON => Asynchronous (๋น๋๊ธฐ์)
|
230 |
+
|
231 |
+
Synchronous => ๋๊ธฐ์ : ์ฝ๋ ์์๋๋ก ์งํ
|
232 |
+
*/
|
233 |
+
|
234 |
+
var search_var;
|
235 |
+
console.log("Internal : sendAjax async");
|
236 |
+
|
237 |
+
$.ajax(url=url, settings={data: data, dataType: dataType, async: false,
|
238 |
+
success: function(response) {
|
239 |
+
console.log("Success : ", typeof response);
|
240 |
+
search_var = handle(response.result); // handle, ํฐ ์๋ฏธ ์์
|
241 |
+
}
|
242 |
+
});
|
243 |
+
|
244 |
+
return search_var
|
245 |
+
}
|
246 |
+
|
static/js/chartjs-chart-financial.js
ADDED
@@ -0,0 +1,525 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*!
|
2 |
+
* @license
|
3 |
+
* chartjs-chart-financial
|
4 |
+
* http://chartjs.org/
|
5 |
+
* Version: 0.1.0
|
6 |
+
*
|
7 |
+
* Copyright 2021 Chart.js Contributors
|
8 |
+
* Released under the MIT license
|
9 |
+
* https://github.com/chartjs/chartjs-chart-financial/blob/master/LICENSE.md
|
10 |
+
*/
|
11 |
+
(function (global, factory) {
|
12 |
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('chart.js'), require('chart.js/helpers')) :
|
13 |
+
typeof define === 'function' && define.amd ? define(['chart.js', 'chart.js/helpers'], factory) :
|
14 |
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Chart, global.Chart.helpers));
|
15 |
+
}(this, (function (chart_js, helpers) { 'use strict';
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
|
19 |
+
* @private
|
20 |
+
*/
|
21 |
+
function computeMinSampleSize(scale, pixels) {
|
22 |
+
let min = scale._length;
|
23 |
+
let prev, curr, i, ilen;
|
24 |
+
|
25 |
+
for (i = 1, ilen = pixels.length; i < ilen; ++i) {
|
26 |
+
min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
|
27 |
+
}
|
28 |
+
|
29 |
+
for (i = 0, ilen = scale.ticks.length; i < ilen; ++i) {
|
30 |
+
curr = scale.getPixelForTick(i);
|
31 |
+
min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
|
32 |
+
prev = curr;
|
33 |
+
}
|
34 |
+
|
35 |
+
return min;
|
36 |
+
}
|
37 |
+
|
38 |
+
/**
|
39 |
+
* This class is based off controller.bar.js from the upstream Chart.js library
|
40 |
+
*/
|
41 |
+
class FinancialController extends chart_js.BarController {
|
42 |
+
|
43 |
+
getLabelAndValue(index) {
|
44 |
+
const me = this;
|
45 |
+
const parsed = me.getParsed(index);
|
46 |
+
const axis = me._cachedMeta.iScale.axis;
|
47 |
+
|
48 |
+
const {o, h, l, c} = parsed;
|
49 |
+
const value = `O: ${o} H: ${h} L: ${l} C: ${c}`;
|
50 |
+
|
51 |
+
return {
|
52 |
+
label: `${me._cachedMeta.iScale.getLabelForValue(parsed[axis])}`,
|
53 |
+
value
|
54 |
+
};
|
55 |
+
}
|
56 |
+
|
57 |
+
getAllParsedValues() {
|
58 |
+
const meta = this._cachedMeta;
|
59 |
+
const axis = meta.iScale.axis;
|
60 |
+
const parsed = meta._parsed;
|
61 |
+
const values = [];
|
62 |
+
for (let i = 0; i < parsed.length; ++i) {
|
63 |
+
values.push(parsed[i][axis]);
|
64 |
+
}
|
65 |
+
return values;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Implement this ourselves since it doesn't handle high and low values
|
70 |
+
* https://github.com/chartjs/Chart.js/issues/7328
|
71 |
+
* @protected
|
72 |
+
*/
|
73 |
+
getMinMax(scale) {
|
74 |
+
const meta = this._cachedMeta;
|
75 |
+
const _parsed = meta._parsed;
|
76 |
+
const axis = meta.iScale.axis;
|
77 |
+
|
78 |
+
if (_parsed.length < 2) {
|
79 |
+
return {min: 0, max: 1};
|
80 |
+
}
|
81 |
+
|
82 |
+
if (scale === meta.iScale) {
|
83 |
+
return {min: _parsed[0][axis], max: _parsed[_parsed.length - 1][axis]};
|
84 |
+
}
|
85 |
+
|
86 |
+
let min = Number.POSITIVE_INFINITY;
|
87 |
+
let max = Number.NEGATIVE_INFINITY;
|
88 |
+
for (let i = 0; i < _parsed.length; i++) {
|
89 |
+
const data = _parsed[i];
|
90 |
+
min = Math.min(min, data.l);
|
91 |
+
max = Math.max(max, data.h);
|
92 |
+
}
|
93 |
+
return {min, max};
|
94 |
+
}
|
95 |
+
|
96 |
+
_getRuler() {
|
97 |
+
const me = this;
|
98 |
+
const opts = me.options;
|
99 |
+
const meta = me._cachedMeta;
|
100 |
+
const iScale = meta.iScale;
|
101 |
+
const axis = iScale.axis;
|
102 |
+
const pixels = [];
|
103 |
+
for (let i = 0; i < meta.data.length; ++i) {
|
104 |
+
pixels.push(iScale.getPixelForValue(me.getParsed(i)[axis]));
|
105 |
+
}
|
106 |
+
const barThickness = opts.barThickness;
|
107 |
+
const min = computeMinSampleSize(iScale, pixels);
|
108 |
+
return {
|
109 |
+
min,
|
110 |
+
pixels,
|
111 |
+
start: iScale._startPixel,
|
112 |
+
end: iScale._endPixel,
|
113 |
+
stackCount: me._getStackCount(),
|
114 |
+
scale: iScale,
|
115 |
+
ratio: barThickness ? 1 : opts.categoryPercentage * opts.barPercentage
|
116 |
+
};
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* @protected
|
121 |
+
*/
|
122 |
+
calculateElementProperties(index, ruler, reset, options) {
|
123 |
+
const me = this;
|
124 |
+
const vscale = me._cachedMeta.vScale;
|
125 |
+
const base = vscale.getBasePixel();
|
126 |
+
const ipixels = me._calculateBarIndexPixels(index, ruler, options);
|
127 |
+
const data = me.chart.data.datasets[me.index].data[index];
|
128 |
+
const open = vscale.getPixelForValue(data.o);
|
129 |
+
const high = vscale.getPixelForValue(data.h);
|
130 |
+
const low = vscale.getPixelForValue(data.l);
|
131 |
+
const close = vscale.getPixelForValue(data.c);
|
132 |
+
|
133 |
+
return {
|
134 |
+
base: reset ? base : low,
|
135 |
+
x: ipixels.center,
|
136 |
+
y: (low + high) / 2,
|
137 |
+
width: ipixels.size,
|
138 |
+
open,
|
139 |
+
high,
|
140 |
+
low,
|
141 |
+
close
|
142 |
+
};
|
143 |
+
}
|
144 |
+
|
145 |
+
draw() {
|
146 |
+
const me = this;
|
147 |
+
const chart = me.chart;
|
148 |
+
const rects = me._cachedMeta.data;
|
149 |
+
helpers.clipArea(chart.ctx, chart.chartArea);
|
150 |
+
for (let i = 0; i < rects.length; ++i) {
|
151 |
+
rects[i].draw(me._ctx);
|
152 |
+
}
|
153 |
+
helpers.unclipArea(chart.ctx);
|
154 |
+
}
|
155 |
+
|
156 |
+
}
|
157 |
+
|
158 |
+
FinancialController.overrides = {
|
159 |
+
label: '',
|
160 |
+
|
161 |
+
parsing: false,
|
162 |
+
|
163 |
+
hover: {
|
164 |
+
mode: 'label'
|
165 |
+
},
|
166 |
+
|
167 |
+
datasets: {
|
168 |
+
categoryPercentage: 0.8,
|
169 |
+
barPercentage: 0.9,
|
170 |
+
animation: {
|
171 |
+
numbers: {
|
172 |
+
type: 'number',
|
173 |
+
properties: ['x', 'y', 'base', 'width', 'open', 'high', 'low', 'close']
|
174 |
+
}
|
175 |
+
}
|
176 |
+
},
|
177 |
+
|
178 |
+
scales: {
|
179 |
+
x: {
|
180 |
+
type: 'timeseries',
|
181 |
+
offset: true,
|
182 |
+
ticks: {
|
183 |
+
major: {
|
184 |
+
enabled: true,
|
185 |
+
},
|
186 |
+
fontStyle: context => context.tick.major ? 'bold' : undefined,
|
187 |
+
source: 'data',
|
188 |
+
maxRotation: 0,
|
189 |
+
autoSkip: true,
|
190 |
+
autoSkipPadding: 75,
|
191 |
+
sampleSize: 100
|
192 |
+
},
|
193 |
+
afterBuildTicks: scale => {
|
194 |
+
const DateTime = window && window.luxon && window.luxon.DateTime;
|
195 |
+
if (!DateTime) {
|
196 |
+
return;
|
197 |
+
}
|
198 |
+
const majorUnit = scale._majorUnit;
|
199 |
+
const ticks = scale.ticks;
|
200 |
+
const firstTick = ticks[0];
|
201 |
+
if (!firstTick) {
|
202 |
+
return;
|
203 |
+
}
|
204 |
+
|
205 |
+
let val = DateTime.fromMillis(firstTick.value);
|
206 |
+
if ((majorUnit === 'minute' && val.second === 0)
|
207 |
+
|| (majorUnit === 'hour' && val.minute === 0)
|
208 |
+
|| (majorUnit === 'day' && val.hour === 9)
|
209 |
+
|| (majorUnit === 'month' && val.day <= 3 && val.weekday === 1)
|
210 |
+
|| (majorUnit === 'year' && val.month === 1)) {
|
211 |
+
firstTick.major = true;
|
212 |
+
} else {
|
213 |
+
firstTick.major = false;
|
214 |
+
}
|
215 |
+
let lastMajor = val.get(majorUnit);
|
216 |
+
|
217 |
+
for (let i = 1; i < ticks.length; i++) {
|
218 |
+
const tick = ticks[i];
|
219 |
+
val = DateTime.fromMillis(tick.value);
|
220 |
+
const currMajor = val.get(majorUnit);
|
221 |
+
tick.major = currMajor !== lastMajor;
|
222 |
+
lastMajor = currMajor;
|
223 |
+
}
|
224 |
+
scale.ticks = ticks;
|
225 |
+
}
|
226 |
+
},
|
227 |
+
y: {
|
228 |
+
type: 'linear'
|
229 |
+
}
|
230 |
+
},
|
231 |
+
|
232 |
+
plugins: {
|
233 |
+
tooltip: {
|
234 |
+
intersect: false,
|
235 |
+
mode: 'index',
|
236 |
+
callbacks: {
|
237 |
+
label(ctx) {
|
238 |
+
const point = ctx.parsed;
|
239 |
+
|
240 |
+
if (!helpers.isNullOrUndef(point.y)) {
|
241 |
+
return chart_js.defaults.plugins.tooltip.callbacks.label(ctx);
|
242 |
+
}
|
243 |
+
|
244 |
+
const {o, h, l, c} = point;
|
245 |
+
|
246 |
+
return `O: ${o} H: ${h} L: ${l} C: ${c}`;
|
247 |
+
}
|
248 |
+
}
|
249 |
+
}
|
250 |
+
}
|
251 |
+
};
|
252 |
+
|
253 |
+
const globalOpts$2 = chart_js.Chart.defaults;
|
254 |
+
|
255 |
+
globalOpts$2.elements.financial = {
|
256 |
+
color: {
|
257 |
+
up: 'rgba(255, 0, 0, 1)',
|
258 |
+
down: 'rgba(0, 0, 255, 1)',
|
259 |
+
unchanged: 'rgba(90, 90, 90, 1)',
|
260 |
+
}
|
261 |
+
};
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Helper function to get the bounds of the bar regardless of the orientation
|
265 |
+
* @param {Rectangle} bar the bar
|
266 |
+
* @param {boolean} [useFinalPosition]
|
267 |
+
* @return {object} bounds of the bar
|
268 |
+
* @private
|
269 |
+
*/
|
270 |
+
function getBarBounds(bar, useFinalPosition) {
|
271 |
+
const {x, y, base, width, height} = bar.getProps(['x', 'low', 'high', 'width', 'height'], useFinalPosition);
|
272 |
+
|
273 |
+
let left, right, top, bottom, half;
|
274 |
+
|
275 |
+
if (bar.horizontal) {
|
276 |
+
half = height / 2;
|
277 |
+
left = Math.min(x, base);
|
278 |
+
right = Math.max(x, base);
|
279 |
+
top = y - half;
|
280 |
+
bottom = y + half;
|
281 |
+
} else {
|
282 |
+
half = width / 2;
|
283 |
+
left = x - half;
|
284 |
+
right = x + half;
|
285 |
+
top = Math.min(y, base); // use min because 0 pixel at top of screen
|
286 |
+
bottom = Math.max(y, base);
|
287 |
+
}
|
288 |
+
|
289 |
+
return {left, top, right, bottom};
|
290 |
+
}
|
291 |
+
|
292 |
+
function inRange(bar, x, y, useFinalPosition) {
|
293 |
+
const skipX = x === null;
|
294 |
+
const skipY = y === null;
|
295 |
+
const bounds = !bar || (skipX && skipY) ? false : getBarBounds(bar, useFinalPosition);
|
296 |
+
|
297 |
+
return bounds
|
298 |
+
&& (skipX || x >= bounds.left && x <= bounds.right)
|
299 |
+
&& (skipY || y >= bounds.top && y <= bounds.bottom);
|
300 |
+
}
|
301 |
+
|
302 |
+
class FinancialElement extends chart_js.Element {
|
303 |
+
|
304 |
+
height() {
|
305 |
+
return this.base - this.y;
|
306 |
+
}
|
307 |
+
|
308 |
+
inRange(mouseX, mouseY, useFinalPosition) {
|
309 |
+
return inRange(this, mouseX, mouseY, useFinalPosition);
|
310 |
+
}
|
311 |
+
|
312 |
+
inXRange(mouseX, useFinalPosition) {
|
313 |
+
return inRange(this, mouseX, null, useFinalPosition);
|
314 |
+
}
|
315 |
+
|
316 |
+
inYRange(mouseY, useFinalPosition) {
|
317 |
+
return inRange(this, null, mouseY, useFinalPosition);
|
318 |
+
}
|
319 |
+
|
320 |
+
getRange(axis) {
|
321 |
+
return axis === 'x' ? this.width / 2 : this.height / 2;
|
322 |
+
}
|
323 |
+
|
324 |
+
getCenterPoint(useFinalPosition) {
|
325 |
+
const {x, low, high} = this.getProps(['x', 'low', 'high'], useFinalPosition);
|
326 |
+
return {
|
327 |
+
x,
|
328 |
+
y: (high + low) / 2
|
329 |
+
};
|
330 |
+
}
|
331 |
+
|
332 |
+
tooltipPosition(useFinalPosition) {
|
333 |
+
const {x, open, close} = this.getProps(['x', 'open', 'close'], useFinalPosition);
|
334 |
+
return {
|
335 |
+
x,
|
336 |
+
y: (open + close) / 2
|
337 |
+
};
|
338 |
+
}
|
339 |
+
}
|
340 |
+
|
341 |
+
const globalOpts$1 = chart_js.Chart.defaults;
|
342 |
+
|
343 |
+
class CandlestickElement extends FinancialElement {
|
344 |
+
draw(ctx) {
|
345 |
+
const me = this;
|
346 |
+
|
347 |
+
const {x, open, high, low, close} = me;
|
348 |
+
|
349 |
+
let borderColors = me.borderColor;
|
350 |
+
if (typeof borderColors === 'string') {
|
351 |
+
borderColors = {
|
352 |
+
up: borderColors,
|
353 |
+
down: borderColors,
|
354 |
+
unchanged: borderColors
|
355 |
+
};
|
356 |
+
}
|
357 |
+
|
358 |
+
let borderColor;
|
359 |
+
if (close < open) {
|
360 |
+
borderColor = helpers.valueOrDefault(borderColors ? borderColors.up : undefined, globalOpts$1.elements.candlestick.borderColor);
|
361 |
+
ctx.fillStyle = helpers.valueOrDefault(me.color ? me.color.up : undefined, globalOpts$1.elements.candlestick.color.up);
|
362 |
+
} else if (close > open) {
|
363 |
+
borderColor = helpers.valueOrDefault(borderColors ? borderColors.down : undefined, globalOpts$1.elements.candlestick.borderColor);
|
364 |
+
ctx.fillStyle = helpers.valueOrDefault(me.color ? me.color.down : undefined, globalOpts$1.elements.candlestick.color.down);
|
365 |
+
} else {
|
366 |
+
borderColor = helpers.valueOrDefault(borderColors ? borderColors.unchanged : undefined, globalOpts$1.elements.candlestick.borderColor);
|
367 |
+
ctx.fillStyle = helpers.valueOrDefault(me.color ? me.color.unchanged : undefined, globalOpts$1.elements.candlestick.color.unchanged);
|
368 |
+
}
|
369 |
+
|
370 |
+
ctx.lineWidth = helpers.valueOrDefault(me.borderWidth, globalOpts$1.elements.candlestick.borderWidth);
|
371 |
+
ctx.strokeStyle = helpers.valueOrDefault(borderColor, globalOpts$1.elements.candlestick.borderColor);
|
372 |
+
|
373 |
+
ctx.beginPath();
|
374 |
+
ctx.moveTo(x, high);
|
375 |
+
ctx.lineTo(x, Math.min(open, close));
|
376 |
+
ctx.moveTo(x, low);
|
377 |
+
ctx.lineTo(x, Math.max(open, close));
|
378 |
+
ctx.stroke();
|
379 |
+
ctx.fillRect(x - me.width / 2, close, me.width, open - close);
|
380 |
+
ctx.strokeRect(x - me.width / 2, close, me.width, open - close);
|
381 |
+
ctx.closePath();
|
382 |
+
}
|
383 |
+
}
|
384 |
+
|
385 |
+
CandlestickElement.id = 'candlestick';
|
386 |
+
CandlestickElement.defaults = helpers.merge({}, [globalOpts$1.elements.financial, {
|
387 |
+
borderColor: globalOpts$1.elements.financial.color.unchanged,
|
388 |
+
borderWidth: 1,
|
389 |
+
}]);
|
390 |
+
|
391 |
+
class CandlestickController extends FinancialController {
|
392 |
+
|
393 |
+
updateElements(elements, start, count, mode) {
|
394 |
+
const me = this;
|
395 |
+
const dataset = me.getDataset();
|
396 |
+
const ruler = me._ruler || me._getRuler();
|
397 |
+
const firstOpts = me.resolveDataElementOptions(start, mode);
|
398 |
+
const sharedOptions = me.getSharedOptions(firstOpts);
|
399 |
+
const includeOptions = me.includeOptions(mode, sharedOptions);
|
400 |
+
|
401 |
+
me.updateSharedOptions(sharedOptions, mode, firstOpts);
|
402 |
+
|
403 |
+
for (let i = start; i < count; i++) {
|
404 |
+
const options = sharedOptions || me.resolveDataElementOptions(i, mode);
|
405 |
+
|
406 |
+
const lineColor = (elements[i]['close'] - elements[i]['open'] < 0)? "rgb(255, 0, 0, 1)" : "rgb(0, 0, 255, 1)";
|
407 |
+
|
408 |
+
const baseProperties = me.calculateElementProperties(i, ruler, mode === 'reset', options);
|
409 |
+
const properties = {
|
410 |
+
...baseProperties,
|
411 |
+
datasetLabel: dataset.label || '',
|
412 |
+
// label: '', // to get label value please use dataset.data[index].label
|
413 |
+
|
414 |
+
// Appearance
|
415 |
+
color: dataset.color,
|
416 |
+
borderColor: lineColor,
|
417 |
+
// borderColor: dataset.borderColor,
|
418 |
+
borderWidth: dataset.borderWidth,
|
419 |
+
};
|
420 |
+
|
421 |
+
if (includeOptions) {
|
422 |
+
properties.options = options;
|
423 |
+
}
|
424 |
+
me.updateElement(elements[i], i, properties, mode);
|
425 |
+
}
|
426 |
+
}
|
427 |
+
|
428 |
+
}
|
429 |
+
|
430 |
+
CandlestickController.id = 'candlestick';
|
431 |
+
CandlestickController.defaults = helpers.merge({
|
432 |
+
dataElementType: CandlestickElement.id
|
433 |
+
}, chart_js.Chart.defaults.financial);
|
434 |
+
|
435 |
+
const globalOpts = chart_js.Chart.defaults;
|
436 |
+
|
437 |
+
class OhlcElement extends FinancialElement {
|
438 |
+
draw(ctx) {
|
439 |
+
const me = this;
|
440 |
+
|
441 |
+
const {x, open, high, low, close} = me;
|
442 |
+
|
443 |
+
const armLengthRatio = helpers.valueOrDefault(me.armLengthRatio, globalOpts.elements.ohlc.armLengthRatio);
|
444 |
+
let armLength = helpers.valueOrDefault(me.armLength, globalOpts.elements.ohlc.armLength);
|
445 |
+
if (armLength === null) {
|
446 |
+
// The width of an ohlc is affected by barPercentage and categoryPercentage
|
447 |
+
// This behavior is caused by extending controller.financial, which extends controller.bar
|
448 |
+
// barPercentage and categoryPercentage are now set to 1.0 (see controller.ohlc)
|
449 |
+
// and armLengthRatio is multipled by 0.5,
|
450 |
+
// so that when armLengthRatio=1.0, the arms from neighbour ohcl touch,
|
451 |
+
// and when armLengthRatio=0.0, ohcl are just vertical lines.
|
452 |
+
armLength = me.width * armLengthRatio * 0.5;
|
453 |
+
}
|
454 |
+
|
455 |
+
if (close < open) {
|
456 |
+
ctx.strokeStyle = helpers.valueOrDefault(me.color ? me.color.up : undefined, globalOpts.elements.ohlc.color.up);
|
457 |
+
} else if (close > open) {
|
458 |
+
ctx.strokeStyle = helpers.valueOrDefault(me.color ? me.color.down : undefined, globalOpts.elements.ohlc.color.down);
|
459 |
+
} else {
|
460 |
+
ctx.strokeStyle = helpers.valueOrDefault(me.color ? me.color.unchanged : undefined, globalOpts.elements.ohlc.color.unchanged);
|
461 |
+
}
|
462 |
+
ctx.lineWidth = helpers.valueOrDefault(me.lineWidth, globalOpts.elements.ohlc.lineWidth);
|
463 |
+
|
464 |
+
ctx.beginPath();
|
465 |
+
ctx.moveTo(x, high);
|
466 |
+
ctx.lineTo(x, low);
|
467 |
+
ctx.moveTo(x - armLength, open);
|
468 |
+
ctx.lineTo(x, open);
|
469 |
+
ctx.moveTo(x + armLength, close);
|
470 |
+
ctx.lineTo(x, close);
|
471 |
+
ctx.stroke();
|
472 |
+
}
|
473 |
+
}
|
474 |
+
|
475 |
+
OhlcElement.id = 'ohlc';
|
476 |
+
OhlcElement.defaults = helpers.merge({}, [globalOpts.elements.financial, {
|
477 |
+
lineWidth: 2,
|
478 |
+
armLength: null,
|
479 |
+
armLengthRatio: 0.8,
|
480 |
+
}]);
|
481 |
+
|
482 |
+
class OhlcController extends FinancialController {
|
483 |
+
|
484 |
+
updateElements(elements, start, count, mode) {
|
485 |
+
const me = this;
|
486 |
+
const dataset = me.getDataset();
|
487 |
+
const ruler = me._ruler || me._getRuler();
|
488 |
+
const firstOpts = me.resolveDataElementOptions(start, mode);
|
489 |
+
const sharedOptions = me.getSharedOptions(firstOpts);
|
490 |
+
const includeOptions = me.includeOptions(mode, sharedOptions);
|
491 |
+
|
492 |
+
for (let i = 0; i < count; i++) {
|
493 |
+
const options = sharedOptions || me.resolveDataElementOptions(i, mode);
|
494 |
+
|
495 |
+
const baseProperties = me.calculateElementProperties(i, ruler, mode === 'reset', options);
|
496 |
+
const properties = {
|
497 |
+
...baseProperties,
|
498 |
+
datasetLabel: dataset.label || '',
|
499 |
+
lineWidth: dataset.lineWidth,
|
500 |
+
armLength: dataset.armLength,
|
501 |
+
armLengthRatio: dataset.armLengthRatio,
|
502 |
+
color: dataset.color,
|
503 |
+
};
|
504 |
+
|
505 |
+
if (includeOptions) {
|
506 |
+
properties.options = options;
|
507 |
+
}
|
508 |
+
me.updateElement(elements[i], i, properties, mode);
|
509 |
+
}
|
510 |
+
}
|
511 |
+
|
512 |
+
}
|
513 |
+
|
514 |
+
OhlcController.id = 'ohlc';
|
515 |
+
OhlcController.defaults = helpers.merge({
|
516 |
+
dataElementType: OhlcElement.id,
|
517 |
+
datasets: {
|
518 |
+
barPercentage: 1.0,
|
519 |
+
categoryPercentage: 1.0
|
520 |
+
}
|
521 |
+
}, chart_js.Chart.defaults.financial);
|
522 |
+
|
523 |
+
chart_js.Chart.register(CandlestickController, OhlcController, CandlestickElement, OhlcElement);
|
524 |
+
|
525 |
+
})));
|
static/js/index.js
ADDED
@@ -0,0 +1,274 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
[ jQuery ]
|
3 |
+
์น์ฌ์ดํธ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ๊ฒ ํ์ฉํ ์ ์๋๋ก ๋์์ฃผ๋ ์คํ์์ค ๊ธฐ๋ฐ์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
|
4 |
+
|
5 |
+
[ ์ต๊ทผ Trends + ๋น์ทํ ์ข
๋ฅ ]
|
6 |
+
1. Lodash
|
7 |
+
2. Moment
|
8 |
+
3. jQuery
|
9 |
+
4. date-fns
|
10 |
+
5. RxJS
|
11 |
+
*/
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
|
16 |
+
|
17 |
+
/*
|
18 |
+
".ready()"๋ DOM(Document Object Model)์ด ์์ ํ ๋ถ๋ฌ์์ง๋ฉด ์คํ๋๋ Event ์ด๋ค.
|
19 |
+
|
20 |
+
์ผ๋ฐ์ ์ผ๋ก ๋ธ๋ผ์ฐ์ ๊ฐ HTML์ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด์๋
|
21 |
+
๋จผ์ ๋ฌธ์ ๊ตฌ์กฐ๋ฅผ ๋ง๋ค๊ณ ๋ง๋ค์ด์ง ๋ฌธ์ ๊ตฌ์กฐ ์์ ๋์์ธ์ ์
ํ๋ ํ์์ ์ทจํ๋ค.
|
22 |
+
|
23 |
+
์ด ๊ณผ์ ์์ ๋์์ธ์ด ์
ํ์ง์ง ์์ ์ํ๋ก ๋ฌธ์ ๊ตฌ์กฐ๊ฐ ๋ง๋ค์ด์ง ์์ ์ ์ํ๋๋ Event๊ฐ ".ready()" ์ด๋ค.
|
24 |
+
|
25 |
+
|
26 |
+
jQuery 3.0 ๋ฒ์ ์ดํ๋ถํฐ๋ "$(handler)" ๊ตฌ๋ฌธ๋ง ๊ถ์ฅ
|
27 |
+
".read()" Event๋ 1.8 ๋ฒ์ ์์๋ deprecated ๋์์ผ๋ฉฐ 3.0์์๋ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ
|
28 |
+
( ๊ทผ๋ฐ ์ฌ๊ธฐ์๋ ์คํ ๋ฌธ์ ์๋จ )
|
29 |
+
*/
|
30 |
+
|
31 |
+
// $(document).ready(function() { });
|
32 |
+
$(function() {
|
33 |
+
console.log("Start debug !!");
|
34 |
+
|
35 |
+
// ์ธ ๋ฒ์งธ ์ธ์ : ํ์ด์ฌ์์ return ๊ฐ ์ฒ๋ฆฌ
|
36 |
+
sendAjax("/submit", {"input_text": "This is a news article about Apple."}, handleOutput);
|
37 |
+
|
38 |
+
stocksInit();
|
39 |
+
calendarInit();
|
40 |
+
});
|
41 |
+
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
|
47 |
+
function handleOutput(output) {
|
48 |
+
console.log(output["output"]);
|
49 |
+
}
|
50 |
+
function handleReturn(output) {
|
51 |
+
// console.log("handle return ");
|
52 |
+
|
53 |
+
// console.log("close : ", output.close);
|
54 |
+
// console.log("diff : ", output.diff);
|
55 |
+
// console.log("name[0] : ", output.name[0]);
|
56 |
+
|
57 |
+
return output;
|
58 |
+
}
|
59 |
+
|
60 |
+
|
61 |
+
|
62 |
+
function stocksInit() {
|
63 |
+
// Javascript -> Flask (Python) -> Javascript
|
64 |
+
output = sendAjax_async("/stocks", "json", handleReturn);
|
65 |
+
|
66 |
+
console.log("stocksInit() Output : ", output);
|
67 |
+
console.log(typeof output);
|
68 |
+
|
69 |
+
// Ticker ๊ธธ์ด ํ์ธํด๋ณด๊ธฐ
|
70 |
+
var object_length = Object.keys(output.ticker).length;
|
71 |
+
console.log(object_length);
|
72 |
+
|
73 |
+
|
74 |
+
// ๋๋๋ง HTML ์์ ์์ฑ
|
75 |
+
stocks = document.querySelector('.stocks');
|
76 |
+
stocks.innerHTML = '';
|
77 |
+
|
78 |
+
// "HTML"์ ์์ ์ถ๊ฐ
|
79 |
+
for (var i = 0; i < object_length; i++) {
|
80 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock ticker"></div>';
|
81 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock name">' + output.name[i] + '</div>';
|
82 |
+
if (output.diff[i] > 0) {
|
83 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock diff up">' + '+' + output.diff[i] + ' %' + '</div>';
|
84 |
+
}
|
85 |
+
else { stocks.innerHTML = stocks.innerHTML + '<div class="stock diff down">' + output.diff[i] + ' %' + '</div>'; }
|
86 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock open">' + output.open[i] + '</div>';
|
87 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock close">' + output.close[i] + '</div>';
|
88 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock sector">' + output.sector[i] + '</div>';
|
89 |
+
stocks.innerHTML = stocks.innerHTML + '<div class="stock industry">' + output.industry[i] + '</div>';
|
90 |
+
|
91 |
+
// Add ticker's chart link
|
92 |
+
link = String('"/'.concat(output.ticker[i], '"'));
|
93 |
+
stock = document.querySelectorAll('.stock.ticker')[i];
|
94 |
+
stock.innerHTML = '';
|
95 |
+
stock.innerHTML = stock.innerHTML + '<a href='.concat(link, '>') + output.ticker[i] + '</a>';
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
/*
|
106 |
+
๋ฌ๋ ฅ ๋ ๋๋ง ํ ๋ ํ์ํ ์ ๋ณด ๋ชฉ๋ก
|
107 |
+
|
108 |
+
ํ์ฌ ์ (์ด๊ธฐ๊ฐ : ํ์ฌ ์๊ฐ)
|
109 |
+
๊ธ์ ๋ง์ง๋ง์ผ ๋ ์ง์ ์์ผ
|
110 |
+
์ ์ ๋ง์ง๋ง์ผ ๋ ์ง์ ์์ผ
|
111 |
+
*/
|
112 |
+
|
113 |
+
function calendarInit() {
|
114 |
+
|
115 |
+
// ๋ ์ง ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
|
116 |
+
var date = new Date(); // ํ์ฌ ๋ ์ง(๋ก์ปฌ ๊ธฐ์ค) ๊ฐ์ ธ์ค๊ธฐ
|
117 |
+
var utc = date.getTime() + (date.getTimezoneOffset() * 60 * 1000); // utc ํ์ค์ ๋์ถ
|
118 |
+
var kstGap = 9 * 60 * 60 * 100; // ํ๊ตญ kst ๊ธฐ์ค์๊ฐ ๋ํ๊ธฐ
|
119 |
+
var today = new Date(utc + kstGap); // ํ๊ตญ ์๊ฐ์ผ๋ก date ๊ฐ์ฒด ๋ง๋ค๊ธฐ(์ค๋)
|
120 |
+
|
121 |
+
console.log("Today : ", today)
|
122 |
+
|
123 |
+
// ๋ฌ๋ ฅ์์ ํ๊ธฐํ๋ ๋ ์ง ๊ฐ์ฒด
|
124 |
+
var thisMonth = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
125 |
+
|
126 |
+
var currentYear = thisMonth.getFullYear(); // ๋ฌ๋ ฅ์์ ํ๊ธฐํ๋ ์ฐ
|
127 |
+
var currentMonth = thisMonth.getMonth(); // ๋ฌ๋ ฅ์์ ํ๊ธฐํ๋ ์
|
128 |
+
var currentDate = thisMonth.getDate(); // ๋ฌ๋ ฅ์์ ํ๊ธฐํ๋ ์ผ
|
129 |
+
|
130 |
+
// kst ๊ธฐ์ค ํ์ฌ์๊ฐ
|
131 |
+
console.log("thisMonth");
|
132 |
+
console.log(currentYear);
|
133 |
+
console.log(currentMonth); // monthIndex
|
134 |
+
console.log(currentDate);
|
135 |
+
console.log(thisMonth);
|
136 |
+
|
137 |
+
// ์บ๋ฆฐ๋ ๋๋๋ง
|
138 |
+
renderCalender(thisMonth);
|
139 |
+
|
140 |
+
|
141 |
+
////////////////////////////////////////////////////////////////
|
142 |
+
|
143 |
+
function renderCalender(thisMonth, help=0) {
|
144 |
+
|
145 |
+
// ๋๋๋ง์ ์ํ ๋ฐ์ดํฐ ์ ๋ฆฌ
|
146 |
+
currentYear = thisMonth.getFullYear();
|
147 |
+
currentMonth = thisMonth = thisMonth.getMonth();
|
148 |
+
if (help != 1) {
|
149 |
+
// currentDate = thisMonth.getDate(); // 1 - 31 : 1 - 31
|
150 |
+
currentDate = new Date(today.getFullYear(), today.getMonth(), today.getDate()).getDate();
|
151 |
+
}
|
152 |
+
|
153 |
+
// ์ด์ ๋ฌ์ ๋ง์ง๋ง๋ ๋ ์ง์ ์์ผ ๊ตฌํ๊ธฐ
|
154 |
+
var startDay = new Date(currentYear, currentMonth, 0);
|
155 |
+
var prevDate = startDay.getDate(); // 1 - 31 : 1 - 31
|
156 |
+
var prevDay = startDay.getDay(); // Sunday - Saturday : 0 - 6
|
157 |
+
|
158 |
+
// ์ด๋ฒ ๋ฌ์ ๋ง์ง๋ง๋ ๋ ์ง์ ์์ผ ๊ตฌํ๊ธฐ
|
159 |
+
var endDay = new Date(currentYear, currentMonth + 1 , 0);
|
160 |
+
var nextDate = endDay.getDate(); // 1 - 31 : 1 - 31
|
161 |
+
var nextDay = endDay.getDay(); // Sunday - Saturday : 0 - 6
|
162 |
+
|
163 |
+
// console.log(prevDate, prevDay, nextDate, nextDay, currentMonth);
|
164 |
+
|
165 |
+
// ํ์ฌ ์ ํ๊ธฐ
|
166 |
+
$('.year-month').text(currentYear + '.' + (currentMonth + 1));
|
167 |
+
|
168 |
+
// ๋๋๋ง html ์์ ์์ฑ
|
169 |
+
calendar = document.querySelector('.dates')
|
170 |
+
calendar.innerHTML = '';
|
171 |
+
|
172 |
+
// ์ง๋๋ฌ
|
173 |
+
for (var i = prevDate - prevDay + 1; i <= prevDate; i++) {
|
174 |
+
calendar.innerHTML = calendar.innerHTML + '<div class="day prev disable">' + i + '</div>'
|
175 |
+
}
|
176 |
+
|
177 |
+
// ์ด๋ฒ๋ฌ
|
178 |
+
for (var i = 1; i <= nextDate; i++) {
|
179 |
+
calendar.innerHTML = calendar.innerHTML + '<div class="day current">' + i + '</div>'
|
180 |
+
}
|
181 |
+
|
182 |
+
// ๋ค์๋ฌ
|
183 |
+
for (var i = 1; i <= (7 - nextDay == 7 ? 0 : 7 - nextDay); i++) {
|
184 |
+
calendar.innerHTML = calendar.innerHTML + '<div class="day next disable">' + i + '</div>'
|
185 |
+
}
|
186 |
+
|
187 |
+
// ์ค๋ ๋ ์ง ํ๊ธฐ
|
188 |
+
if (today.getMonth() == currentMonth) {
|
189 |
+
todayDate = today.getDate();
|
190 |
+
var currentMonthDate = document.querySelectorAll('.dates .current'); // ์ค๊ฐ ๋์ด์ฐ๊ธฐ ์ฃผ์
|
191 |
+
// var currentMonthDate = document.querySelectorAll('div.day.current'); // ๊ฐ์ ์๋ฏธ
|
192 |
+
|
193 |
+
// console.log(currentMonthDate)
|
194 |
+
currentMonthDate[todayDate-1].classList.add('today');
|
195 |
+
}
|
196 |
+
|
197 |
+
// ์ด์ ๋ฌ๋ก ์ด๋
|
198 |
+
$('.go-prev').on('click', function() {
|
199 |
+
if (help == 0) {
|
200 |
+
thisMonth = new Date(currentYear, currentMonth - 1, 1);
|
201 |
+
renderCalender(thisMonth, 1);
|
202 |
+
}
|
203 |
+
else {
|
204 |
+
renderCalender(thisMonth, 1);
|
205 |
+
}
|
206 |
+
})
|
207 |
+
|
208 |
+
// ๋ค์๋ฌ๋ก ์ด๋
|
209 |
+
$('.go-next').on('click', function() {
|
210 |
+
if (help == 0) {
|
211 |
+
thisMonth = new Date(currentYear, currentMonth + 1, 1);
|
212 |
+
renderCalender(thisMonth, 1);
|
213 |
+
}
|
214 |
+
else {
|
215 |
+
renderCalender(thisMonth, 1);
|
216 |
+
}
|
217 |
+
})
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
|
222 |
+
|
223 |
+
|
224 |
+
|
225 |
+
|
226 |
+
/**
|
227 |
+
*
|
228 |
+
* @param {string} url from javascript to flask(python) with route
|
229 |
+
* @param {dictionary} data from javascript to flask(python) with data
|
230 |
+
* @param {function} handle ํฐ ์๋ฏธ ์์
|
231 |
+
*/
|
232 |
+
function sendAjax(url, data, handle) {
|
233 |
+
/*
|
234 |
+
jQuery.getJSON(url, [, data], [, success])
|
235 |
+
|
236 |
+
Load JSON-encoded data from the server using a GET HTTP request.
|
237 |
+
*/
|
238 |
+
|
239 |
+
$.getJSON(url, data,
|
240 |
+
function(response) {
|
241 |
+
handle(response.result);
|
242 |
+
});
|
243 |
+
}
|
244 |
+
|
245 |
+
|
246 |
+
/**
|
247 |
+
*
|
248 |
+
* @param {string} url from javascript to flask(python) with route
|
249 |
+
* @param {dictionary} data from javascript to flask(python) with data
|
250 |
+
* @param {string} dataType The type of data that you're expecting back from the server. (ex. "json")
|
251 |
+
* @param {function} handle ํฐ ์๋ฏธ ์์
|
252 |
+
* @returns from flask(python) to javascript with data
|
253 |
+
*/
|
254 |
+
function sendAjax_async(url, data, dataType, handle) {
|
255 |
+
/*
|
256 |
+
jQuery.ajax(url, [, settings])
|
257 |
+
|
258 |
+
jQuery.getJSON => Asynchronous (๋น๋๊ธฐ์)
|
259 |
+
|
260 |
+
Synchronous => ๋๊ธฐ์ : ์ฝ๋ ์์๋๋ก ์งํ
|
261 |
+
*/
|
262 |
+
|
263 |
+
var search_var;
|
264 |
+
console.log("Internal : sendAjax async");
|
265 |
+
|
266 |
+
$.ajax(url=url, settings={data: data, dataType: dataType, async: false,
|
267 |
+
success: function(response) {
|
268 |
+
console.log("Success : ", typeof response);
|
269 |
+
search_var = handle(response.result); // handle, ํฐ ์๋ฏธ ์์
|
270 |
+
}
|
271 |
+
});
|
272 |
+
|
273 |
+
return search_var
|
274 |
+
}
|
static/js/news.js
ADDED
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// jQuery
|
2 |
+
// $(document).ready(function() { });
|
3 |
+
$(function() {
|
4 |
+
console.log("Start News's Title !");
|
5 |
+
newsInit();
|
6 |
+
});
|
7 |
+
|
8 |
+
|
9 |
+
|
10 |
+
|
11 |
+
|
12 |
+
function newsInit() {
|
13 |
+
// Ticker ์ด๋ฆ ๊ฐ์ ธ์ค๊ธฐ
|
14 |
+
// ํด๋น class์ text ๊ฐ์ ธ์ค๊ธฐ
|
15 |
+
let ticker = document.querySelector('.goticker .tickerName').textContent;
|
16 |
+
let date = document.querySelector('.titleDate').textContent;
|
17 |
+
let title = document.querySelector('.titleName').textContent;
|
18 |
+
let url = document.querySelector('.NewsURL').textContent;
|
19 |
+
|
20 |
+
ticker = ticker;
|
21 |
+
date = date.substring(6, date.length);
|
22 |
+
title = title.substring(7, title.length);
|
23 |
+
url = url.substring(5, url.length);
|
24 |
+
|
25 |
+
console.log(ticker);
|
26 |
+
console.log(date);
|
27 |
+
console.log(title);
|
28 |
+
console.log(url);
|
29 |
+
|
30 |
+
|
31 |
+
//////////////////////////////////////////////////////////////////////
|
32 |
+
|
33 |
+
// Javascript๋ฅผ ์ด์ฉํด HTML์ ๋์ ์ผ๋ก ํ๊ทธ ์ถ๊ฐ
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
// a ํ๊ทธ onclick ์ ์ฉ
|
38 |
+
const goticker = document.querySelector('.goticker');
|
39 |
+
let stockURL = '/'.concat(ticker);
|
40 |
+
goticker.setAttribute('href', stockURL);
|
41 |
+
|
42 |
+
|
43 |
+
// a ํ๊ทธ์ URL ์ ์ฉ
|
44 |
+
const addURL = document.querySelector('.NewsURL .input-News-URL');
|
45 |
+
addURL.setAttribute('href', url);
|
46 |
+
|
47 |
+
|
48 |
+
// ๋ชจ๋ธ์์ ์ง๋ฌธ ์์ Ticker์ ์๋ง๊ฒ ์์ฑํ๊ธฐ
|
49 |
+
const example_value = document.querySelector('#model .text-form #text-input');
|
50 |
+
example = "Why did " + ticker + "'s stock go down?";
|
51 |
+
example_value.setAttribute('value', example);
|
52 |
+
|
53 |
+
|
54 |
+
//////////////////////////////////////////////////////////////////////
|
55 |
+
// NER ๊ด๋ จ
|
56 |
+
|
57 |
+
ents = sendAjax_async('/ner', {'ticker': ticker, 'date': date, 'title': title}, dataType="json", handle=handleReturn);
|
58 |
+
// ents = {'text': [], 'start_char': [], 'end_char': [], 'label_': [], 'news': []}
|
59 |
+
console.log(ents);
|
60 |
+
|
61 |
+
let news = ents['news'];
|
62 |
+
let numOfNER = ents['text'].length;
|
63 |
+
|
64 |
+
|
65 |
+
// ๋๋๋ง html ์์ ์์ฑ
|
66 |
+
news_ner = document.querySelector('.entities');
|
67 |
+
news_ner.innerHTML = '';
|
68 |
+
|
69 |
+
for (i=0; i<numOfNER-1; i++) {
|
70 |
+
start_idx = (i == 0) ? 0 : ents['end_char'][i-1];
|
71 |
+
end_idx = ents['start_char'][i];
|
72 |
+
last_idx = ents['end_char'][i];
|
73 |
+
|
74 |
+
label = ents['label_'][i];
|
75 |
+
if (label == 'ORG') { class_name = "entity_org"; }
|
76 |
+
else if (label == 'PERSON') { class_name = "entity_person"; }
|
77 |
+
else if (label == 'FAC') { class_name = "entity_fac"; }
|
78 |
+
else if (label == 'GPE') { class_name = "entity_gpe"; }
|
79 |
+
else if (label == 'PRODUCT') { class_name = "entity_product"; }
|
80 |
+
else { console.log("[ Error !!! - New NER label_ ] : ", ents['label_'][i], ents['text'][i]); class_name = "none"; }
|
81 |
+
|
82 |
+
news_ner.innerHTML = news_ner.innerHTML + news.substring(start_idx, end_idx);
|
83 |
+
news_ner.innerHTML = news_ner.innerHTML + '<mark class=' + class_name
|
84 |
+
+ ' style="margin: 0 0.25em; line-height: 1;">'
|
85 |
+
+ news.substring(end_idx, last_idx)
|
86 |
+
+ '<span class="show-label" style="font-size: 0.8em; font-weight: bold; line-height: 1; border-radius: 0.35em; vertical-align: middle; margin-left: 0.5rem">'
|
87 |
+
+ label + '</span></mark>';
|
88 |
+
}
|
89 |
+
news_ner.innerHTML = news_ner.innerHTML + news.substring(ents['end_char'][numOfNER-1]);
|
90 |
+
|
91 |
+
|
92 |
+
|
93 |
+
|
94 |
+
|
95 |
+
|
96 |
+
|
97 |
+
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
|
102 |
+
//////////////////////////////////////////////////////////////////////
|
103 |
+
// ๋ชจ๋ธ ์ ์ฉ ๋ด์ฉ ( Submit )
|
104 |
+
|
105 |
+
var sendTitle = title; // Javascript -> Python ๋ณด๋ด๊ธฐ ์ํ title
|
106 |
+
|
107 |
+
|
108 |
+
// title์์ & ํ์๊ฐ ์์ ์ ์์.
|
109 |
+
// Title ์์ '&'๋ก ํ์๋์ด ์๋๋ฐ ๋ฐ๋ก ๊ตฌ๋ณํด์ผ ๋๋ค.
|
110 |
+
// andSymbolInTitle ์์ ๊ฐ์ ธ์จ '&' ์์น index๋ฅผ title๊ณผ ํฉ์ณ์ค๋ค.
|
111 |
+
andSymbolInTitle = [];
|
112 |
+
let idx = 0;
|
113 |
+
// title = "asdf&asdf&AS&DF&&";
|
114 |
+
// sendTitle = title;
|
115 |
+
|
116 |
+
while (true) {
|
117 |
+
idx = sendTitle.indexOf('&', idx);
|
118 |
+
if (idx == -1) { break; }
|
119 |
+
sendTitle = sendTitle.substring(0, idx) + sendTitle.substring(idx+1, sendTitle.length);
|
120 |
+
console.log(sendTitle);
|
121 |
+
andSymbolInTitle.push(idx + andSymbolInTitle.length);
|
122 |
+
}
|
123 |
+
|
124 |
+
console.log(andSymbolInTitle);
|
125 |
+
console.log("Last String", sendTitle);
|
126 |
+
|
127 |
+
|
128 |
+
|
129 |
+
// function ์์ "async"๋ฅผ ๋ถ์ด๋ฉด ํด๋น ํจ์๋ ํญ์ ํ๋ผ๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค.
|
130 |
+
const translateText = async (text) => {
|
131 |
+
// ๋ชฉ์ : Flask์ input์ ๋ณด๋ด์ฃผ๊ณ output์ ๋ฐ์์ค๋ ๊ณผ์
|
132 |
+
console.log("Start translateText async");
|
133 |
+
|
134 |
+
// "await"๋ "async" ํจ์ ์์์๋ง ๋์ํ๋ค.
|
135 |
+
// "await" ํค์๋๋ฅผ ๋ง๋๋ฉด Promise๊ฐ ์ฒ๋ฆฌ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค.
|
136 |
+
// Promise๊ฐ ์ฒ๋ฆฌ๋๊ธธ ๊ธฐ๋ค๋ฆฌ๋ ๋์์ ์์ง์ด ๋ค๋ฅธ์ผ(๋ค๋ฅธ ์คํฌ๋ฆฝํธ๋ฅผ ์คํ, ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฑ)์ ํ ์ ์๊ธฐ ๋๋ฌธ์, CPU ๋ฆฌ์์ค๊ฐ ๋ญ๋น๋์ง ์๋๋ค.
|
137 |
+
const inferResponse = await fetch(`newsQuestions?ticker=${ticker}&date=${date}&title=${sendTitle}&andSymbolInTitle=${andSymbolInTitle}&questions=${text}`); // Javascript -> Flask(python)
|
138 |
+
|
139 |
+
// console.log("inferResponse : ", inferResponse);
|
140 |
+
|
141 |
+
const inferJson = await inferResponse.json(); // Flask(python) -> Javascript
|
142 |
+
|
143 |
+
// console.log(inferJson);
|
144 |
+
return inferJson.result['answer'];
|
145 |
+
};
|
146 |
+
|
147 |
+
|
148 |
+
/* ๋ชจ๋ธ Submit button ๊ด๋ จ ๋ด์ฉ */
|
149 |
+
// form ํ๊ทธ์ class ์ด๋ฆ
|
150 |
+
const textForm = document.querySelector('.text-form');
|
151 |
+
|
152 |
+
|
153 |
+
// addEventListener(type, listener)
|
154 |
+
// addEventListener(type, listener, options)
|
155 |
+
// addEventListener(type, listener, useCapture)
|
156 |
+
textForm.addEventListener('submit', async (event) => {
|
157 |
+
event.preventDefault();
|
158 |
+
// console.log(event);
|
159 |
+
|
160 |
+
// html -> javascript : input ๋ฐ์์์ output ๋ณด๋ด๊ธฐ
|
161 |
+
const textInput = document.getElementById('text-input');
|
162 |
+
const textParagraph = document.querySelector('.text-output');
|
163 |
+
|
164 |
+
console.log("textInput : ", textInput, textInput.value);
|
165 |
+
try {
|
166 |
+
// sendAjax("/inference", {"input_text" : textInput.value}, handleOutput);
|
167 |
+
|
168 |
+
// "await"๋ "async" ํจ์ ์์์๋ง ๋์ํ๋ค.
|
169 |
+
// "await" ํค์๋๋ฅผ ๋ง๋๋ฉด Promise๊ฐ ์ฒ๋ฆฌ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ๋ค.
|
170 |
+
// Promise๊ฐ ์ฒ๋ฆฌ๋๊ธธ ๊ธฐ๋ค๋ฆฌ๋ ๋์์ ์์ง์ด ๋ค๋ฅธ์ผ(๋ค๋ฅธ ์คํฌ๋ฆฝํธ๋ฅผ ์คํ, ์ด๋ฒคํธ ์ฒ๋ฆฌ ๋ฑ)์ ํ ์ ์๊ธฐ ๋๋ฌธ์, CPU ๋ฆฌ์์ค๊ฐ ๋ญ๋น๋์ง ์๋๋ค.
|
171 |
+
const answer = await translateText(textInput.value); // Flask์ input์ ๋ณด๋ด์ฃผ๊ณ output์ ๋ฐ์์ค๋ ๊ณผ์
|
172 |
+
|
173 |
+
console.log("Answer : ", answer);
|
174 |
+
textParagraph.textContent = answer;
|
175 |
+
} catch (err) {
|
176 |
+
console.error(err);
|
177 |
+
}
|
178 |
+
});
|
179 |
+
|
180 |
+
|
181 |
+
|
182 |
+
|
183 |
+
|
184 |
+
|
185 |
+
}
|
186 |
+
|
187 |
+
|
188 |
+
|
189 |
+
|
190 |
+
|
191 |
+
|
192 |
+
|
193 |
+
|
194 |
+
|
195 |
+
|
196 |
+
|
197 |
+
|
198 |
+
|
199 |
+
|
200 |
+
/**
|
201 |
+
*
|
202 |
+
* @param {string} url from javascript to flask(python) with route
|
203 |
+
* @param {dictionary} data from javascript to flask(python) with data
|
204 |
+
* @param {function} handle ํฐ ์๋ฏธ ์์
|
205 |
+
*/
|
206 |
+
function sendAjax(url, data, handle) {
|
207 |
+
/*
|
208 |
+
jQuery.getJSON(url, [, data], [, success])
|
209 |
+
|
210 |
+
Load JSON-encoded data from the server using a GET HTTP request.
|
211 |
+
*/
|
212 |
+
|
213 |
+
$.getJSON(url, data,
|
214 |
+
function(response) {
|
215 |
+
handle(response.result);
|
216 |
+
});
|
217 |
+
}
|
218 |
+
|
219 |
+
|
220 |
+
/**
|
221 |
+
*
|
222 |
+
* @param {string} url from javascript to flask(python) with route
|
223 |
+
* @param {dictionary} data from javascript to flask(python) with data
|
224 |
+
* @param {string} dataType The type of data that you're expecting back from the server. (ex. "json")
|
225 |
+
* @param {function} handle ํฐ ์๋ฏธ ์์
|
226 |
+
* @returns from flask(python) to javascript with data
|
227 |
+
*/
|
228 |
+
function sendAjax_async(url, data, dataType, handle) {
|
229 |
+
/*
|
230 |
+
jQuery.ajax(url, [, settings])
|
231 |
+
|
232 |
+
jQuery.getJSON => Asynchronous (๋น๋๊ธฐ์)
|
233 |
+
|
234 |
+
Synchronous => ๋๊ธฐ์ : ์ฝ๋ ์์๋๋ก ์งํ
|
235 |
+
*/
|
236 |
+
|
237 |
+
var search_var;
|
238 |
+
console.log("Internal : sendAjax async");
|
239 |
+
|
240 |
+
$.ajax(url=url, settings={data: data, dataType: dataType, async: false,
|
241 |
+
success: function(response) {
|
242 |
+
console.log("Success : ", typeof response);
|
243 |
+
search_var = handle(response.result); // handle, ํฐ ์๋ฏธ ์์
|
244 |
+
}
|
245 |
+
});
|
246 |
+
|
247 |
+
return search_var
|
248 |
+
}
|
templates/chart.html
ADDED
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- html ํ๊ทธ : HTML๋ก ์์ฑ๋์ด ์๋ค๋ ๊ฒ์ ์๋ ค์ค -->
|
2 |
+
<!-- html ํ๊ทธ : html ํ์ผ ์ ์ฒด๋ฅผ ๊ฐ์ธ๋ ํ๊ทธ -->
|
3 |
+
<html>
|
4 |
+
<!-- head ํ๊ทธ : ๋จธ๋ฆฌ๋ง์ ํด๋น -->
|
5 |
+
<!-- head ํ๊ทธ : css๋ javascript๋ฅผ ์ฐ๊ฒฐํด์ค -->
|
6 |
+
<!-- head ํ๊ทธ : ํ๋น์ฝ์ด๋ ๋ฌธ์์ด ์ธ์ฝ๋ฉ๊ณผ ๊ฐ์ ๋ฌธ์์ ๋ค์ํ ์ ๋ณด๋ฅผ ์ ๊ณต -->
|
7 |
+
<head>
|
8 |
+
|
9 |
+
<!-- link ํ๊ทธ : ์ฃผ๋ก ์ธ๋ถ css ํ์ผ์ ์ฐ๊ฒฐํ ๋ ์ฌ์ฉ -->
|
10 |
+
<link rel="stylesheet" href="static/css/chartStyle.css" />
|
11 |
+
|
12 |
+
|
13 |
+
<!-- jQuery -->
|
14 |
+
<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>
|
15 |
+
<!-- script ํ๊ทธ : ์ธ๋ถ js ํ์ผ์ ์ฐ๊ฒฐํ๊ฑฐ๋ javascript ์ฝ๋๋ฅผ ์
๋ ฅํ ๋ ์ฌ์ฉ -->
|
16 |
+
<script type="text/javascript" src="static/js/chartIndex.js"></script>
|
17 |
+
|
18 |
+
|
19 |
+
|
20 |
+
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
<!-- Recharts -->
|
25 |
+
<!-- <script src="https://unpkg.com/react/umd/react.production.min.js"></script>
|
26 |
+
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
|
27 |
+
|
28 |
+
<script src="https://unpkg.com/prop-types/prop-types.min.js"></script>
|
29 |
+
<script src="https://unpkg.com/recharts/umd/Recharts.js"></script> -->
|
30 |
+
|
31 |
+
<!-- <script src="https://unpkg.com/recharts/umd/Recharts.min.js"></script> -->
|
32 |
+
|
33 |
+
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
<script src="https://cdn.jsdelivr.net/npm/luxon@1.26.0"></script>
|
39 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.1/dist/chart.js"></script>
|
40 |
+
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.0.0"></script>
|
41 |
+
<script src="static/js/chartjs-chart-financial.js" type="text/javascript"></script>
|
42 |
+
|
43 |
+
|
44 |
+
|
45 |
+
|
46 |
+
<!-- ๋ณด๋ฅ ๋ชจ๋ฅด๊ฒ ์ ... -->
|
47 |
+
|
48 |
+
<!-- ========================================================================================= -->
|
49 |
+
|
50 |
+
|
51 |
+
<!-- Include Chart.js from a CDN -->
|
52 |
+
|
53 |
+
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.2.1/chart.min.js"></script> -->
|
54 |
+
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.2.1/chart.js"></script> -->
|
55 |
+
<!-- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> -->
|
56 |
+
|
57 |
+
|
58 |
+
<!-- <script src="https://cdn.jsdelivr.net/npm/luxon@1.26.0"></script>
|
59 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.1/dist/chart.js"></script>
|
60 |
+
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.0.0"></script>
|
61 |
+
|
62 |
+
|
63 |
+
<script src="https://cdn.jsdelivr.net/npm/luxon@1.26.0"></script>
|
64 |
+
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.0.1/dist/chart.js"></script>
|
65 |
+
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.0.0"></script>
|
66 |
+
|
67 |
+
<script src="https://www.chartjs.org/chartjs-chart-financial/chartjs-chart-financial.js"></script> -->
|
68 |
+
|
69 |
+
|
70 |
+
|
71 |
+
<!--
|
72 |
+
CDN : Content Delivery Network
|
73 |
+
์ง๋ฆฌ์ ์ ์ฝ ์์ด ์ ์ธ๊ณ ์ฌ์ฉ์์๊ฒ ๋น ๋ฅด๊ณ ์์ ํ๊ฒ ์ฝํ
์ธ ๋ฅผ ์ ์กํ ์ ์๋ '์ฝํ
์ธ ์ ์ก ๊ธฐ์ ์ ์๋ฏธ'
|
74 |
+
์๋ฒ์ ์ฌ์ฉ์ ์ฌ์ด์ ๋ฌผ๋ฆฌ์ ์ธ ๊ฑฐ๋ฆฌ๋ฅผ ์ค์ฌ ์ฝํ
์ธ ๋ก๋ฉ์ ์์๋๋ ์๊ฐ์ ์ต์ํํ๋ค.
|
75 |
+
-->
|
76 |
+
</head>
|
77 |
+
|
78 |
+
|
79 |
+
|
80 |
+
|
81 |
+
|
82 |
+
<!-- body ํ๊ทธ : ๋ณธ๋ฌธ์ ํด๋นํ๋ ๋ถ๋ถ, ์ค์ ๋ณด์ฌ์ง๋ ํ๋ฉด์ ํด๋น -->
|
83 |
+
<body>
|
84 |
+
<h1><a class="gohome" href="/">Stock News Summaries AI</a></h1>
|
85 |
+
<a class="goticker"><h2 class="tickerName">{{embed}}</h2></a>
|
86 |
+
<h3 class="practice"></h3>
|
87 |
+
|
88 |
+
<div>
|
89 |
+
<div id="chart-container" width="974" height="486"></div>
|
90 |
+
|
91 |
+
<div class="myChart-container">
|
92 |
+
<canvas id="myChart"></canvas>
|
93 |
+
</div>
|
94 |
+
|
95 |
+
</div>
|
96 |
+
|
97 |
+
|
98 |
+
|
99 |
+
|
100 |
+
|
101 |
+
<!--
|
102 |
+
[ ์์ฑ ]
|
103 |
+
|
104 |
+
align : ์ ๋ ฌ์ ์ง์ ํ๋ค. (left, center, right)
|
105 |
+
border : ํ
๋๋ฆฌ ์ ์ ๋๊ป๋ฅผ ์ง์ ํ๋ค.
|
106 |
+
bgcolor : ๋ฐฐ๊ฒฝ์์ ์ง์ ํ๋ค. (์์ "red", "black" ์ฒ๋ผ ๊ธฐ์กด์ ์ ์๋์ด์๋ ์์ ์ฌ์ฉํ ์๋ ์์ผ๋ฉฐ
|
107 |
+
rgbํ์์ #000000 ์ผ๋ก๋ ์์ ์ง์ ํ ์ ์๋ค.)
|
108 |
+
bordercolor : ํ
๋๋ฆฌ ์ ์ ์์ ์ง์ ํ๋ค. ์์ ์ง์ ํ๋ ๋ฐฉ๋ฒ์ bgcolor์ ๋์ผํ๋ค.
|
109 |
+
cellspacing : ์
๊ฐ์ ๊ฐ๊ฒฉ์ ์ง์ ํ๋ค.
|
110 |
+
width : ๊ฐ๋ก ๊ธธ์ด๋ฅผ ์ง์ ํ๋ค. (์์ ๊ฐ์ ์
๋ ฅํ ์๋, % ๋จ์๋ก ์
๋ ฅํ ์ ์๋ค.
|
111 |
+
%๋ฅผ ์ฌ์ฉํ ๋๋ ์น๋ธ๋ผ์ฐ์ ํฌ๊ธฐ์ ๋ํ % ์ด๋ค.)
|
112 |
+
height : ์ธ๋ก ๊ธธ์ด๋ฅผ ์ง์ ํ๋ค.
|
113 |
+
rawspan : ์ง์ ํ ๊ฐ๋งํผ ํ์ ๋ณํฉํ๋ค. (์์๋๋ก)
|
114 |
+
colspan : ์ง์ ํ ๊ฐ๋งํผ ์ด์ ๋ณํฉํ๋ค. (์ข์ฐ๋ก)
|
115 |
+
-->
|
116 |
+
|
117 |
+
|
118 |
+
|
119 |
+
|
120 |
+
|
121 |
+
|
122 |
+
|
123 |
+
<!-- table ํ๊ทธ : ํ
์ด๋ธ์ ๋ง๋ ๋ค. -->
|
124 |
+
<table class="table"
|
125 |
+
|
126 |
+
text-align="center"
|
127 |
+
align-items="center"
|
128 |
+
justify-content="center"
|
129 |
+
|
130 |
+
border="1"
|
131 |
+
width="90%"
|
132 |
+
height="200"
|
133 |
+
cellspacing="5">
|
134 |
+
|
135 |
+
|
136 |
+
<!-- caption ํ๊ทธ : ํ
์ด๋ธ ์ด๋ฆ ํ์ -->
|
137 |
+
<caption class="table-title"></caption>
|
138 |
+
|
139 |
+
<!-- ํ
์ด๋ธ์ ํค๋ ์์ญ ์ง์ -->
|
140 |
+
<thread>
|
141 |
+
<!-- tr ํ๊ทธ : ํ
์ด๋ธ์ ํ(๊ฐ๋ก ํ์ค)์ ๋ง๋ ๋ค. -->
|
142 |
+
<tr align="center" bgcolor="white">
|
143 |
+
<!-- td ํ๊ทธ : ํ
์ด๋ธ์ ์ด์ ๋ง๋ ๋ค. -->
|
144 |
+
<td width="5%"></td>
|
145 |
+
|
146 |
+
<!-- th ํ๊ทธ : ํ
์ด๋ธ(ํ)์ ํค๋ ๋ถ๋ถ(์๋์ผ๋ก ๊ฐ์ด๋ฐ ์ ๋ ฌ, ๊ตต๊ฒ ์ ์ฉ) -->
|
147 |
+
<th width="10%">Date</th>
|
148 |
+
<th width="10%">Diff</th>
|
149 |
+
<th class="title-width">Articles</th>
|
150 |
+
</tr>
|
151 |
+
</thread>
|
152 |
+
|
153 |
+
<!-- tbody ํ๊ทธ : -->
|
154 |
+
<tbody class="news-table">
|
155 |
+
|
156 |
+
<!-- News: Date, Diff, Title ์ถ๊ฐ -->
|
157 |
+
|
158 |
+
</tbody>
|
159 |
+
|
160 |
+
</table>
|
161 |
+
|
162 |
+
|
163 |
+
</body>
|
164 |
+
</html>
|
templates/index.html
ADDED
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- html ํ๊ทธ : HTML๋ก ์์ฑ๋์ด ์๋ค๋ ๊ฒ์ ์๋ ค์ค -->
|
2 |
+
<!-- html ํ๊ทธ : html ํ์ผ ์ ์ฒด๋ฅผ ๊ฐ์ธ๋ ํ๊ทธ -->
|
3 |
+
<html>
|
4 |
+
|
5 |
+
|
6 |
+
<!-- head ํ๊ทธ : ๋จธ๋ฆฌ๋ง์ ํด๋น -->
|
7 |
+
<!-- head ํ๊ทธ : css๋ javascript๋ฅผ ์ฐ๊ฒฐํด์ค -->
|
8 |
+
<!-- head ํ๊ทธ : ํ๋น์ฝ์ด๋ ๋ฌธ์์ด ์ธ์ฝ๋ฉ๊ณผ ๊ฐ์ ๋ฌธ์์ ๋ค์ํ ์ ๋ณด๋ฅผ ์ ๊ณต -->
|
9 |
+
<head>
|
10 |
+
|
11 |
+
<!-- link ํ๊ทธ : ์ฃผ๋ก ์ธ๋ถ css ํ์ผ์ ์ฐ๊ฒฐํ ๋ ์ฌ์ฉ -->
|
12 |
+
<link rel="stylesheet" href="static/css/style.css" />
|
13 |
+
|
14 |
+
<!-- jQuery -->
|
15 |
+
<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>
|
16 |
+
<!-- script ํ๊ทธ : ์ธ๋ถ js ํ์ผ์ ์ฐ๊ฒฐํ๊ฑฐ๋ javascript ์ฝ๋๋ฅผ ์
๋ ฅํ ๋ ์ฌ์ฉ -->
|
17 |
+
<script type="text/javascript" src="static/js/index.js"></script>
|
18 |
+
|
19 |
+
</head>
|
20 |
+
|
21 |
+
|
22 |
+
|
23 |
+
|
24 |
+
<!-- body ํ๊ทธ : ๋ณธ๋ฌธ์ ํด๋นํ๋ ๋ถ๋ถ, ์ค์ ๋ณด์ฌ์ง๋ ํ๋ฉด์ ํด๋น -->
|
25 |
+
<body>
|
26 |
+
<h1><a class="gohome" href="" onclick="sendAjax('/', undefined, undefined);"> Stock News NER & Analysis </a></h1>
|
27 |
+
<p id='embed'>{{embed}}</p>
|
28 |
+
|
29 |
+
<p id='mylog'/>
|
30 |
+
|
31 |
+
|
32 |
+
<!-- [ id class ์ฐจ์ด ] -->
|
33 |
+
<!-- id : ํ ๋ฌธ์์ ๋จ ํ๋์ ์์์๋ง ์ ์ฉ(์ค๋ณตX) -->
|
34 |
+
<!-- id : ํน์ ์์์ ์ด๋ฆ์ ๋ถ์ด๋๋ฐ ์ฌ์ฉ -->
|
35 |
+
<!-- class : ๋์ผํ ๊ฐ์ ๊ฐ๋ ์์ ๋ง์ -->
|
36 |
+
<!-- class : ์คํ์ผ์ ๋ถ๋ฅ์ ์ฌ์ฉ -->
|
37 |
+
|
38 |
+
|
39 |
+
<div class="stocks_wrap">
|
40 |
+
<div class="stocks_columns">
|
41 |
+
<div class="column ticker">Ticker</div>
|
42 |
+
<div class="column name">Name</div>
|
43 |
+
<div class="column dff">Diff</div>
|
44 |
+
<div class="column open">Open</div>
|
45 |
+
<div class="column close">Close</div>
|
46 |
+
<div class="column sector">Sector</div>
|
47 |
+
<div class="column industry">Industry</div>
|
48 |
+
</div>
|
49 |
+
|
50 |
+
<div class="stocks"></div>
|
51 |
+
</div>
|
52 |
+
|
53 |
+
|
54 |
+
|
55 |
+
|
56 |
+
|
57 |
+
|
58 |
+
<!-- div ํ๊ทธ : ์์๋ฅผ ๋ง๋ฌ -->
|
59 |
+
<div class="sec_cal">
|
60 |
+
|
61 |
+
<div class="cal_nav">
|
62 |
+
<!-- a ํ๊ทธ : ํด๋ฆญ์ ๋ค๋ฅธ ์ฃผ์๋ก ์ด๋ํ๋ ํ๊ทธ -->
|
63 |
+
<!-- a href๋ ํต์ URL์ ๋ช
์ํ ๋ ์ฌ์ฉํ์ง๋ง javascript๋ฅผ ์ฌ์ฉํ ๋๋ ์ด๋ค. -->
|
64 |
+
<a href="javascript:;" class="nav-btn go-prev">prev</a>
|
65 |
+
<div class="year-month"></div>
|
66 |
+
<a href="javascript:;" class="nav-btn go-next">next</a>
|
67 |
+
</div>
|
68 |
+
|
69 |
+
<div class="cal_wrap">
|
70 |
+
<div class="days">
|
71 |
+
<div class="day">MON</div>
|
72 |
+
<div class="day">TUE</div>
|
73 |
+
<div class="day">WED</div>
|
74 |
+
<div class="day">THU</div>
|
75 |
+
<div class="day">FRI</div>
|
76 |
+
<div class="day">SAT</div>
|
77 |
+
<div class="day">SUN</div>
|
78 |
+
</div>
|
79 |
+
|
80 |
+
<div class="dates"></div>
|
81 |
+
<!--
|
82 |
+
<div class="day prev disable">
|
83 |
+
<div class="day current"> or <div class="day current today">
|
84 |
+
<div class="day next disable">
|
85 |
+
-->
|
86 |
+
</div>
|
87 |
+
|
88 |
+
|
89 |
+
<form class="year-month-day-form">
|
90 |
+
<label for="year-month-day-input">๋ ์ง๋ฅผ ์
๋ ฅํ์์ค. (์์. 2023-02-10) </label>
|
91 |
+
<input
|
92 |
+
id="year-month-day-input"
|
93 |
+
type="text"
|
94 |
+
placeholder="2023-02-10"
|
95 |
+
value="2023-02-10"
|
96 |
+
/>
|
97 |
+
|
98 |
+
<button id="text-submit">Submit</button>
|
99 |
+
</form>
|
100 |
+
</div>
|
101 |
+
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
</body>
|
106 |
+
</html>
|
templates/news_analysis.html
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!-- html ํ๊ทธ : HTML๋ก ์์ฑ๋์ด ์๋ค๋ ๊ฒ์ ์๋ ค์ค -->
|
2 |
+
<!-- html ํ๊ทธ : html ํ์ผ ์ ์ฒด๋ฅผ ๊ฐ์ธ๋ ํ๊ทธ -->
|
3 |
+
<html>
|
4 |
+
<!-- head ํ๊ทธ : ๋จธ๋ฆฌ๋ง์ ํด๋น -->
|
5 |
+
<!-- head ํ๊ทธ : css๋ javascript๋ฅผ ์ฐ๊ฒฐํด์ค -->
|
6 |
+
<!-- head ํ๊ทธ : ํ๋น์ฝ์ด๋ ๋ฌธ์์ด ์ธ์ฝ๋ฉ๊ณผ ๊ฐ์ ๋ฌธ์์ ๋ค์ํ ์ ๋ณด๋ฅผ ์ ๊ณต -->
|
7 |
+
<head>
|
8 |
+
|
9 |
+
<!-- link ํ๊ทธ : ์ฃผ๋ก ์ธ๋ถ css ํ์ผ์ ์ฐ๊ฒฐํ ๋ ์ฌ์ฉ -->
|
10 |
+
<link rel="stylesheet" href="static/css/news.css" />
|
11 |
+
|
12 |
+
<!-- jQuery -->
|
13 |
+
<script src='//cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js'></script>
|
14 |
+
<!-- script ํ๊ทธ : ์ธ๋ถ js ํ์ผ์ ์ฐ๊ฒฐํ๊ฑฐ๋ javascript ์ฝ๋๋ฅผ ์
๋ ฅํ ๋ ์ฌ์ฉ -->
|
15 |
+
<script type="text/javascript" src="static/js/news.js"></script>
|
16 |
+
|
17 |
+
|
18 |
+
</head>
|
19 |
+
|
20 |
+
|
21 |
+
|
22 |
+
<!-- body ํ๊ทธ : ๋ณธ๋ฌธ์ ํด๋นํ๋ ๋ถ๋ถ, ์ค์ ๋ณด์ฌ์ง๋ ํ๋ฉด์ ํด๋น -->
|
23 |
+
<body>
|
24 |
+
<h1><a class="gohome" href="/">Stock News Summaries AI</a></h1>
|
25 |
+
<a class="goticker"><h2 class="tickerName">{{embed1}}</h2></a>
|
26 |
+
<h3 class="titleDate">{{embed2}}</h2>
|
27 |
+
<h3 class="titleName">{{embed3}}</h2>
|
28 |
+
<h3 class="NewsURL">URL: <a class="input-News-URL" target=โ_blankโ>{{embed4}}</a></h2>
|
29 |
+
|
30 |
+
|
31 |
+
|
32 |
+
<!-- named entity recognition (NER) -->
|
33 |
+
<figure style="margin-bottom: 6rem">
|
34 |
+
<div class="entities" style="line-height: 2.5; direction: ltr">
|
35 |
+
|
36 |
+
</div>
|
37 |
+
</figure>
|
38 |
+
|
39 |
+
|
40 |
+
|
41 |
+
|
42 |
+
<!-- section ํ๊ทธ : HTML ๋ฌธ์์ ํฌํจ๋ ๋
๋ฆฝ์ ์ธ ์น์
์ ์ ์ํ ๋ ์ฌ์ฉ -->
|
43 |
+
<section id="model">
|
44 |
+
<h2>Questions about Stock's News</h2>
|
45 |
+
|
46 |
+
<!-- p ํ๊ทธ : paragraph, ์ฆ ๋ฌธ๋จ์ ์ฝ์๋ก, ํ๋์ ๋ฌธ๋จ์ ๋ง๋ค ๋ ์ฌ์ฉ -->
|
47 |
+
<p>
|
48 |
+
Model :
|
49 |
+
|
50 |
+
<!-- target="_blank" : ๊ธฐ๋ณธ์์ฑ ์ค ํ๋๋ก ํด๋ฆญ์ ๊ณ์ํด์ ์๋ก์ด ์ฐฝ์ด ์ด๋ฆฌ๊ฒ ๋๋ค. -->
|
51 |
+
<a
|
52 |
+
href="https://huggingface.co/allenai/tk-instruct-base-def-pos"
|
53 |
+
rel="noeferrer"
|
54 |
+
target="_blank">Tk-instruct Model</a
|
55 |
+
>
|
56 |
+
</p>
|
57 |
+
|
58 |
+
|
59 |
+
<!-- form ํ๊ทธ : ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ํ ํ๊ทธ -->
|
60 |
+
<!-- form ํ๊ทธ : ๋ฐ์ดํฐ๋ฅผ ์ ์กํ url (action ์์ฑ) -->
|
61 |
+
<!-- form ํ๊ทธ : ๋ฐ์ดํฐ ์ ๋ฌ ๋ฐฉ์์ด get์ธ์ง post์ธ์ง ๊ฒฐ์ (method ์์ฑ) -->
|
62 |
+
<form class="text-form">
|
63 |
+
|
64 |
+
<!-- label ํ๊ทธ๋ for ์์ฑ์ ์ฌ์ฉํด์ input ํ๊ทธ์ id ์์ฑ์ ์ฐ๊ณํด์ ์ฌ์ฉ -->
|
65 |
+
<label for="text-input">[ Questions ]</label> <br>
|
66 |
+
<input
|
67 |
+
id="text-input"
|
68 |
+
type="text"
|
69 |
+
placeholder="Input Questions"
|
70 |
+
value="Why did Alphabet's stock go down?"
|
71 |
+
/>
|
72 |
+
|
73 |
+
<button id="text-submit">Submit</button>
|
74 |
+
<p> [ Answer ] </p>
|
75 |
+
<p class="text-output"></p>
|
76 |
+
</form>
|
77 |
+
|
78 |
+
</section>
|
79 |
+
|
80 |
+
|
81 |
+
|
82 |
+
|
83 |
+
|
84 |
+
</body>
|
85 |
+
|
86 |
+
|
87 |
+
|
88 |
+
</html>
|