├── .gitignore ├── LICENSE ├── README.md ├── requirements.txt ├── run_gm_web.py ├── run_jq_web.py ├── run_tq_web.py ├── run_ts_web.py └── web ├── css ├── app.0ead0cf7.css └── chunk-vendors.e818cfcf.css ├── fonts ├── element-icons.535877f5.woff └── element-icons.732389de.ttf ├── index.html └── js ├── app.fdd123f3.js ├── app.fdd123f3.js.map ├── chunk-vendors.aefc5aaa.js └── chunk-vendors.aefc5aaa.js.map /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | .idea/ 6 | conf.py 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | .python-version 88 | 89 | # pipenv 90 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 91 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 92 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 93 | # install all needed dependencies. 94 | #Pipfile.lock 95 | 96 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 97 | __pypackages__/ 98 | 99 | # Celery stuff 100 | celerybeat-schedule 101 | celerybeat.pid 102 | 103 | # SageMath parsed files 104 | *.sage.py 105 | 106 | # Environments 107 | .env 108 | .venv 109 | env/ 110 | venv/ 111 | ENV/ 112 | env.bak/ 113 | venv.bak/ 114 | 115 | # Spyder project settings 116 | .spyderproject 117 | .spyproject 118 | 119 | # Rope project settings 120 | .ropeproject 121 | 122 | # mkdocs documentation 123 | /site 124 | 125 | # mypy 126 | .mypy_cache/ 127 | .dmypy.json 128 | dmypy.json 129 | 130 | # Pyre type checker 131 | .pyre/ 132 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 zengbin93 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 缠中说禅技术分析工具网页 2 | 3 | 使用 Tushare Pro / 聚宽 / 掘金 / 天勤 的数据进行缠中说禅技术分析结果展示 4 | 5 | ## 启动方法 6 | 7 | 1. 执行 `pip install -r requirements.txt` 安装环境 8 | 2. 确定所要使用的数据源,完成相关设置 9 | 3. 执行对应的数据源的脚本启动服务,不要改端口!!! 10 | 11 | `python run_ts_web.py` - 启动使用 `Tushare Pro` 数据的前端页面 12 | 13 | `python run_jq_web.py` - 启动使用 `聚宽` 数据的前端页面 14 | 15 | `python run_gm_web.py` - 启动使用 `掘金` 数据的前端页面 16 | 17 | `python run_tq_web.py` - 启动使用 `天勤` 数据的前端页面 18 | 19 | 启动后在本地 8005 端口访问服务,在对应的脚本中可以看到示例; 20 | 其中,ts_code 为对应的标的代码;trade_date 为交易日期,freqs 为K线周期 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tqsdk 2 | flask 3 | tushare 4 | requests 5 | pandas 6 | tornado 7 | czsc 8 | gm 9 | -------------------------------------------------------------------------------- /run_gm_web.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import json 3 | import os 4 | from tornado.ioloop import IOLoop 5 | from tornado.httpserver import HTTPServer 6 | from tornado.options import define, parse_command_line, options 7 | from tornado.web import RequestHandler, Application 8 | from tornado.web import StaticFileHandler 9 | from datetime import datetime, timedelta 10 | import czsc 11 | from czsc import KlineAnalyze 12 | from gm.api import * 13 | 14 | 15 | # 在这里设置你的掘金 token,要在本地启动掘金终端,才能正常获取数据 16 | set_token("set your gm token") 17 | 18 | assert czsc.__version__ == "0.5.8", "当前 czsc 版本为 {},请升级为 0.5.8 版本".format(czsc.__version__) 19 | 20 | 21 | def get_gm_kline(symbol, end_date, freq='D', k_count=3000): 22 | """从掘金获取历史K线数据""" 23 | if "-" not in end_date and isinstance(end_date, str): 24 | end_date = datetime.strptime(end_date, "%Y%m%d") 25 | freq_convert = {"60s": "1min", "300s": "5min", "1800s": "30min", "3600s": "60min", "1d": "D"} 26 | freq_convert = {v: k for k, v in freq_convert.items()} 27 | if freq[-1] in ['n', 'D']: 28 | freq = freq_convert[freq] 29 | if freq.endswith('min'): 30 | end_date += timedelta(days=1) 31 | df = history_n(symbol=symbol, frequency=freq, end_time=end_date, 32 | fields='symbol,eob,open,close,high,low,volume', 33 | count=k_count, df=True) 34 | df['dt'] = df['eob'] 35 | df['vol'] = df['volume'] 36 | df = df[['symbol', 'dt', 'open', 'close', 'high', 'low', 'vol']] 37 | df.sort_values('dt', inplace=True, ascending=True) 38 | df['dt'] = df.dt.apply(lambda x: x.strftime(r"%Y-%m-%d %H:%M:%S")) 39 | df.reset_index(drop=True, inplace=True) 40 | 41 | for col in ['open', 'close', 'high', 'low']: 42 | df[col] = df[col].apply(round, args=(2,)) 43 | return df 44 | 45 | 46 | # 端口固定为 8005,不可以调整 47 | define('port', type=int, default=8005, help='服务器端口') 48 | current_path = os.path.dirname(__file__) 49 | 50 | 51 | class BaseHandler(RequestHandler): 52 | def set_default_headers(self): 53 | self.set_header("Access-Control-Allow-Origin", "*") # 这个地方可以写域名 54 | self.set_header("Access-Control-Allow-Headers", "x-requested-with") 55 | self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') 56 | 57 | def post(self): 58 | self.write('some post') 59 | 60 | def get(self): 61 | self.write('some get') 62 | 63 | def options(self): 64 | self.set_status(204) 65 | self.finish() 66 | 67 | 68 | class BasicHandler(BaseHandler): 69 | """股票基本信息""" 70 | def get(self): 71 | ts_code = self.get_argument('ts_code') 72 | results = {"msg": "success", "basic": None} 73 | self.write(json.dumps(results, ensure_ascii=False)) 74 | 75 | 76 | class KlineHandler(BaseHandler): 77 | """K 线""" 78 | def get(self): 79 | ts_code = self.get_argument('ts_code') 80 | freq = self.get_argument('freq') 81 | trade_date = self.get_argument('trade_date') 82 | if trade_date == 'null': 83 | trade_date = datetime.now().date().__str__().replace("-", "") 84 | kline = get_gm_kline(symbol=ts_code, end_date=trade_date, freq=freq, k_count=3000) 85 | ka = KlineAnalyze(kline, bi_mode="new", verbose=False, use_xd=True, max_count=5000) 86 | kline = ka.to_df(ma_params=(5, 20), use_macd=True, max_count=5000, mode='new') 87 | kline = kline.fillna("") 88 | kline.loc[:, "dt"] = kline.dt.apply(str) 89 | columns = ["dt", "open", "close", "low", "high", "vol", 'fx_mark', 'fx', 'bi', 'xd'] 90 | 91 | self.finish({'kdata': kline[columns].values.tolist()}) 92 | 93 | 94 | if __name__ == '__main__': 95 | parse_command_line() 96 | app = Application([ 97 | ('/kline', KlineHandler), 98 | ('/basic', BasicHandler), 99 | (r'^/(.*?)$', StaticFileHandler, {"path": os.path.join(current_path, "web"), 100 | "default_filename": "index.html"}), 101 | ], 102 | static_path=os.path.join(current_path, "web"), 103 | dubug=True 104 | ) 105 | http_server = HTTPServer(app) 106 | http_server.listen(options.port) 107 | IOLoop.current().start() 108 | 109 | # 交易所代码如下: 110 | # 上交所 SHSE 111 | # 深交所 SZSE 112 | # 中金所 CFFEX 113 | # 上期所 SHFE 114 | # 大商所 DCE 115 | # 郑商所 CZCE 116 | # 上海国际能源交易中心 INE 117 | 118 | # 掘金数据文档:https://www.myquant.cn/docs/data/98? 119 | 120 | # http://localhost:8005/?ts_code=SHSE.000001&asset=I&trade_date=20200613&freqs=D,30min,5min,1min 121 | -------------------------------------------------------------------------------- /run_jq_web.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from tornado.ioloop import IOLoop 3 | from tornado.httpserver import HTTPServer 4 | from tornado.options import define, options, parse_command_line 5 | from tornado.web import RequestHandler, Application 6 | from tornado.web import StaticFileHandler 7 | from czsc import KlineAnalyze 8 | import os 9 | import pickle 10 | import czsc 11 | import json 12 | import requests 13 | import warnings 14 | import pandas as pd 15 | from datetime import datetime 16 | 17 | url = "https://dataapi.joinquant.com/apis" 18 | home_path = os.path.expanduser("~") 19 | file_token = os.path.join(home_path, "jq.token") 20 | 21 | assert czsc.__version__ == "0.5.8", "当前 czsc 版本为 {},请升级为 0.5.8 版本".format(czsc.__version__) 22 | 23 | def set_token(jq_mob, jq_pwd): 24 | """ 25 | 26 | :param jq_mob: str 27 | mob是申请JQData时所填写的手机号 28 | :param jq_pwd: str 29 | Password为聚宽官网登录密码,新申请用户默认为手机号后6位 30 | :return: 31 | """ 32 | pickle.dump([jq_mob, jq_pwd], open(file_token, 'wb')) 33 | 34 | 35 | def get_token(): 36 | """获取调用凭证""" 37 | if not os.path.exists(file_token): 38 | raise ValueError(f"{file_token} 文件不存在,请先调用 set_token 进行设置") 39 | 40 | jq_mob, jq_pwd = pickle.load(open(file_token, 'rb')) 41 | body = { 42 | "method": "get_current_token", 43 | "mob": jq_mob, # mob是申请JQData时所填写的手机号 44 | "pwd": jq_pwd, # Password为聚宽官网登录密码,新申请用户默认为手机号后6位 45 | } 46 | response = requests.post(url, data=json.dumps(body)) 47 | token = response.text 48 | return token 49 | 50 | 51 | def text2df(text): 52 | rows = [x.split(",") for x in text.strip().split('\n')] 53 | df = pd.DataFrame(rows[1:], columns=rows[0]) 54 | return df 55 | 56 | 57 | def get_kline(symbol, end_date: datetime, freq: str, start_date: datetime = None, count=None): 58 | """获取K线数据 59 | 60 | :param symbol: str 61 | 聚宽标的代码 62 | :param start_date: datetime 63 | 截止日期 64 | :param end_date: datetime 65 | 截止日期 66 | :param freq: str 67 | K线级别 68 | :param count: int 69 | K线数量,最大值为 5000 70 | :return: pd.DataFrame 71 | 72 | >>> start_date = datetime.strptime("20200701", "%Y%m%d") 73 | >>> end_date = datetime.strptime("20200719", "%Y%m%d") 74 | >>> df1 = get_kline(symbol="000001.XSHG", start_date=start_date, end_date=end_date, freq="1min") 75 | >>> df2 = get_kline(symbol="000001.XSHG", end_date=end_date, freq="1min", count=1000) 76 | """ 77 | if count and count > 5000: 78 | warnings.warn(f"count={count}, 超过5000的最大值限制,仅返回最后5000条记录") 79 | 80 | # 1m, 5m, 15m, 30m, 60m, 120m, 1d, 1w, 1M 81 | freq_convert = {"1min": "1m", "5min": '5m', '15min': '15m', 82 | "30min": "30m", "60min": '60m', "D": "1d", "W": '1w'} 83 | if start_date: 84 | data = { 85 | "method": "get_price_period", 86 | "token": get_token(), 87 | "code": symbol, 88 | "unit": freq_convert[freq], 89 | "date": start_date.strftime("%Y-%m-%d"), 90 | "end_date": end_date.strftime("%Y-%m-%d"), 91 | # "fq_ref_date": end_date 92 | } 93 | elif count: 94 | data = { 95 | "method": "get_price", 96 | "token": get_token(), 97 | "code": symbol, 98 | "count": count, 99 | "unit": freq_convert[freq], 100 | "end_date": end_date.strftime("%Y-%m-%d"), 101 | # "fq_ref_date": end_date 102 | } 103 | else: 104 | raise ValueError("start_date 和 count 不能同时为空") 105 | 106 | r = requests.post(url, data=json.dumps(data)) 107 | df = text2df(r.text) 108 | df['symbol'] = symbol 109 | df.rename({'date': 'dt', 'volume': 'vol'}, axis=1, inplace=True) 110 | df = df[['symbol', 'dt', 'open', 'close', 'high', 'low', 'vol']] 111 | for col in ['open', 'close', 'high', 'low', 'vol']: 112 | df.loc[:, col] = df[col].apply(lambda x: round(float(x), 2)) 113 | df.loc[:, "dt"] = pd.to_datetime(df['dt']) 114 | return df 115 | 116 | 117 | define('port', type=int, default=8005, help='服务器端口') 118 | current_path = os.path.dirname(__file__) 119 | 120 | 121 | class BaseHandler(RequestHandler): 122 | def set_default_headers(self): 123 | self.set_header("Access-Control-Allow-Origin", "*") # 这个地方可以写域名 124 | self.set_header("Access-Control-Allow-Headers", "x-requested-with") 125 | self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') 126 | 127 | def post(self): 128 | self.write('some post') 129 | 130 | def get(self): 131 | self.write('some get') 132 | 133 | def options(self): 134 | self.set_status(204) 135 | self.finish() 136 | 137 | 138 | class BasicHandler(BaseHandler): 139 | """股票基本信息""" 140 | def get(self): 141 | ts_code = self.get_argument('ts_code') 142 | results = {"msg": "success", "basic": None} 143 | self.write(json.dumps(results, ensure_ascii=False)) 144 | 145 | 146 | class KlineHandler(BaseHandler): 147 | """K 线""" 148 | def get(self): 149 | ts_code = self.get_argument('ts_code') 150 | freq = self.get_argument('freq') 151 | trade_date = self.get_argument('trade_date') 152 | if trade_date == 'null': 153 | trade_date = datetime.now().date() 154 | else: 155 | trade_date = datetime.strptime(trade_date, "%Y%m%d") 156 | kline = get_kline(symbol=ts_code, end_date=trade_date, freq=freq, count=5000) 157 | kline.loc[:, "dt"] = pd.to_datetime(kline.dt) 158 | ka = KlineAnalyze(kline, bi_mode="new", verbose=False, use_xd=True, max_count=5000) 159 | kline = ka.to_df(ma_params=(5, 20), use_macd=True, max_count=5000, mode='new') 160 | kline = kline.fillna("") 161 | kline.loc[:, "dt"] = kline.dt.apply(str) 162 | columns = ["dt", "open", "close", "low", "high", "vol", 'fx_mark', 'fx', 'bi', 'xd'] 163 | 164 | self.finish({'kdata': kline[columns].values.tolist()}) 165 | 166 | 167 | if __name__ == '__main__': 168 | parse_command_line() 169 | app = Application([ 170 | ('/kline', KlineHandler), 171 | ('/basic', BasicHandler), 172 | (r'^/(.*?)$', StaticFileHandler, {"path": os.path.join(current_path, "web"), 173 | "default_filename": "index.html"}), 174 | ], 175 | static_path=os.path.join(current_path, "web"), 176 | dubug=True 177 | ) 178 | http_server = HTTPServer(app) 179 | http_server.listen(options.port) 180 | IOLoop.current().start() 181 | 182 | # 查看聚宽标的编码规范 183 | # https://www.joinquant.com/help/api/help?name=JQData#%E8%8E%B7%E5%8F%96%E6%A0%87%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BF%A1%E6%81%AF 184 | 185 | # 使用聚宽数据,只需要给出正确的标的代码,支持股票和期货,有实时数据,不需要设置 asset 参数 186 | # http://localhost:8005/?ts_code=000001.XSHG&trade_date=20201121&freqs=1min 187 | 188 | 189 | # 聚宽期货数据:https://www.joinquant.com/help/api/help?name=Future 190 | # 191 | # ``` 192 | # 交易市场 代码后缀 示例代码 证券简称 193 | # 上海证券交易所 .XSHG '600519.XSHG' 贵州茅台 194 | # 深圳证券交易所 .XSHE '000001.XSHE' 平安银行 195 | # 中金所 .CCFX 'IC9999.CCFX' 中证500主力合约 196 | # 大商所 .XDCE 'A9999.XDCE' 豆一主力合约 197 | # 上期所 .XSGE 'AU9999.XSGE' 黄金主力合约 198 | # 郑商所 .XZCE 'CY8888.XZCE' 棉纱期货指数 199 | # 上海能源交易所 .XINE 'SC9999.XINE' 原油主力合约 200 | # ``` 201 | 202 | -------------------------------------------------------------------------------- /run_tq_web.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | """ 3 | 天勤开始收费,不再维护这个脚本 4 | ================================================= 5 | """ 6 | import os 7 | import json 8 | import pandas as pd 9 | import czsc 10 | from czsc import KlineAnalyze 11 | from datetime import datetime, timedelta 12 | from flask import Flask, request, make_response, jsonify 13 | from tqsdk import TqApi 14 | 15 | 16 | base_path = os.path.split(os.path.realpath(__file__))[0] 17 | web_path = os.path.join(base_path, 'web') 18 | app = Flask(__name__, static_folder=web_path) 19 | 20 | # api = TqApi(_stock=True) # 支持股票数据 21 | api = TqApi() 22 | 23 | 24 | def format_kline(kline): 25 | """格式化K线""" 26 | def __convert_time(t): 27 | try: 28 | dt = datetime.utcfromtimestamp(t/1000000000) 29 | dt = dt + timedelta(hours=8) # 中国默认时区 30 | return dt 31 | except: 32 | return "" 33 | 34 | kline['dt'] = kline['datetime'].apply(__convert_time) 35 | kline['vol'] = kline['volume'] 36 | columns = ['symbol', 'dt', 'open', 'close', 'high', 'low', 'vol'] 37 | df = kline[columns] 38 | df = df.dropna(axis=0) 39 | df.drop_duplicates(subset=['dt'], inplace=True) 40 | df.sort_values('dt', inplace=True, ascending=True) 41 | df.reset_index(drop=True, inplace=True) 42 | return df 43 | 44 | 45 | def get_kline(symbol="SHFE.cu2002", freq='1min', k_count=3000): 46 | """获取K线""" 47 | freq_map = {'1min': 60, '5min': 300, '15min': 900, '30min': 1800, 48 | '60min': 3600, 'D': 3600*24, 'W': 86400*7} 49 | df = api.get_kline_serial(symbol, duration_seconds=freq_map[freq], data_length=k_count) 50 | df = format_kline(df) 51 | return df 52 | 53 | 54 | @app.route('/', methods=['GET']) 55 | def index(): 56 | return app.send_static_file('index.html') 57 | 58 | 59 | @app.route('/kline', methods=['POST', 'GET']) 60 | def kline(): 61 | if request.method == "POST": 62 | data = json.loads(request.get_data(as_text=True)) 63 | elif request.method == "GET": 64 | data = request.args 65 | else: 66 | raise ValueError 67 | 68 | ts_code = data.get('ts_code') 69 | freq = data.get('freq') 70 | k = get_kline(symbol=ts_code, freq=freq, k_count=5000) 71 | if czsc.__version__ < "0.5": 72 | ka = KlineAnalyze(k, bi_mode="new", xd_mode='strict') 73 | k = pd.DataFrame(ka.kline_new) 74 | else: 75 | ka = KlineAnalyze(k, min_bi_k=5, verbose=False) 76 | k = ka.to_df(ma_params=(5, 20), use_macd=True, max_count=5000) 77 | 78 | k = k.fillna("") 79 | kline.loc[:, "dt"] = kline.dt.apply(str) 80 | columns = ["dt", "open", "close", "low", "high", "vol", 'fx_mark', 'fx', 'bi', 'xd'] 81 | res = make_response(jsonify({'kdata': k[columns].values.tolist()})) 82 | res.headers['Access-Control-Allow-Origin'] = '*' 83 | res.headers['Access-Control-Allow-Method'] = '*' 84 | res.headers['Access-Control-Allow-Headers'] = '*' 85 | return res 86 | 87 | 88 | if __name__ == '__main__': 89 | app.run(port=8005, debug=True) 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /run_ts_web.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import czsc 4 | import json 5 | import os 6 | import time 7 | import pandas as pd 8 | from tornado.ioloop import IOLoop 9 | from tornado.httpserver import HTTPServer 10 | from tornado.options import define, options, parse_command_line 11 | from tornado.web import RequestHandler, Application 12 | from tornado.web import StaticFileHandler 13 | import tushare as ts 14 | from datetime import datetime, timedelta 15 | import czsc 16 | from czsc import KlineAnalyze 17 | 18 | # 首次使用,需要在这里设置你的 tushare token,用于获取数据;在同一台机器上,tushare token 只需要设置一次 19 | # 没有 token,到 https://tushare.pro/register?reg=7 注册获取 20 | # ts.set_token("your tushare token") 21 | 22 | assert czsc.__version__ == "0.5.8", "当前 czsc 版本为 {},请升级为 0.5.8 版本".format(czsc.__version__) 23 | 24 | 25 | def _get_start_date(end_date, freq): 26 | end_date = datetime.strptime(end_date, '%Y%m%d') 27 | if freq == '1min': 28 | start_date = end_date - timedelta(days=60) 29 | elif freq == '5min': 30 | start_date = end_date - timedelta(days=150) 31 | elif freq == '30min': 32 | start_date = end_date - timedelta(days=1000) 33 | elif freq == 'D': 34 | start_date = end_date - timedelta(weeks=1000) 35 | elif freq == 'W': 36 | start_date = end_date - timedelta(weeks=1000) 37 | else: 38 | raise ValueError("'freq' value error, current value is %s, " 39 | "optional valid values are ['1min', '5min', '30min', " 40 | "'D', 'W']" % freq) 41 | return start_date 42 | 43 | 44 | def get_kline(ts_code, end_date, freq='30min', asset='E'): 45 | """获取指定级别的前复权K线 46 | 47 | :param ts_code: str 48 | 股票代码,如 600122.SH 49 | :param freq: str 50 | K线级别,可选值 [1min, 5min, 15min, 30min, 60min, D, M, Y] 51 | :param end_date: str 52 | 日期,如 20190610 53 | :param asset: str 54 | 交易资产类型,可选值 E股票 I沪深指数 C数字货币 FT期货 FD基金 O期权 CB可转债(v1.2.39),默认E 55 | :return: pd.DataFrame 56 | columns = ["symbol", "dt", "open", "close", "high", "low", "vol"] 57 | """ 58 | start_date = _get_start_date(end_date, freq) 59 | start_date = start_date.date().__str__().replace("-", "") 60 | end_date = datetime.strptime(end_date, '%Y%m%d') 61 | end_date = end_date + timedelta(days=1) 62 | end_date = end_date.date().__str__().replace("-", "") 63 | 64 | df = ts.pro_bar(ts_code=ts_code, freq=freq, start_date=start_date, end_date=end_date, 65 | adj='qfq', asset=asset) 66 | 67 | # 统一 k 线数据格式为 6 列,分别是 ["symbol", "dt", "open", "close", "high", "low", "vr"] 68 | if "min" in freq: 69 | df.rename(columns={'ts_code': "symbol", "trade_time": "dt"}, inplace=True) 70 | else: 71 | df.rename(columns={'ts_code': "symbol", "trade_date": "dt"}, inplace=True) 72 | 73 | df.drop_duplicates(subset='dt', keep='first', inplace=True) 74 | df.sort_values('dt', inplace=True) 75 | df['dt'] = df.dt.apply(str) 76 | if freq.endswith("min"): 77 | # 清理 9:30 的空数据 78 | df['not_start'] = df.dt.apply(lambda x: not x.endswith("09:30:00")) 79 | df = df[df['not_start']] 80 | df.reset_index(drop=True, inplace=True) 81 | 82 | k = df[['symbol', 'dt', 'open', 'close', 'high', 'low', 'vol']] 83 | 84 | for col in ['open', 'close', 'high', 'low']: 85 | k[col] = k[col].apply(round, args=(2,)) 86 | return k 87 | 88 | 89 | def get_stock_basic(ts_code=None): 90 | """获取股票的基本信息""" 91 | file_cache = "stock_basic.csv" 92 | if os.path.exists(file_cache) and time.time() - os.path.getmtime(file_cache) < 3600: 93 | df = pd.read_csv(file_cache, dtype={"list_date": str}, encoding="utf-8") 94 | else: 95 | pro = ts.pro_api() 96 | df = pro.stock_basic(exchange='', list_status='L', fields='ts_code,name,area,industry,list_date') 97 | df.to_csv(file_cache, index=False, encoding="utf-8") 98 | 99 | if not ts_code: 100 | return df 101 | else: 102 | share = df[df["ts_code"] == ts_code].iloc[0].to_dict() 103 | return share 104 | 105 | 106 | # 端口固定为 8005,不可以调整 107 | define('port', type=int, default=8005, help='服务器端口') 108 | current_path = os.path.dirname(__file__) 109 | 110 | 111 | class BaseHandler(RequestHandler): 112 | def set_default_headers(self): 113 | self.set_header("Access-Control-Allow-Origin", "*") # 这个地方可以写域名 114 | self.set_header("Access-Control-Allow-Headers", "x-requested-with") 115 | self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') 116 | 117 | def post(self): 118 | self.write('some post') 119 | 120 | def get(self): 121 | self.write('some get') 122 | 123 | def options(self): 124 | self.set_status(204) 125 | self.finish() 126 | 127 | 128 | class BasicHandler(BaseHandler): 129 | """股票基本信息""" 130 | def get(self): 131 | ts_code = self.get_argument('ts_code') 132 | basic = get_stock_basic(ts_code) 133 | basic['symbol'] = basic['ts_code'] 134 | results = {"msg": "success", "basic": [basic]} 135 | self.write(json.dumps(results, ensure_ascii=False)) 136 | 137 | 138 | class KlineHandler(BaseHandler): 139 | """K 线""" 140 | def get(self): 141 | ts_code = self.get_argument('ts_code') 142 | freq = self.get_argument('freq') 143 | asset = self.get_argument('asset', "E") 144 | trade_date = self.get_argument('trade_date') 145 | if trade_date == 'null': 146 | trade_date = datetime.now().date().__str__().replace("-", "") 147 | kline = get_kline(ts_code=ts_code, end_date=trade_date, freq=freq, asset=asset) 148 | kline.loc[:, "dt"] = pd.to_datetime(kline.dt) 149 | 150 | ka = KlineAnalyze(kline, bi_mode="new", verbose=False, use_xd=True, max_count=5000) 151 | kline = ka.to_df(ma_params=(5, 20), use_macd=True, max_count=5000, mode='new') 152 | kline = kline.fillna("") 153 | kline.loc[:, "dt"] = kline.dt.apply(str) 154 | columns = ["dt", "open", "close", "low", "high", "vol", 'fx_mark', 'fx', 'bi', 'xd'] 155 | 156 | self.finish({'kdata': kline[columns].values.tolist()}) 157 | 158 | 159 | if __name__ == '__main__': 160 | parse_command_line() 161 | app = Application([ 162 | ('/kline', KlineHandler), 163 | ('/basic', BasicHandler), 164 | (r'^/(.*?)$', StaticFileHandler, {"path": os.path.join(current_path, "web"), 165 | "default_filename": "index.html"}), 166 | ], 167 | static_path=os.path.join(current_path, "web"), 168 | dubug=True 169 | ) 170 | http_server = HTTPServer(app) 171 | http_server.listen(options.port) 172 | IOLoop.current().start() 173 | 174 | 175 | # http://localhost:8005/?ts_code=000001.SH&asset=I&trade_date=20200613&freqs=D,30min,5min,1min 176 | 177 | -------------------------------------------------------------------------------- /web/fonts/element-icons.535877f5.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengbin93/czsc_web_ui/686ec6daefc9ef43dcee7c99433f79631c0f84c5/web/fonts/element-icons.535877f5.woff -------------------------------------------------------------------------------- /web/fonts/element-icons.732389de.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zengbin93/czsc_web_ui/686ec6daefc9ef43dcee7c99433f79631c0f84c5/web/fonts/element-icons.732389de.ttf -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 缠论分析工具
-------------------------------------------------------------------------------- /web/js/app.fdd123f3.js: -------------------------------------------------------------------------------- 1 | (function(t){function e(e){for(var n,s,o=e[0],l=e[1],c=e[2],u=0,p=[];ua.datas[t.dataIndex][0]?A:z,e}}}},{name:"MACD",type:"bar",xAxisIndex:2,yAxisIndex:2,data:n.macd,barWidth:"40%",itemStyle:{normal:{color:function(t){var e;return e=t.data>=0?A:z,e}}}},{name:"DIF",type:"line",symbol:"none",xAxisIndex:2,yAxisIndex:2,data:n.dif,lineStyle:{normal:{color:"#da6ee8",width:1}}},{name:"DEA",type:"line",symbol:"none",xAxisIndex:2,yAxisIndex:2,data:n.dea,lineStyle:{normal:{opacity:.8,color:"#39afe6",width:1}}}]}}n=function(t,e,a){var n,r,i,s;if(s=2/(t+1),a)for(i=[e[0][a]],n=1,r=e.length;n data.datas[params.dataIndex][0]) {\r\n\t\t\t\t\t\t\t\t\tcolorList = upColor;\r\n\t\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\t\tcolorList = downColor;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\treturn colorList;\r\n\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}, {\r\n\t\t\t\t\tname: 'MACD',\r\n\t\t\t\t\ttype: 'bar',\r\n\t\t\t\t\txAxisIndex: 2,\r\n\t\t\t\t\tyAxisIndex: 2,\r\n\t\t\t\t\tdata: macd.macd,\r\n\t\t\t\t\tbarWidth: '40%',\r\n\t\t\t\t\titemStyle: {\r\n\t\t\t\t\t\tnormal: {\r\n\t\t\t\t\t\t\tcolor: function(params) {\r\n\t\t\t\t\t\t\t\tvar colorList;\r\n\t\t\t\t\t\t\t\tif (params.data >= 0) {\r\n\t\t\t\t\t\t\t\t\tcolorList = upColor;\r\n\t\t\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\t\t\tcolorList = downColor;\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\treturn colorList;\r\n\t\t\t\t\t\t\t},\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}, {\r\n\t\t\t\t\tname: 'DIF',\r\n\t\t\t\t\ttype: 'line',\r\n\t\t\t\t\tsymbol: \"none\",\r\n\t\t\t\t\txAxisIndex: 2,\r\n\t\t\t\t\tyAxisIndex: 2,\r\n\t\t\t\t\tdata: macd.dif,\r\n\t\t\t\t\tlineStyle: {\r\n\t\t\t\t\t\tnormal: {\r\n\t\t\t\t\t\t\tcolor: '#da6ee8',\r\n\t\t\t\t\t\t\twidth: 1\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}, {\r\n\t\t\t\t\tname: 'DEA',\r\n\t\t\t\t\ttype: 'line',\r\n\t\t\t\t\tsymbol: \"none\",\r\n\t\t\t\t\txAxisIndex: 2,\r\n\t\t\t\t\tyAxisIndex: 2,\r\n\t\t\t\t\tdata: macd.dea,\r\n\t\t\t\t\tlineStyle: {\r\n\t\t\t\t\t\tnormal: {\r\n\t\t\t\t\t\t\topacity: 0.8,\r\n\t\t\t\t\t\t\tcolor: '#39afe6',\r\n\t\t\t\t\t\t\twidth: 1\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t]\r\n\t\t};\r\n\t\t\r\n\t\t\r\n}\r\n \r\n\r\n\r\nexport {initKOption};\r\n\r\n\r\n\r\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{\"id\":\"app\"}},[_c('router-view')],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./App.vue?vue&type=template&id=79bde48d&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{\"id\":\"kline\"}},[_vm._m(0),_c('br'),_c('div',{staticClass:\"k-forms\"},[_c('el-form',{staticClass:\"form-inline\",attrs:{\"inline\":true}},[_c('el-form-item',{attrs:{\"label\":\"参数配置: \"}},[_c('el-tooltip',{staticClass:\"item\",attrs:{\"effect\":\"dark\",\"content\":\"请输入标的代码\",\"placement\":\"top-start\"}},[_c('el-input',{attrs:{\"placeholder\":\"请输入标的代码\"},model:{value:(_vm.ts_code_new),callback:function ($$v) {_vm.ts_code_new=$$v},expression:\"ts_code_new\"}})],1)],1),_c('el-form-item',[_c('el-tooltip',{staticClass:\"item\",attrs:{\"effect\":\"dark\",\"content\":\"请选择资产类型\",\"placement\":\"top-start\"}},[_c('el-select',{attrs:{\"placeholder\":\"资产类型\"},model:{value:(_vm.asset_new),callback:function ($$v) {_vm.asset_new=$$v},expression:\"asset_new\"}},_vm._l((_vm.asset_types),function(asset){return _c('div',{key:asset},[_c('el-option',{attrs:{\"label\":asset,\"value\":asset}})],1)}),0)],1)],1),_c('el-form-item',{attrs:{\"label\":\"K线级别: \"}},[_c('el-tooltip',{staticClass:\"item\",attrs:{\"effect\":\"dark\",\"content\":\"请选择需要展示的多个级别 K 线\",\"placement\":\"top-start\"}},[_c('el-checkbox-group',{attrs:{\"size\":\"large\",\"fill\":\"#606266\",\"text-color\":\"#ffffff\"},model:{value:(_vm.levels_checked),callback:function ($$v) {_vm.levels_checked=$$v},expression:\"levels_checked\"}},_vm._l((_vm.levels),function(level){return _c('el-checkbox-button',{key:level,attrs:{\"label\":level}},[_vm._v(_vm._s(level))])}),1)],1)],1),_c('el-form-item',[_c('span',{staticClass:\"date-picker\"},[_vm._v(\"请选择交易日期: \")]),_c('el-date-picker',{directives:[{name:\"loading\",rawName:\"v-loading\",value:(_vm.loading),expression:\"loading\"}],attrs:{\"align\":\"right\",\"type\":\"date\",\"placeholder\":\"选择日期\",\"value-format\":\"yyyyMMdd\",\"picker-options\":_vm.pickerOptions},model:{value:(_vm.trade_date_new),callback:function ($$v) {_vm.trade_date_new=$$v},expression:\"trade_date_new\"}})],1),_c('el-form-item',[_c('el-button',{attrs:{\"type\":\"primary\"},on:{\"click\":_vm.updatePara}},[_vm._v(\"更新参数\")])],1)],1)],1),(_vm.asset === 'E')?_c('Basic',{attrs:{\"ts_code\":_vm.ts_code}}):_vm._e(),_vm._l((_vm.freqs.split(',')),function(level){return _c('div',{key:level},[_c('plot-kline',{attrs:{\"freq\":level,\"ts_code\":_vm.ts_code,\"asset\":_vm.asset,\"trade_date\":_vm.trade_date}})],1)}),_c('Footer')],2)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('nav',{staticClass:\"navbar is-dark is-fixed-top\",attrs:{\"role\":\"navigation\",\"aria-label\":\"main navigation\"}},[_c('div',{staticClass:\"navbar-menu\",attrs:{\"id\":\"navbarChan\"}},[_c('div',{staticClass:\"navbar-brand\"},[_c('a',{staticClass:\"navbar-item\",attrs:{\"href\":\"\"}},[_vm._v(\" Tushare - 缠论分析工具 \")]),_c('a',{staticClass:\"navbar-burger burger\",attrs:{\"role\":\"button\",\"aria-label\":\"menu\",\"aria-expanded\":\"false\",\"data-target\":\"navbarBasicExample\"}},[_c('span',{attrs:{\"aria-hidden\":\"true\"}}),_c('span',{attrs:{\"aria-hidden\":\"true\"}}),_c('span',{attrs:{\"aria-hidden\":\"true\"}})])]),_c('div',{staticClass:\"navbar-start\"}),_c('div',{staticClass:\"navbar-end\"},[_c('div',{staticClass:\"navbar-item\"},[_c('div',{staticClass:\"buttons\"},[_c('a',{staticClass:\"button is-primary\"},[_c('strong',[_vm._v(\"Sign up\")])]),_c('a',{staticClass:\"button is-light\"},[_vm._v(\" Log in \")])])])])])])}]\n\nexport { render, staticRenderFns }","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _vm._m(0)}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('footer',{staticClass:\"footer\"},[_c('div',{staticClass:\"content has-text-centered\"},[_c('p',[_c('strong',[_vm._v(\"Chan\")]),_vm._v(\" by \"),_c('a',{attrs:{\"href\":\"https://github.com/zengbin93/chan\"}},[_vm._v(\"zengbin93\")]),_vm._v(\". The source code is licensed \"),_c('a',{attrs:{\"href\":\"http://opensource.org/licenses/mit-license.php\"}},[_vm._v(\"MIT\")]),_vm._v(\". \")])])])}]\n\nexport { render, staticRenderFns }","import { render, staticRenderFns } from \"./Footer.vue?vue&type=template&id=722d277a&\"\nvar script = {}\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('section',{staticClass:\"section\"},[_c('div',{staticStyle:{\"height\":\"580px\"},attrs:{\"id\":_vm.chart_id}})])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","export default {\r\n // api: \"http://103.235.232.152:8005\",\r\n api: \"http://localhost:8005\",\r\n // api: process.env.chan_api ? process.env.chan_api : \"http://localhost:8005\"\r\n}\r\n\r\n","\r\n\r\n\r\n\r\n\r\n\r\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./PlotKline.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./PlotKline.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./PlotKline.vue?vue&type=template&id=2996d916&scoped=true&\"\nimport script from \"./PlotKline.vue?vue&type=script&lang=js&\"\nexport * from \"./PlotKline.vue?vue&type=script&lang=js&\"\nimport style0 from \"./PlotKline.vue?vue&type=style&index=0&id=2996d916&scoped=true&lang=css&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"2996d916\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"tile is-ancestor\"},[_c('div',{staticClass:\"tile is-parent\"},[_c('el-table',{staticStyle:{\"width\":\"100%\"},attrs:{\"data\":_vm.basic,\"stripe\":\"\"}},[_c('el-table-column',{attrs:{\"prop\":\"symbol\",\"label\":\"交易代码\",\"width\":\"180\",\"align\":\"center\"}}),_c('el-table-column',{attrs:{\"prop\":\"name\",\"label\":\"股票名称\",\"width\":\"180\",\"align\":\"center\"}}),_c('el-table-column',{attrs:{\"prop\":\"area\",\"label\":\"区域\",\"width\":\"180\",\"align\":\"center\"}}),_c('el-table-column',{attrs:{\"prop\":\"industry\",\"label\":\"行业\",\"width\":\"180\",\"align\":\"center\"}}),_c('el-table-column',{attrs:{\"prop\":\"market\",\"label\":\"市场类型\",\"width\":\"180\",\"align\":\"center\"}}),_c('el-table-column',{attrs:{\"prop\":\"list_date\",\"label\":\"上市日期\",\"width\":\"180\",\"align\":\"center\"}})],1)],1),_c('div',{staticClass:\"tile is-parent is-4\"},[_c('el-link',{attrs:{\"href\":_vm.f10_url,\"target\":\"_blank\"}},[_vm._v(\"同花顺F10\")])],1)])])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\r\n\r\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Basic.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Basic.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Basic.vue?vue&type=template&id=745ff3ca&\"\nimport script from \"./Basic.vue?vue&type=script&lang=js&\"\nexport * from \"./Basic.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\n\n\n\n\n","import mod from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Kline.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../node_modules/thread-loader/dist/cjs.js!../../node_modules/babel-loader/lib/index.js!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./Kline.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./Kline.vue?vue&type=template&id=3e29f87f&\"\nimport script from \"./Kline.vue?vue&type=script&lang=js&\"\nexport * from \"./Kline.vue?vue&type=script&lang=js&\"\nimport style0 from \"./Kline.vue?vue&type=style&index=0&lang=scss&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","\r\nimport Vue from \"vue\";\r\nimport Router from \"vue-router\";\r\n\r\nimport Kline from '../pages/Kline.vue'\r\n\r\nVue.use(Router)\r\n\r\nvar router = [\r\n // {path: \"/\", name: \"index\", redirect: '/kline'},\r\n {\r\n path: \"/\", \r\n name: \"kline\", \r\n component: Kline,\r\n meta: { title: 'K线' },\r\n props: (route) => ({ \r\n ts_code: route.query.ts_code, \r\n asset: route.query.asset, \r\n trade_date: route.query.trade_date,\r\n freqs: route.query.freqs\r\n }),\r\n }\r\n]\r\n\r\nexport default new Router({\r\n mode: 'history',\r\n routes: router\r\n})\r\n\r\n\r\n","import Vue from 'vue'\nimport ElementUI from 'element-ui';\nimport 'element-ui/lib/theme-chalk/index.css';\nimport App from './App.vue'\nimport router from './router';\n\nVue.use(ElementUI);\nVue.config.productionTip = false\n\nnew Vue({\n router,\n render: h => h(App),\n}).$mount('#app')\n","import mod from \"-!../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--6-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--6-oneOf-1-2!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./PlotKline.vue?vue&type=style&index=0&id=2996d916&scoped=true&lang=css&\"; export default mod; export * from \"-!../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--6-oneOf-1-0!../../node_modules/css-loader/dist/cjs.js??ref--6-oneOf-1-1!../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../node_modules/postcss-loader/src/index.js??ref--6-oneOf-1-2!../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../node_modules/vue-loader/lib/index.js??vue-loader-options!./PlotKline.vue?vue&type=style&index=0&id=2996d916&scoped=true&lang=css&\""],"sourceRoot":""} --------------------------------------------------------------------------------