├── README.md ├── app.py ├── app.pyc ├── chan ├── __init__.py ├── chan_app.py ├── constant.py ├── db │ ├── dao.py │ ├── db_helper.py │ └── object.py ├── level.py ├── mapper.py ├── object.py ├── sequence.py ├── trade_manager.py ├── trend.py └── yanzheng_fenxing_handler.py ├── demo.jpeg ├── event ├── __init__.py └── engine.py ├── gateway ├── jq │ ├── __init__.py │ └── jq_gateway.py └── tq │ ├── __init__.py │ └── tq_gateway.py ├── jq ├── __init__.py ├── jq_test.py └── north_money_bool_band.py ├── mytushare ├── __init__.py ├── api.py ├── api2.py └── cache.sqlite ├── requirements.txt ├── sina ├── __init__.py ├── __init__.pyc ├── api.py └── api.pyc ├── static ├── data.json ├── key.txt ├── kline.html └── pens.json ├── test ├── __init__.py ├── cache.sqlite └── test_pandas_datareader.py ├── tools ├── __init__.py ├── __init__.pyc ├── time_tools.py └── time_tools.pyc ├── trader ├── __init__.py ├── constant.py ├── event.py ├── gateway.py └── object.py ├── web ├── __init__.py ├── encoder │ ├── __init__.py │ └── alchemy_encoder.py └── web_app.py └── yahoo ├── __init__.py ├── __init__.pyc ├── api.py └── api.pyc /README.md: -------------------------------------------------------------------------------- 1 | # ChanMaster 2 | 缠论量化 3 | 4 | 自动分型、严格笔、同级别分解、线段、中枢 5 | 6 | 7 | ![demo](demo.jpeg) 8 | 9 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import os 3 | if os.path.exists("/tmp/ChanMaster/chan.db"): 4 | os.remove("/tmp/ChanMaster/chan.db") 5 | 6 | import logging 7 | 8 | from chan.chan_app import ChanApp 9 | from chan.level import FiveMinuteLevel, OneMinuteLevel 10 | from gateway.jq.jq_gateway import JQGateway 11 | from gateway.tq.tq_gateway import TQGateway 12 | from trader.constant import Exchange 13 | 14 | __author__ = "hanweiwei" 15 | __date__ = "2018/10/24" 16 | 17 | app_log = logging.getLogger("app") 18 | app_log.setLevel(logging.INFO) 19 | handler = logging.StreamHandler(stream=None) 20 | formatter = logging.Formatter('%(asctime)s %(filename)s : %(levelname)s %(message)s') # 定义该handler格式 21 | handler.setFormatter(formatter) 22 | app_log.addHandler(handler) 23 | 24 | if __name__ == '__main__': 25 | 26 | 27 | chan_app = ChanApp(FiveMinuteLevel(), JQGateway, Exchange.AGU, "603568.XSHG") 28 | chan_app.start() 29 | -------------------------------------------------------------------------------- /app.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/app.pyc -------------------------------------------------------------------------------- /chan/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2018/10/25" -------------------------------------------------------------------------------- /chan/chan_app.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from datetime import datetime 3 | from typing import List 4 | 5 | from chan.level import Level 6 | from chan.object import ChanBarData, Pen, YanZhengFenxing, DerivePen 7 | from chan.trend import BaseTrend 8 | from chan.yanzheng_fenxing_handler import YanZhengFenxingHandler 9 | from event.engine import EventEngine, Event 10 | from tools import time_tools 11 | from trader.constant import Exchange, Interval 12 | from trader.event import EVENT_BAR 13 | from trader.gateway import BaseGateway 14 | from trader.object import KLineRequest, BarData, SubscribeBarRequest 15 | 16 | __author__ = "hanweiwei" 17 | __date__ = "2018/10/25" 18 | 19 | 20 | class SubTrend(BaseTrend): 21 | def __init__(self, interval: Interval, parent_trend): 22 | super().__init__(interval, parent_trend) 23 | 24 | def _handle_pens(self): 25 | if len(self.pens): 26 | cur = self.pens[-1] 27 | last_fenxing = cur.end_fenxing 28 | elif len(self.fenxings) > 2: 29 | last_fenxing = self.fenxings[0] 30 | else: 31 | # 分型不够 32 | return 33 | index = last_fenxing.index + 1 34 | while index < len(self.fenxings): 35 | 36 | cur_fenxing = self.fenxings[index] 37 | if last_fenxing.is_top == cur_fenxing.is_top: 38 | # 同向分型 ,看是否需要修正笔 39 | if last_fenxing.is_top and cur_fenxing.extreme_bar.include_high > last_fenxing.extreme_bar.include_high \ 40 | or \ 41 | not last_fenxing.is_top and cur_fenxing.extreme_bar.include_low < last_fenxing.extreme_bar.include_low: 42 | self._fix_pen() 43 | # 修正笔,重置last_fenxing 44 | if len(self.pens): 45 | cur = self.pens[-1] 46 | last_fenxing = cur.end_fenxing 47 | elif last_fenxing.index + 1 < len(self.fenxings): 48 | last_fenxing = self.fenxings[last_fenxing.index + 1] 49 | else: 50 | # 分型不够 51 | return 52 | index = last_fenxing.index + 1 53 | continue 54 | 55 | if self._is_fenxing_match(last_fenxing.index, cur_fenxing.index): 56 | pen = Pen(len(self.pens), last_fenxing, cur_fenxing, cur_fenxing.is_top) 57 | self.pens.append(pen) 58 | # print("new pen %s -> %s", pen.start_bar_index(), pen.end_bar_index()) 59 | self.pen_dao.insert(pen) 60 | self._handle_segments(False) 61 | self._handle_centre() 62 | # 新的笔产生,重置last_fenxing 63 | last_fenxing = cur_fenxing 64 | index = cur_fenxing.index + 1 65 | 66 | def _fix_pen(self): 67 | while self.pens: 68 | # 修正笔 69 | last_pen = self.pens[-1] 70 | for i in range(last_pen.end_fenxing.index + 1, len(self.fenxings)): 71 | if self._is_fenxing_match(last_pen.start_fenxing.index, i): 72 | last_pen_end_bar_index_before = last_pen.end_bar_index() 73 | last_pen.end_fenxing = self.fenxings[i] 74 | self.pen_dao.insert(self.pens[-1]) 75 | # print("fix pen %s %s,%s -> %s,%s" % (last_pen.index, 76 | # self.pens[-1].start_bar_index(), last_pen_end_bar_index_before, 77 | # self.pens[-1].start_bar_index(), 78 | # self.pens[-1].end_bar_index())) 79 | self._handle_segments(True) 80 | self._handle_centre() 81 | return True 82 | 83 | # print("delete pen", last_pen.index, last_pen.start_bar_index(), last_pen.end_bar_index()) 84 | self.pens.remove(last_pen) 85 | self.pen_dao.delete(last_pen) 86 | return False 87 | 88 | def _is_fenxing_match(self, start: int, end: int): 89 | 90 | # if end - start == 1: 91 | # # 次级别要有结构 92 | # return False 93 | start_fenxing = self.fenxings[start] 94 | end_fenxing = self.fenxings[end] 95 | 96 | # 满足5根k线否 97 | start_fenxing_end_bar_index = self.bars[start_fenxing.extreme_bar.end_index() + 1].end_index() 98 | end_fenxing_start_bar_index = self.bars[end_fenxing.extreme_bar.start_index() - 1].start_index() 99 | if end_fenxing_start_bar_index - start_fenxing_end_bar_index < 2: 100 | # 不满足5根k线 101 | return False 102 | 103 | # end 是不是start分型与end分型之间的极值分型 104 | # 找出极值分型, 以及回荡深度,为次高点成笔做准备 105 | highest = start_fenxing.extreme_bar.include_high # 记录遍利过的最高点 106 | lowest = start_fenxing.extreme_bar.include_low # 记录遍历过的最低点 107 | highest_fenxing = start_fenxing 108 | lowest_fenxing = start_fenxing 109 | 110 | secondary_back_deep = 0 # 次高点成笔回荡深度 111 | for i in range(start + 1, end + 1): 112 | cur = self.fenxings[i] 113 | if cur.is_top == start_fenxing.is_top and secondary_back_deep < 0.5: 114 | if start_fenxing.is_top: 115 | secondary_back_deep = (cur.extreme_bar.include_high - lowest) / (highest - lowest) 116 | else: 117 | secondary_back_deep = (highest - cur.extreme_bar.include_low) / (highest - lowest) 118 | 119 | if cur.is_top and highest < cur.extreme_bar.include_high: 120 | highest = cur.extreme_bar.include_high 121 | highest_fenxing = cur 122 | elif not cur.is_top and lowest > cur.extreme_bar.include_low: 123 | lowest = cur.extreme_bar.include_low 124 | lowest_fenxing = cur 125 | 126 | if start_fenxing.is_top and highest_fenxing != start_fenxing \ 127 | or \ 128 | not start_fenxing.is_top and lowest_fenxing != start_fenxing: 129 | return False 130 | 131 | # end分型是极值分型,可以直接成笔 132 | if start_fenxing.is_top and lowest_fenxing == end_fenxing \ 133 | or \ 134 | not start_fenxing.is_top and highest_fenxing == end_fenxing: 135 | return True 136 | 137 | # 回荡深度超过50%,不满足次高点成笔 138 | if secondary_back_deep > 0: 139 | return False 140 | # 次高点成笔,满足4根k线否 141 | if start_fenxing.is_top: 142 | extreme_fenxing = lowest_fenxing 143 | else: 144 | extreme_fenxing = highest_fenxing 145 | extreme_fenxing_start_bar_index = self.bars[extreme_fenxing.bar_index - 1].include_index 146 | if extreme_fenxing_start_bar_index - start_fenxing_end_bar_index < 1: 147 | # 次高点成笔不足4根k线 148 | return True 149 | else: 150 | # 次高点成笔 151 | return True 152 | 153 | 154 | class Trend(BaseTrend): 155 | def __init__(self, interval: Interval, parent_trend): 156 | super().__init__(interval, parent_trend) 157 | self.pens: List[Pen] = [] 158 | self.bars: List[ChanBarData] = [] 159 | self.yanzheng_fenxing_handler: YanZhengFenxingHandler = None 160 | 161 | def _on_derive_pen(self, start_datetime: datetime, end_datetime: datetime, is_rise: bool): 162 | # 需要修正,推笔不能用分型来表示,应该严格定义到那根K线上 163 | if self.interval == Interval.MINUTE_5: 164 | start_datetime = time_tools.m1_2_m5(start_datetime) 165 | end_datetime = time_tools.m1_2_m5(end_datetime) 166 | elif self.interval == Interval.MINUTE_30: 167 | print("30m pend %s %s" % (start_datetime, end_datetime)) 168 | start_datetime = time_tools.m5_2_m30(start_datetime) 169 | end_datetime = time_tools.m5_2_m30(end_datetime) 170 | print("30m pend %s %s" % (start_datetime, end_datetime)) 171 | 172 | start_bar = None 173 | end_bar = None 174 | for i in range(len(self.bars) - 1, -1, -1): 175 | bar = self.bars[i] 176 | if bar.datetime < start_datetime: 177 | break 178 | if bar.datetime == end_datetime: 179 | end_bar = bar 180 | if bar.datetime == start_datetime: 181 | start_bar = bar 182 | if start_bar and end_bar: 183 | pen = DerivePen(index=len(self.pens), _start_bar=start_bar, _end_bar=end_bar, 184 | is_rise=is_rise) 185 | self.pens.append(pen) 186 | self.pen_dao.insert(pen) 187 | self._handle_segments(False) 188 | self.on_new_pen() 189 | else: 190 | # print(start_datetime, end_datetime) 191 | pass 192 | self._handle_centre() 193 | 194 | def on_bar(self, bar: BarData): 195 | super(Trend, self).on_bar(bar) 196 | if self.yanzheng_fenxing_handler: 197 | self.yanzheng_fenxing_handler.on_new_bar(self.bars[-1]) 198 | 199 | def on_new_fenxing(self): 200 | if self.yanzheng_fenxing_handler: 201 | self.yanzheng_fenxing_handler.on_new_fenxing(self.fenxings[-1]) 202 | 203 | def on_new_pen(self): 204 | # 验证分型处理器更新 205 | self.yanzheng_fenxing_handler = YanZhengFenxingHandler() 206 | # self.yanzheng_fenxing_handler.init(self) 207 | 208 | def on_yanzheng_fenxing_success(self, yanzheng_fenxing: YanZhengFenxing): 209 | last_pen = self.pens[-1] 210 | if yanzheng_fenxing.extreme_fenxing.is_top and yanzheng_fenxing.extreme_fenxing.extreme_bar.include_high > last_pen.high(): 211 | print("二卖", yanzheng_fenxing.confirm_bar.index, "开仓价", yanzheng_fenxing.confirm_bar.close_price, 212 | "止损价", yanzheng_fenxing.loss_price) 213 | elif not yanzheng_fenxing.extreme_fenxing.is_top and yanzheng_fenxing.extreme_fenxing.extreme_bar.include_low > last_pen.low(): 214 | print("二买", yanzheng_fenxing.confirm_bar.index, "开仓价", yanzheng_fenxing.confirm_bar.close_price, 215 | "止损价", yanzheng_fenxing.loss_price) 216 | 217 | 218 | class ChanApp(object): 219 | """ 220 | 做本级别线段 221 | 使用次级别严格笔,推出本级别笔,再推出本级别的中枢和线段 222 | """ 223 | 224 | def __init__(self, level: Level, gateway: type, exchange: Exchange, symbol: str): 225 | """ 226 | 227 | :param level: 228 | :param gateway: 229 | :param exchange: 230 | :param symbol: 231 | """ 232 | self._level = level 233 | self._exchange = exchange 234 | self._symbol = symbol 235 | self._parent_trend = Trend(self._level.get_parent_level(), None) 236 | self._trend = Trend(self._level.get_level(), self._parent_trend) 237 | self._child_trend = SubTrend(self._level.get_child_level(), self._trend) 238 | self._event_engine = EventEngine() 239 | self._gateway: BaseGateway = gateway(self._event_engine) 240 | 241 | def init(self): 242 | init_start_time = time_tools.timestamp() 243 | 244 | sub_klines = 8000 245 | request = KLineRequest(self._symbol, self._exchange, self._level.get_parent_level(), int(sub_klines / 5 / 5)) 246 | bars = self._gateway.get_kline_data(request) 247 | self._parent_trend.init_bars(bars) 248 | 249 | request = KLineRequest(self._symbol, self._exchange, self._level.get_level(), int(sub_klines / 5)) 250 | bars = self._gateway.get_kline_data(request) 251 | self._trend.init_bars(bars) 252 | 253 | request = KLineRequest(self._symbol, self._exchange, self._level.get_child_level(), sub_klines) 254 | bars = self._gateway.get_kline_data(request) 255 | self._child_trend.init_bars(bars) 256 | init_end_time = time_tools.timestamp() 257 | print("init total time %s" % (init_end_time - init_start_time)) 258 | 259 | def start(self): 260 | self.init() 261 | 262 | print("init bar finish") 263 | self._event_engine.register(EVENT_BAR, self.on_bar) 264 | self._event_engine.start() 265 | 266 | self._gateway.subscribe_bar( 267 | SubscribeBarRequest(symbol=self._symbol, exchange=self._exchange, interval=self._level.get_child_level())) 268 | 269 | self._gateway.subscribe_bar( 270 | SubscribeBarRequest(symbol=self._symbol, exchange=self._exchange, interval=self._level.get_level())) 271 | 272 | self._gateway.subscribe_bar( 273 | SubscribeBarRequest(symbol=self._symbol, exchange=self._exchange, interval=self._level.get_parent_level())) 274 | self._gateway.connect(None) 275 | 276 | def on_bar(self, event: Event): 277 | bar: BarData = event.data 278 | # print(bar) 279 | if bar.interval == self._level.get_level(): 280 | self._trend.on_bar(bar) 281 | elif bar.interval == self._level.get_child_level(): 282 | self._child_trend.on_bar(bar) 283 | elif bar.interval == self._level.get_parent_level(): 284 | self._parent_trend.on_bar(bar) 285 | 286 | # dataframe_60 = getKLineData("600887.SS", scale=MINUTE_60, start_date="20181010", end_date="20181118") 287 | # dataframe_60 = getKLineData("SH603019", scale=MINUTE_60, count=len(dataframe) / 12) 288 | # handle_include(dataframe_60) 289 | # handle_fenxing(dataframe_60) 290 | # pens_60 = handle_pen(dataframe_60) 291 | # if pens_60: 292 | # print dataframe_60.ix[pens_60[0]["start"]] 293 | 294 | # app_log.info("处理数据完毕:%s", datetime.datetime.now()) 295 | # 296 | # json_str = dataframe.to_json(orient="records") 297 | # data = encoder.loads(json_str) 298 | # data = {"kline": data, "pens": pens} 299 | # with open("../static/data.encoder", "w") as fp: 300 | # encoder.dump(data, fp) 301 | # fp.close() 302 | # # break 303 | # time.sleep(60) 304 | 305 | # plt.figure() 306 | # pl = dataframe.plot.box() 307 | # plt.show() 308 | -------------------------------------------------------------------------------- /chan/constant.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from enum import Enum 3 | 4 | __author__ = "hanweiwei" 5 | __date__ = "2019-06-02" 6 | 7 | 8 | -------------------------------------------------------------------------------- /chan/db/dao.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from typing import List 3 | 4 | from chan.db.db_helper import get_session 5 | from chan.db.object import PenDO, ChanBarDataDO, CentreDO 6 | from chan.mapper import ChanBarDataMP, PenMP, CentreMP 7 | from chan.object import ChanBarData, Pen, Centre 8 | 9 | __author__ = "hanweiwei" 10 | __date__ = "2019-06-06" 11 | 12 | 13 | class ChanBarDataDao(): 14 | 15 | def __init__(self): 16 | self.chan_bar_data_mp = ChanBarDataMP() 17 | 18 | def insert(self, chan_bar_data: ChanBarData): 19 | chan_bar_data_do = self.chan_bar_data_mp.mapper(chan_bar_data) 20 | 21 | # 创建session对象: 22 | session = get_session() 23 | # 添加到session: 24 | session.merge(chan_bar_data_do) 25 | # 提交即保存到数据库: 26 | session.commit() 27 | # 关闭session: 28 | session.close() 29 | 30 | def select(self, gateway, exchange, symbol, interval, limit): 31 | session = get_session() 32 | bars: List = session.query(ChanBarDataDO) \ 33 | .filter(ChanBarDataDO.gateway == gateway) \ 34 | .filter(ChanBarDataDO.exchange == exchange) \ 35 | .filter(ChanBarDataDO.symbol == symbol) \ 36 | .filter(ChanBarDataDO.interval == interval) \ 37 | .order_by(ChanBarDataDO.datetime.desc()) \ 38 | .limit(limit) \ 39 | .all() 40 | session.close() 41 | bars.reverse() 42 | return bars 43 | 44 | 45 | class PenDao(): 46 | 47 | def __init__(self): 48 | self.pen_mp = PenMP() 49 | 50 | def insert(self, pen: Pen): 51 | pen_do = self.pen_mp.mapper(pen) 52 | 53 | # 创建session对象: 54 | session = get_session() 55 | # 添加到session: 56 | 57 | old_pen = session.query(PenDO) \ 58 | .filter(PenDO.gateway == pen_do.gateway) \ 59 | .filter(PenDO.exchange == pen_do.exchange) \ 60 | .filter(PenDO.symbol == pen_do.symbol) \ 61 | .filter(PenDO.interval == pen_do.interval) \ 62 | .filter(PenDO.start_datetime == pen_do.start_datetime) \ 63 | .first() 64 | if old_pen: 65 | session.delete(old_pen) 66 | session.add(pen_do) 67 | # 提交即保存到数据库: 68 | session.commit() 69 | # 关闭session: 70 | session.close() 71 | 72 | def select(self, gateway, exchange, symbol, interval, start_time): 73 | session = get_session() 74 | pens: List[PenDO] = session.query(PenDO) \ 75 | .filter(PenDO.gateway == gateway) \ 76 | .filter(PenDO.exchange == exchange) \ 77 | .filter(PenDO.symbol == symbol) \ 78 | .filter(PenDO.interval == interval) \ 79 | .filter(PenDO.start_datetime >= start_time) \ 80 | .all() 81 | session.close() 82 | return pens 83 | 84 | def delete(self, pen: Pen): 85 | pen_do = self.pen_mp.mapper(pen) 86 | 87 | # 创建session对象: 88 | session = get_session() 89 | # 添加到session: 90 | 91 | old_pen = session.query(PenDO) \ 92 | .filter(PenDO.gateway == pen_do.gateway) \ 93 | .filter(PenDO.exchange == pen_do.exchange) \ 94 | .filter(PenDO.symbol == pen_do.symbol) \ 95 | .filter(PenDO.interval == pen_do.interval) \ 96 | .filter(PenDO.start_datetime == pen_do.start_datetime) \ 97 | .first() 98 | if old_pen: 99 | session.delete(old_pen) 100 | # 提交即保存到数据库: 101 | session.commit() 102 | # 关闭session: 103 | session.close() 104 | 105 | 106 | class CentreDao(): 107 | 108 | def __init__(self): 109 | self.centre_mp = CentreMP() 110 | 111 | def insert(self, centre: Centre): 112 | centre_do = self.centre_mp.mapper(centre) 113 | 114 | # 创建session对象: 115 | session = get_session() 116 | # 添加到session: 117 | 118 | old_centre = session.query(CentreDO) \ 119 | .filter(CentreDO.gateway == centre_do.gateway) \ 120 | .filter(CentreDO.exchange == centre_do.exchange) \ 121 | .filter(CentreDO.symbol == centre_do.symbol) \ 122 | .filter(CentreDO.interval == centre_do.interval) \ 123 | .filter(CentreDO.start_datetime == centre_do.start_datetime) \ 124 | .first() 125 | if old_centre: 126 | session.delete(old_centre) 127 | session.add(centre_do) 128 | # 提交即保存到数据库: 129 | session.commit() 130 | # 关闭session: 131 | session.close() 132 | 133 | def select(self, gateway, exchange, symbol, interval, start_time): 134 | session = get_session() 135 | centres: List[CentreDO] = session.query(CentreDO) \ 136 | .filter(CentreDO.gateway == gateway) \ 137 | .filter(CentreDO.exchange == exchange) \ 138 | .filter(CentreDO.symbol == symbol) \ 139 | .filter(CentreDO.interval == interval) \ 140 | .filter(CentreDO.start_datetime >= start_time) \ 141 | .all() 142 | session.close() 143 | return centres 144 | -------------------------------------------------------------------------------- /chan/db/db_helper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from sqlalchemy import create_engine, MetaData 3 | from sqlalchemy.orm import sessionmaker, Session 4 | 5 | from chan.db.object import Base, BaseDO 6 | 7 | __author__ = "hanweiwei" 8 | __date__ = "2019-06-06" 9 | 10 | # 初始化数据库连接: 11 | engine = create_engine('sqlite:////tmp/ChanMaster/chan.db', echo=False) 12 | BaseDO.metadata.create_all(engine) 13 | # 创建DBSession类型: 14 | DBSession = sessionmaker(bind=engine) 15 | 16 | 17 | # 创建DBSession类型: 18 | 19 | 20 | def get_session(): 21 | """ 22 | :rtype : Session 23 | :return: 24 | """ 25 | return DBSession() 26 | -------------------------------------------------------------------------------- /chan/db/object.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from sqlalchemy import Column, String, INT, FLOAT, DATETIME, BOOLEAN 4 | from sqlalchemy.ext.declarative import declarative_base 5 | 6 | __author__ = "hanweiwei" 7 | __date__ = "2019-06-03" 8 | 9 | 10 | class Base(object): 11 | pass 12 | # def to_json(self): 13 | # dict = self.__dict__ 14 | # if "_sa_instance_state" in dict: 15 | # del dict["_sa_instance_state"] 16 | # return dict 17 | 18 | 19 | # 创建对象的基类: 20 | BaseDO = declarative_base(Base) 21 | 22 | 23 | # 定义User对象: 24 | class ChanBarDataDO(BaseDO): 25 | """ 26 | create table chan_bar_data 27 | ( 28 | gateway VARCHAR(10) not null, 29 | exchange VARCHAR(10) not null, 30 | symbol VARCHAR(10) not null, 31 | datetime DATETIME not null, 32 | interval VARCHAR(5) not null, 33 | volume INT, 34 | open_price FLOAT, 35 | close_price FLOAT, 36 | high_price FLOAT, 37 | low_price FLOAT, 38 | include_high FLOAT, 39 | include_low FLOAT, 40 | include_index INT, 41 | include_tail INT, 42 | primary key (gateway, exchange, symbol, datetime, interval) 43 | ) 44 | IF NOT EXISTS; 45 | """ 46 | 47 | # 表的名字: 48 | __tablename__ = 'chan_bar_data' 49 | 50 | # 表的结构: 51 | gateway = Column(String(10), primary_key=True) 52 | exchange = Column(String(10), primary_key=True) 53 | symbol = Column(String(10), primary_key=True) 54 | interval = Column(String(5), primary_key=True) 55 | datetime = Column(DATETIME, primary_key=True) 56 | volume = Column(INT) 57 | open_price = Column(FLOAT) 58 | close_price = Column(FLOAT) 59 | high_price = Column(FLOAT) 60 | low_price = Column(FLOAT) 61 | include_high = Column(FLOAT) 62 | include_low = Column(FLOAT) 63 | include_index = Column(INT) 64 | include_tail = Column(INT) 65 | 66 | 67 | class PenDO(BaseDO): 68 | __tablename__ = 'pen' 69 | 70 | # 表的结构: 71 | gateway = Column(String(10), primary_key=True) 72 | exchange = Column(String(10), primary_key=True) 73 | symbol = Column(String(10), primary_key=True) 74 | interval = Column(String(5), primary_key=True) 75 | start_datetime = Column(DATETIME, primary_key=True) 76 | end_datetime = Column(DATETIME, primary_key=True) 77 | is_rise = Column(BOOLEAN) 78 | 79 | 80 | class CentreDO(BaseDO): 81 | __tablename__ = 'centre' 82 | 83 | # 表的结构: 84 | gateway = Column(String(10), primary_key=True) 85 | exchange = Column(String(10), primary_key=True) 86 | symbol = Column(String(10), primary_key=True) 87 | interval = Column(String(5), primary_key=True) 88 | start_datetime = Column(DATETIME, primary_key=True) 89 | end_datetime = Column(DATETIME, primary_key=True) 90 | high = Column(FLOAT) 91 | low = Column(FLOAT) 92 | is_rise = Column(BOOLEAN) 93 | -------------------------------------------------------------------------------- /chan/level.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from trader.constant import Interval 3 | 4 | __author__ = "hanweiwei" 5 | __date__ = "2019-06-02" 6 | 7 | 8 | class Level(object): 9 | 10 | def get_level(self): 11 | raise NotImplementedError 12 | 13 | def get_child_level(self): 14 | raise NotImplementedError 15 | 16 | def get_parent_level(self): 17 | raise NotImplementedError 18 | 19 | def get_grand_level(self): 20 | raise NotImplementedError 21 | 22 | 23 | class ThirtyMinuteLevel(Level): 24 | """ 25 | 5分钟级别 26 | """ 27 | 28 | def get_level(self): 29 | return Interval.MINUTE_30 30 | 31 | def get_child_level(self): 32 | return Interval.MINUTE_5 33 | 34 | def get_parent_level(self): 35 | return Interval.DAILY 36 | 37 | def get_grand_level(self): 38 | return Interval.DAILY 39 | 40 | 41 | class FiveMinuteLevel(Level): 42 | """ 43 | 5分钟级别 44 | """ 45 | 46 | def get_level(self): 47 | return Interval.MINUTE_5 48 | 49 | def get_child_level(self): 50 | return Interval.MINUTE 51 | 52 | def get_parent_level(self): 53 | return Interval.MINUTE_30 54 | 55 | def get_grand_level(self): 56 | return Interval.DAILY 57 | 58 | 59 | class OneMinuteLevel(Level): 60 | 61 | def get_level(self): 62 | return Interval.MINUTE 63 | 64 | def get_child_level(self): 65 | return Interval.SECOND_10 66 | 67 | def get_parent_level(self): 68 | return Interval.MINUTE_5 69 | 70 | def get_grand_level(self): 71 | return Interval.MINUTE_30 72 | 73 | 74 | def parse_level(level): 75 | if level == "5m": 76 | return FiveMinuteLevel() 77 | elif level == "1m": 78 | return OneMinuteLevel() 79 | elif level == "30m": 80 | return ThirtyMinuteLevel() 81 | -------------------------------------------------------------------------------- /chan/mapper.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from chan.db.object import ChanBarDataDO, PenDO, CentreDO 3 | from chan.object import ChanBarData, Pen, Centre 4 | 5 | __author__ = "hanweiwei" 6 | __date__ = "2019-06-03" 7 | 8 | 9 | class ChanBarDataMP: 10 | 11 | def mapper(self, chan_bar_data: ChanBarData): 12 | """ 13 | 14 | :param chan_bar_data: 15 | :rtype :ChanBarDataDO 16 | :return: 17 | """ 18 | return ChanBarDataDO( 19 | gateway=chan_bar_data.gateway_name, 20 | exchange=chan_bar_data.exchange.value, 21 | symbol=chan_bar_data.symbol, 22 | datetime=chan_bar_data.datetime, 23 | interval=chan_bar_data.interval.value, 24 | volume=chan_bar_data.volume, 25 | open_price=chan_bar_data.open_price, 26 | close_price=chan_bar_data.close_price, 27 | high_price=chan_bar_data.high_price, 28 | low_price=chan_bar_data.low_price, 29 | include_high=chan_bar_data.include_high, 30 | include_low=chan_bar_data.include_low, 31 | include_index=chan_bar_data.include_index, 32 | include_tail=chan_bar_data.include_tail 33 | ) 34 | 35 | 36 | class PenMP: 37 | 38 | def mapper(self, pen: Pen): 39 | """ 40 | 41 | :param pen: 42 | :rtype :PenDO 43 | :return: 44 | """ 45 | return PenDO( 46 | gateway=pen.start_bar().gateway_name, 47 | exchange=pen.start_bar().exchange.value, 48 | symbol=pen.start_bar().symbol, 49 | interval=pen.start_bar().interval.value, 50 | start_datetime=pen.start_bar().datetime, 51 | end_datetime=pen.end_bar().datetime, 52 | is_rise=pen.is_rise 53 | ) 54 | 55 | 56 | class CentreMP: 57 | 58 | def mapper(self, centre: Centre): 59 | """ 60 | 61 | :param centre: 62 | :rtype :CentreDO 63 | :return: 64 | """ 65 | return CentreDO( 66 | gateway=centre.start_pen.start_bar().gateway_name, 67 | exchange=centre.start_pen.start_bar().exchange.value, 68 | symbol=centre.start_pen.start_bar().symbol, 69 | interval=centre.start_pen.start_bar().interval.value, 70 | start_datetime=centre.start_pen.start_bar().datetime, 71 | end_datetime=centre.end_pen.end_bar().datetime, 72 | is_rise=centre.is_rise, 73 | high=centre.high, 74 | low=centre.low 75 | ) 76 | -------------------------------------------------------------------------------- /chan/object.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from dataclasses import dataclass 3 | 4 | from trader.object import BarData 5 | 6 | __author__ = "hanweiwei" 7 | __date__ = "2019-06-02" 8 | 9 | 10 | @dataclass 11 | class ChanBarData(BarData): 12 | index: int = 0 13 | include_high: float = 0 14 | include_low: float = 0 15 | include_index: int = -1 16 | include_tail: int = -1 17 | 18 | def is_include(self): 19 | return self.include_index != -1 20 | 21 | def start_index(self): 22 | if self.is_include(): 23 | return self.include_index 24 | else: 25 | return self.index 26 | 27 | def end_index(self): 28 | if self.is_include(): 29 | return self.include_tail 30 | else: 31 | return self.index 32 | 33 | 34 | @dataclass 35 | class Fenxing(object): 36 | index: int = 0 37 | is_top: bool = False # 是否顶分 38 | extreme_bar: ChanBarData = None 39 | bar_index: int = 0 40 | start_bar: ChanBarData = None 41 | end_bar: ChanBarData = None 42 | 43 | 44 | @dataclass 45 | class BasePen(object): 46 | def start_bar(self): 47 | raise NotImplementedError 48 | 49 | def end_bar(self): 50 | raise NotImplementedError 51 | 52 | def high(self): 53 | raise NotImplementedError 54 | 55 | def low(self): 56 | raise NotImplementedError 57 | 58 | 59 | @dataclass 60 | class Pen(BasePen): 61 | index: int 62 | start_fenxing: Fenxing 63 | end_fenxing: Fenxing 64 | is_rise: bool 65 | 66 | def start_bar(self): 67 | return self.start_fenxing.extreme_bar 68 | 69 | def start_bar_index(self): 70 | return self.start_fenxing.bar_index 71 | 72 | def end_bar(self): 73 | return self.end_fenxing.extreme_bar 74 | 75 | def end_bar_index(self): 76 | return self.end_fenxing.bar_index 77 | 78 | def high(self): 79 | if self.is_rise: 80 | return self.end_bar().include_high 81 | else: 82 | return self.start_bar().include_high 83 | 84 | def low(self): 85 | if self.is_rise: 86 | return self.start_bar().include_low 87 | else: 88 | return self.end_bar().include_low 89 | 90 | 91 | @dataclass 92 | class DerivePen(BasePen): 93 | index: int 94 | _start_bar: ChanBarData 95 | _end_bar: ChanBarData 96 | is_rise: bool = False 97 | 98 | def start_bar(self): 99 | return self._start_bar 100 | 101 | def end_bar(self): 102 | return self._end_bar 103 | 104 | def high(self): 105 | if self.is_rise: 106 | return self.end_bar().include_high 107 | else: 108 | return self.start_bar().include_high 109 | 110 | def low(self): 111 | if self.is_rise: 112 | return self.start_bar().include_low 113 | else: 114 | return self.end_bar().include_low 115 | 116 | 117 | @dataclass 118 | class Divider(object): 119 | """ 120 | 同级别分界线 121 | """ 122 | last_pen: Pen 123 | confirm_pen: Pen 124 | 125 | 126 | @dataclass 127 | class Centre(object): 128 | """ 129 | 中枢 130 | """ 131 | 132 | high: float #  中枢上边界 133 | low: float # 中枢下边界 134 | is_rise: bool # 上涨中枢、下跌中枢 135 | start_pen: Pen # 起笔 136 | end_pen: Pen # 结束笔 137 | 138 | 139 | @dataclass 140 | class YanZhengFenxing(object): 141 | extreme_fenxing: Fenxing 142 | confirm_fenxing: Fenxing 143 | confirm_bar: ChanBarData 144 | loss_price: float 145 | invalid: bool = False # 验证分型是否已经失效 146 | 147 | def should_stop_loss(self, bar: BarData): 148 | return self.extreme_fenxing.is_top and bar.close_price > self.loss_price \ 149 | or \ 150 | not self.extreme_fenxing.is_top and bar.close_price < self.loss_price 151 | -------------------------------------------------------------------------------- /chan/sequence.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-06-09" 5 | 6 | 7 | class SignatureSequenceItem(object): 8 | """ 9 | 特征序列 10 | 11 | 12 | """ 13 | EXTEND = 1 # 延伸 14 | TURN = 2 # 转折 15 | INCLUDE = 3 # 包含 16 | 17 | def __init__(self, is_rise, extremum_pen_index, high, low): 18 | self.is_rise = is_rise 19 | self.extremum_pen_index = extremum_pen_index # 极值点笔索引。极值点:上升线段的最高点,下降线段的最低点 20 | # 高低点用作包含关系和分型的判断 21 | self.high = high # 高点 22 | self.low = low # 低点 23 | 24 | def __str__(self): 25 | return "is_rise:%s,extremum_pen_index:%s,high:%s,low:%s" % ( 26 | self.is_rise, self.extremum_pen_index, self.high, self.low) 27 | 28 | @classmethod 29 | def create_by_pen(cls, pen): 30 | """ 31 | :type pen:Pen 32 | :return: 33 | """ 34 | return SignatureSequenceItem(not pen.is_rise, pen.index, pen.high(), pen.low()) 35 | 36 | def grow(self, next_signature): 37 | """ 38 | 特征序列的生长 39 | 特征序列之间的二种关系: 40 | 延伸:沿着线段方向运行,可能有相互包含 41 | 转折:特征序列形成了分型,导致线段终结 42 | :type next_signature: SignatureSequenceItem 43 | :param next_signature: 44 | :return: relation, signature : 特征序列关系 和 极值特征序列 45 | """ 46 | if self.high < next_signature.high and self.low < next_signature.low and self.is_rise or \ 47 | self.high > next_signature.high and self.low > next_signature.low and not self.is_rise: 48 | # 延伸,没有包含 49 | relation = SignatureSequenceItem.EXTEND 50 | return relation, next_signature 51 | 52 | elif self.high < next_signature.high and self.low < next_signature.low and not self.is_rise or \ 53 | self.high > next_signature.high and self.low > next_signature.low and self.is_rise: 54 | # 直接转折 55 | relation = SignatureSequenceItem.TURN 56 | # if self.is_rise: 57 | return relation, self 58 | # else: 59 | # return relation, next_signature 60 | else: 61 | # 处于包含关系, 但也算延伸 62 | relation = SignatureSequenceItem.EXTEND 63 | if self.low <= next_signature.low and self.high >= next_signature.high: 64 | # 左包含右 65 | if self.is_rise: 66 | ret_signature = SignatureSequenceItem(self.is_rise, self.extremum_pen_index, 67 | self.high, 68 | next_signature.low) 69 | else: 70 | ret_signature = SignatureSequenceItem(self.is_rise, self.extremum_pen_index, 71 | next_signature.high, 72 | self.low) 73 | else: 74 | # if self.is_rise: 75 | ret_signature = next_signature 76 | # 笔破坏 77 | if self.is_rise: 78 | ret_signature = SignatureSequenceItem(self.is_rise, next_signature.extremum_pen_index, 79 | next_signature.high, 80 | self.low) 81 | else: 82 | ret_signature = SignatureSequenceItem(self.is_rise, next_signature.extremum_pen_index, 83 | self.high, 84 | next_signature.low) 85 | return relation, ret_signature 86 | -------------------------------------------------------------------------------- /chan/trade_manager.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-06-09" 5 | 6 | 7 | class TradeManager(object): 8 | 9 | def __init__(self): 10 | pass 11 | 12 | 13 | -------------------------------------------------------------------------------- /chan/trend.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from abc import abstractmethod 3 | from datetime import datetime 4 | from typing import List 5 | 6 | from chan.db.dao import ChanBarDataDao, CentreDao, PenDao 7 | from chan.object import ChanBarData, Fenxing, Divider, Centre, Pen, YanZhengFenxing 8 | from chan.sequence import SignatureSequenceItem 9 | from trader.constant import Interval 10 | from trader.object import BarData 11 | 12 | __author__ = "hanweiwei" 13 | __date__ = "2019-06-09" 14 | 15 | 16 | class BaseTrend(object): 17 | """ 18 | 走势 19 | """ 20 | 21 | def __init__(self, interval: Interval, parent_trend): 22 | """ 23 | 24 | :param parentTrend: 25 | :type parentTrend:BaseTrend 26 | """ 27 | self.interval = interval 28 | self.parent_trend: BaseTrend = parent_trend 29 | 30 | self.chan_bar_data_dao = ChanBarDataDao() 31 | self.pen_dao = PenDao() 32 | self.centre_dao = CentreDao() 33 | 34 | self.bars: List[ChanBarData] = [] 35 | self.fenxings: List[Fenxing] = [] 36 | self.pens: List[Pen] = [] 37 | self.dividers: List[Divider] = [] 38 | self.centres: List[Centre] = [] 39 | 40 | # 当前是否上涨趋势 41 | self.is_rise = True 42 | 43 | def init_bars(self, bars: List[BarData]): 44 | for index, bar in enumerate(bars): 45 | # if index > len(bars) / 2: 46 | # break 47 | self._append_bar(bar) 48 | 49 | def on_bar(self, bar: BarData): 50 | self._append_bar(bar) 51 | 52 | @abstractmethod 53 | def on_new_fenxing(self): 54 | pass 55 | 56 | @abstractmethod 57 | def on_new_pen(self): 58 | pass 59 | 60 | @abstractmethod 61 | def _on_derive_pen(self, start_datetime: datetime, end_datetime: datetime, is_rise: bool): 62 | 63 | """ 64 | 次级别推出本级别的笔 65 | :param pen: 66 | :return: 67 | """ 68 | 69 | @abstractmethod 70 | def _handle_pens(self): 71 | pass 72 | 73 | def _append_bar(self, bar: BarData): 74 | index = len(self.bars) 75 | row = ChanBarData(symbol=bar.symbol, exchange=bar.exchange, interval=bar.interval, datetime=bar.datetime, 76 | open_price=bar.open_price, high_price=bar.high_price, low_price=bar.low_price, 77 | close_price=bar.close_price, volume=bar.volume, gateway_name=bar.gateway_name, 78 | include_high=bar.high_price, include_low=bar.low_price, 79 | include_index=-1, include_tail=-1, index=index) 80 | if len(self.bars) > 2: 81 | last_index = index - 1 82 | last_row = self.bars[last_index] 83 | if last_row.is_include(): 84 | # 前一根K线也是包含, 找此包含关系的第一根K线的前一根K线 85 | last_last_row = self.bars[last_row.include_index - 1] 86 | else: 87 | last_last_row = self.bars[index - 2] 88 | before_include_after = (last_row.include_high >= row.high_price) and ( 89 | last_row.include_low <= row.low_price) 90 | after_include_before = (last_row.include_high <= row.high_price) and ( 91 | last_row.include_low >= row.low_price) 92 | if before_include_after: 93 | # 前包后 94 | if last_last_row.include_high > last_row.include_high: 95 | # 低低 96 | row.include_high = row.high_price 97 | row.include_low = last_row.include_low 98 | else: 99 | # 高高 100 | row.include_high = last_row.include_high 101 | row.include_low = row.low_price 102 | if last_row.is_include(): 103 | row.include_index = last_row.include_index 104 | else: 105 | row.include_index = last_index 106 | last_row.include_index = last_index 107 | for i in range(row.include_index, row.index): 108 | self.bars[i].include_low = row.include_low 109 | self.bars[i].include_high = row.include_high 110 | elif after_include_before: 111 | # 后包前 112 | if last_last_row.include_high > last_row.include_high: 113 | # 低低 114 | row.include_high = last_row.include_high 115 | row.include_low = row.low_price 116 | else: 117 | # 高高 118 | row.include_high = row.high_price 119 | row.include_low = last_row.include_low 120 | if last_row.is_include(): 121 | row.include_index = last_row.include_index 122 | else: 123 | row.include_index = last_index 124 | last_row.include_index = last_index 125 | for i in range(row.include_index, row.index): 126 | self.bars[i].include_low = row.include_low 127 | self.bars[i].include_high = row.include_high 128 | else: 129 | if last_row.is_include(): 130 | for i in range(last_index, last_row.include_index - 1, -1): 131 | _temp_row = self.bars[i] 132 | _temp_row.include_tail = last_index 133 | self.bars.append(row) 134 | self.chan_bar_data_dao.insert(row) 135 | self._handle_fenxing() 136 | 137 | def _handle_fenxing(self): 138 | index = len(self.bars) - 1 139 | cur = self.bars[len(self.bars) - 1] 140 | if cur.is_include(): 141 | # 有包含关系,所以这跟k线不会产生新的分型 142 | return 143 | else: 144 | last_index = index - 1 145 | if last_index < 0: 146 | return 147 | last = self.bars[last_index] 148 | 149 | if last.is_include(): 150 | # 取包含关系的第一根K线的前一根K线作为last 151 | last_last_index = last.include_index - 1 152 | else: 153 | last_last_index = last_index - 1 154 | if last_last_index < 0: 155 | return 156 | last_last = self.bars[last_last_index] 157 | 158 | if last_last.include_high > last.include_high and cur.include_high > last.include_high: 159 | # 底分型 160 | if last.is_include(): 161 | for i in range(last.start_index(), last.end_index() + 1): 162 | bar = self.bars[i] 163 | if bar.low_price == bar.include_low: 164 | extreme_bar = bar 165 | else: 166 | extreme_bar = last 167 | self.fenxings.append( 168 | Fenxing(index=len(self.fenxings), is_top=False, extreme_bar=extreme_bar, bar_index=extreme_bar.index, 169 | start_bar=last_last, end_bar=cur)) 170 | elif last_last.include_high < last.include_high and cur.include_high < last.include_high: 171 | # 顶分型 172 | if last.is_include(): 173 | for i in range(last.start_index(), last.end_index() + 1): 174 | bar = self.bars[i] 175 | if bar.high_price == bar.include_high: 176 | extreme_bar = bar 177 | else: 178 | extreme_bar = last 179 | self.fenxings.append( 180 | Fenxing(index=len(self.fenxings), is_top=True, extreme_bar=extreme_bar, bar_index=extreme_bar.index, 181 | start_bar=last_last, end_bar=cur)) 182 | else: 183 | # 有新的分型产生,才会产生新的笔 184 | return 185 | self._handle_pens() 186 | self.on_new_fenxing() 187 | 188 | def _handle_segments(self, has_fix_pen=False): 189 | """ 190 | 线段 191 | :return: 192 | """ 193 | if len(self.pens) < 3: return 194 | 195 | if has_fix_pen: 196 | while self.dividers: 197 | divider = self.dividers[-1] 198 | if self.pens[-1].index <= divider.confirm_pen.index: 199 | # 修正笔导致之前的线段划分失效 200 | self.dividers.remove(divider) 201 | continue 202 | break 203 | 204 | if self.dividers: 205 | divider = self.dividers[-1] 206 | # 为什么直接+4: 因为确认前一个同级别分界线时,新的线段至少已经走了三笔,可以直接从第四笔取特征序列来进行后续的判断 207 | signature_sequence_index = divider.confirm_pen.index + 1 208 | if signature_sequence_index >= len(self.pens): 209 | return 210 | start_pen = self.pens[divider.last_pen.index + 1] 211 | 212 | else: 213 | signature_sequence_index = 0 # 当前特征序列在pens列表中的索引 214 | current_pen = self.pens[signature_sequence_index] 215 | 216 | # 始终把初始走势当成上涨线段 217 | # 第一笔下跌,这一笔是特征序列的0号 218 | # 第一笔上涨,就把第二笔当作特征序列的0号,不管第一笔 219 | if current_pen.is_rise: 220 | # 第一笔上涨,就把第二笔当作特征序列的0号,不管第一笔 221 | # 第一笔就是线段的开始 222 | signature_sequence_index += 1 223 | start_pen = self.pens[0] 224 | else: 225 | # 第一笔下跌,把第二笔作为线段的开始 226 | start_pen = self.pens[signature_sequence_index + 1] 227 | current_pen = self.pens[signature_sequence_index] 228 | # print(current_pen.index) 229 | current_signature = SignatureSequenceItem.create_by_pen(current_pen) 230 | signature_sequence_index += 2 231 | while signature_sequence_index < len(self.pens): 232 | next_signature = SignatureSequenceItem.create_by_pen(self.pens[signature_sequence_index]) 233 | relation, ret_signature = current_signature.grow(next_signature) 234 | if self.interval == Interval.MINUTE_5: 235 | print(current_signature.extremum_pen_index, current_signature.high, current_signature.low, 236 | next_signature.extremum_pen_index, 237 | next_signature.high, next_signature.low, relation, 238 | ret_signature.extremum_pen_index) 239 | if relation == SignatureSequenceItem.EXTEND: 240 | # 线段的延伸 241 | current_signature = ret_signature 242 | signature_sequence_index += 2 243 | elif ret_signature == SignatureSequenceItem.INCLUDE: 244 | current_signature = ret_signature 245 | signature_sequence_index += 2 246 | else: 247 | # 线段的转折 248 | pen = self.pens[ret_signature.extremum_pen_index] 249 | divider = Divider(self.pens[pen.index - 1], confirm_pen=self.pens[signature_sequence_index]) 250 | self.dividers.append(divider) 251 | 252 | # 推笔 253 | if self.parent_trend: 254 | self.parent_trend._on_derive_pen(start_pen.start_bar().datetime, 255 | divider.last_pen.end_bar().datetime, 256 | ret_signature.is_rise) 257 | signature_sequence_index += 1 258 | if signature_sequence_index >= len(self.pens): 259 | break 260 | start_pen = self.pens[divider.last_pen.index + 1] 261 | current_signature = SignatureSequenceItem.create_by_pen(self.pens[signature_sequence_index]) # 特征序列的1号 262 | signature_sequence_index += 2 263 | 264 | def _handle_centre(self): 265 | """ 266 | 处理中枢 267 | :return: 268 | """ 269 | if len(self.pens) < 3: 270 | return 271 | if self.centres: 272 | cur_centre = self.centres[-1] 273 | cur_pen = self.pens[-1] 274 | if cur_pen.index - cur_centre.end_pen.index == 1: 275 | if not (cur_pen.high() < cur_centre.low or cur_pen.low() > cur_centre.high): 276 | # 中枢维持 277 | cur_centre.end_pen = cur_pen 278 | self.centre_dao.insert(cur_centre) 279 | return 280 | else: 281 | # 中枢破坏 282 | return 283 | elif cur_pen.index <= cur_centre.end_pen.index: 284 | # 没有产生新的笔,是修正笔 285 | # 最简单就是把这个中枢删掉,重新生成个中枢。 麻烦点,就去删掉之前中枢中的笔,重新确定中枢的属性。 286 | # 这里采用简单的方法 287 | self.centres.remove(cur_centre) 288 | 289 | # 新中枢判断 290 | if self.centres: 291 | cur_pen = self.pens[self.centres[-1].end_pen.index + 1] # 这个笔是出三买和三卖的笔 292 | else: 293 | cur_pen = self.pens[0] 294 | if cur_pen.is_rise: 295 | cur_pen = self.pens[1] 296 | 297 | new_centre = None 298 | cur_index = cur_pen.index 299 | next_index = cur_pen.index + 2 300 | while next_index < len(self.pens): 301 | cur_pen = self.pens[cur_index] 302 | next_pen = self.pens[next_index] 303 | if not (cur_pen.high() < next_pen.low() or cur_pen.low() > next_pen.high()): 304 | centre_low = cur_pen.low() if cur_pen.low() > next_pen.low() else next_pen.low() 305 | centre_high = cur_pen.high() if cur_pen.high() < next_pen.high() else next_pen.high() 306 | new_centre = Centre(high=centre_high, low=centre_low, is_rise=not cur_pen.is_rise, start_pen=cur_pen, 307 | end_pen=next_pen) 308 | self.centres.append(new_centre) 309 | self.centre_dao.insert(new_centre) 310 | break 311 | cur_index += 2 312 | next_index += 2 313 | 314 | # 产生了新中枢,去看中枢是否延伸 315 | if not new_centre: 316 | return 317 | cur_index = next_index + 1 318 | while cur_index < len(self.pens): 319 | cur_pen = self.pens[cur_index] 320 | if not (cur_pen.high() < new_centre.low or cur_pen.low() > new_centre.high): 321 | # 中枢维持 322 | new_centre.end_pen = cur_pen 323 | self.centre_dao.insert(new_centre) 324 | cur_index += 1 325 | continue 326 | break 327 | 328 | def on_yanzheng_fenxing_success(self, yanzheng_fenxing: YanZhengFenxing): 329 | pass 330 | -------------------------------------------------------------------------------- /chan/yanzheng_fenxing_handler.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from typing import List 3 | 4 | from chan.object import Fenxing, YanZhengFenxing, ChanBarData 5 | from chan.trend import BaseTrend 6 | from trader.object import BarData 7 | 8 | __author__ = "hanweiwei" 9 | __date__ = "2019-06-09" 10 | 11 | 12 | class YanZhengFenxingHandler(object): 13 | """ 14 | 新产生一个极值点分型, 就有可能出多个验证分型 15 | """ 16 | trend: BaseTrend = None 17 | extreme_fenxing: Fenxing = None 18 | confirm_fenxing: Fenxing = None # 正在判定中的验证分型 19 | fenxing_pause: bool = False # 是否分型停顿 20 | complete_yanzheng_fenxings: List[YanZhengFenxing] = [] # 已完成的验证分型 21 | judge_price: float = 0 22 | 23 | def on_new_fenxing(self, fenxing: Fenxing): 24 | if not self.extreme_fenxing and self.trend.pens[-1].end_fenxing.is_top!=fenxing.is_top: 25 | self.extreme_fenxing= fenxing 26 | return 27 | if fenxing.is_top != self.extreme_fenxing.is_top: 28 | return 29 | if fenxing.is_top and fenxing.extreme_bar.include_high > self.extreme_fenxing.extreme_bar.include_high: 30 | self.extreme_fenxing = fenxing 31 | self.confirm_fenxing = None 32 | self.fenxing_pause = False 33 | elif not fenxing.is_top and fenxing.extreme_bar.include_low < self.extreme_fenxing.extreme_bar.include_low: 34 | self.extreme_fenxing = fenxing 35 | self.confirm_fenxing = None 36 | self.fenxing_pause = False 37 | else: 38 | if self.complete_yanzheng_fenxings: 39 | last_yanzheng_fenxing = self.complete_yanzheng_fenxings[-1] 40 | if last_yanzheng_fenxing.extreme_fenxing == self.extreme_fenxing and not last_yanzheng_fenxing.invalid: 41 | # 上一个验证分型未失效 42 | return 43 | 44 | self.confirm_fenxing = fenxing 45 | if self.fenxing_pause: 46 | # 1.分型停顿 + 验证分型 47 | if self.confirm_fenxing.is_top: 48 | loss_price = self.confirm_fenxing.extreme_bar.include_high 49 | else: 50 | loss_price = self.confirm_fenxing.extreme_bar.include_low 51 | yanzheng_fenxing = YanZhengFenxing(extreme_fenxing=self.extreme_fenxing, 52 | confirm_fenxing=self.confirm_fenxing, loss_price=loss_price, 53 | confirm_bar=self.confirm_fenxing.end_bar) 54 | self.complete_yanzheng_fenxings.append(yanzheng_fenxing) 55 | self.trend.on_yanzheng_fenxing_success(yanzheng_fenxing) 56 | self.confirm_fenxing = None 57 | else: 58 | if self.extreme_fenxing.is_top: 59 | extreme_fenxing_last_include_extreme_price = self.extreme_fenxing.end_bar.include_low 60 | else: 61 | extreme_fenxing_last_include_extreme_price = self.extreme_fenxing.end_bar.include_high 62 | # 计算穿越判据价格 63 | if self.extreme_fenxing.is_top: 64 | yanzheng_fenxing_last_include_extreme_price = self.confirm_fenxing.end_bar.include_low 65 | if yanzheng_fenxing_last_include_extreme_price < extreme_fenxing_last_include_extreme_price: 66 | self.judge_price = yanzheng_fenxing_last_include_extreme_price 67 | else: 68 | self.judge_price = extreme_fenxing_last_include_extreme_price 69 | else: 70 | yanzheng_fenxing_last_include_extreme_price = self.confirm_fenxing.end_bar.include_high 71 | if yanzheng_fenxing_last_include_extreme_price > extreme_fenxing_last_include_extreme_price: 72 | self.judge_price = yanzheng_fenxing_last_include_extreme_price 73 | else: 74 | self.judge_price = extreme_fenxing_last_include_extreme_price 75 | 76 | def on_new_bar(self, bar: ChanBarData): 77 | if not self.extreme_fenxing: 78 | return 79 | # 验证分型破坏止损 80 | if self.complete_yanzheng_fenxings and not self.complete_yanzheng_fenxings[-1].invalid: 81 | last_yanzheng_fenxing = self.complete_yanzheng_fenxings[-1] 82 | if last_yanzheng_fenxing.should_stop_loss(bar): 83 | self.complete_yanzheng_fenxings[-1].invalid = True 84 | 85 | if self.confirm_fenxing: 86 | # 2。验证分型和极值分型极值穿越判断 87 | if self.extreme_fenxing.is_top and bar.close_price < self.judge_price \ 88 | or \ 89 | not self.extreme_fenxing.is_top and bar.close_price > self.judge_price: 90 | # 验证分型和极值分型极值穿越判据成立,验证分型有效 91 | if self.confirm_fenxing.is_top: 92 | loss_price = self.confirm_fenxing.extreme_bar.include_high 93 | else: 94 | loss_price = self.confirm_fenxing.extreme_bar.include_low 95 | yanzheng_fenxing = YanZhengFenxing(extreme_fenxing=self.extreme_fenxing, 96 | confirm_fenxing=self.confirm_fenxing, loss_price=loss_price, 97 | confirm_bar=bar) 98 | self.complete_yanzheng_fenxings.append(yanzheng_fenxing) 99 | self.trend.on_yanzheng_fenxing_success(yanzheng_fenxing) 100 | self.confirm_fenxing = None 101 | 102 | self.judge_fenxing_pause(bar) 103 | 104 | def judge_fenxing_pause(self, bar): 105 | if not self.fenxing_pause: 106 | # 分型停顿判断 107 | # 计算极值分型的最后一根k线的极值 108 | if self.extreme_fenxing.is_top: 109 | extreme_fenxing_last_include_extreme_price = self.extreme_fenxing.end_bar.include_low 110 | else: 111 | extreme_fenxing_last_include_extreme_price = self.extreme_fenxing.end_bar.include_high 112 | if self.extreme_fenxing.is_top and bar.close_price < extreme_fenxing_last_include_extreme_price \ 113 | or \ 114 | not self.extreme_fenxing.is_top and bar.close_price > extreme_fenxing_last_include_extreme_price: 115 | # 分型停顿确认是验证分型有效 116 | self.fenxing_pause = True 117 | 118 | def init(self, trend: BaseTrend): 119 | self.trend = trend 120 | last_pen = self.trend.pens[-1] 121 | if last_pen.end_fenxing.index + 1 >= len(self.trend.fenxings): 122 | return 123 | extreme_fenxing = trend.fenxings[last_pen.end_fenxing.index + 1] 124 | self.extreme_fenxing = extreme_fenxing 125 | for i in range(last_pen.end_fenxing.index + 1, len(trend.fenxings)): 126 | fenxing = trend.fenxings[i] 127 | if fenxing.is_top != self.extreme_fenxing.is_top: 128 | continue 129 | if fenxing.is_top and fenxing.extreme_bar.include_high > self.extreme_fenxing.extreme_bar.include_high: 130 | self.extreme_fenxing = fenxing 131 | elif not fenxing.is_top and fenxing.extreme_bar.include_low < self.extreme_fenxing.extreme_bar.include_low: 132 | self.extreme_fenxing = fenxing 133 | for i in range(self.extreme_fenxing.end_bar.index + 1, len(trend.bars)): 134 | self.judge_fenxing_pause(trend.bars[i]) 135 | -------------------------------------------------------------------------------- /demo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/demo.jpeg -------------------------------------------------------------------------------- /event/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-06-08" -------------------------------------------------------------------------------- /event/engine.py: -------------------------------------------------------------------------------- 1 | """ 2 | Event-driven framework of vn.py framework. 3 | """ 4 | 5 | from collections import defaultdict 6 | from queue import Empty, Queue 7 | from threading import Thread 8 | from time import sleep 9 | from typing import Any, Callable 10 | 11 | EVENT_TIMER = "eTimer" 12 | 13 | 14 | class Event: 15 | """ 16 | Event object consists of a type string which is used 17 | by event engine for distributing event, and a data 18 | object which contains the real data. 19 | """ 20 | 21 | def __init__(self, type: str, data: Any = None): 22 | """""" 23 | self.type = type 24 | self.data = data 25 | 26 | 27 | # Defines handler function to be used in event engine. 28 | HandlerType = Callable[[Event], None] 29 | 30 | 31 | class EventEngine: 32 | """ 33 | Event engine distributes event object based on its type 34 | to those handlers registered. 35 | It also generates timer event by every interval seconds, 36 | which can be used for timing purpose. 37 | """ 38 | 39 | def __init__(self, interval: int = 1): 40 | """ 41 | Timer event is generated every 1 second by default, if 42 | interval not specified. 43 | """ 44 | self._interval = interval 45 | self._queue = Queue() 46 | self._active = False 47 | self._thread = Thread(target=self._run) 48 | self._timer = Thread(target=self._run_timer) 49 | self._handlers = defaultdict(list) 50 | self._general_handlers = [] 51 | 52 | def _run(self): 53 | """ 54 | Get event from queue and then process it. 55 | """ 56 | while self._active: 57 | try: 58 | event = self._queue.get(block=True, timeout=1) 59 | self._process(event) 60 | except Empty: 61 | pass 62 | 63 | def _process(self, event: Event): 64 | """ 65 | First ditribute event to those handlers registered listening 66 | to this type. 67 | Then distrubute event to those general handlers which listens 68 | to all types. 69 | """ 70 | if event.type in self._handlers: 71 | [handler(event) for handler in self._handlers[event.type]] 72 | 73 | if self._general_handlers: 74 | [handler(event) for handler in self._general_handlers] 75 | 76 | def _run_timer(self): 77 | """ 78 | Sleep by interval second(s) and then generate a timer event. 79 | """ 80 | while self._active: 81 | sleep(self._interval) 82 | event = Event(EVENT_TIMER) 83 | self.put(event) 84 | 85 | def start(self): 86 | """ 87 | Start event engine to process events and generate timer events. 88 | """ 89 | self._active = True 90 | self._thread.start() 91 | self._timer.start() 92 | 93 | def stop(self): 94 | """ 95 | Stop event engine. 96 | """ 97 | self._active = False 98 | self._timer.join() 99 | self._thread.join() 100 | 101 | def put(self, event: Event): 102 | """ 103 | Put an event object into event queue. 104 | """ 105 | self._queue.put(event) 106 | 107 | def register(self, type: str, handler: HandlerType): 108 | """ 109 | Register a new handler function for a specific event type. Every 110 | function can only be registered once for each event type. 111 | """ 112 | handler_list = self._handlers[type] 113 | if handler not in handler_list: 114 | handler_list.append(handler) 115 | 116 | def unregister(self, type: str, handler: HandlerType): 117 | """ 118 | Unregister an existing handler function from event engine. 119 | """ 120 | handler_list = self._handlers[type] 121 | 122 | if handler in handler_list: 123 | handler_list.remove(handler) 124 | 125 | if not handler_list: 126 | self._handlers.pop(type) 127 | 128 | def register_general(self, handler: HandlerType): 129 | """ 130 | Register a new handler function for all event types. Every 131 | function can only be registered once for each event type. 132 | """ 133 | if handler not in self._general_handlers: 134 | self._general_handlers.append(handler) 135 | 136 | def unregister_general(self, handler: HandlerType): 137 | """ 138 | Unregister an existing general handler function. 139 | """ 140 | if handler in self._general_handlers: 141 | self._general_handlers.remove(handler) -------------------------------------------------------------------------------- /gateway/jq/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from __future__ import absolute_import 4 | 5 | __author__ = "hanweiwei" 6 | __date__ = "2020/10/13" -------------------------------------------------------------------------------- /gateway/jq/jq_gateway.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | from threading import Thread 4 | from typing import List 5 | 6 | from pandas import DataFrame 7 | 8 | from event.engine import EventEngine 9 | from trader.constant import Interval, Exchange, Direction, Offset 10 | from trader.gateway import BaseGateway 11 | from trader.object import KLineRequest, BarData, SubscribeBarRequest, OrderRequest 12 | 13 | OFFSET_DICT = { 14 | Offset.OPEN: "OPEN", 15 | Offset.CLOSE: "CLOSE", 16 | Offset.CLOSETODAY: "CLOSETODAY", 17 | } 18 | 19 | DIRECTION_DICT = { 20 | Direction.LONG: "BUY", 21 | Direction.SHORT: "SELL" 22 | } 23 | 24 | __author__ = "hanweiwei" 25 | __date__ = "2019-05-31" 26 | 27 | from tqsdk import api, TqSim, TqBacktest 28 | from jqdatasdk import * 29 | 30 | 31 | def _bar_interval_2_seconds(bar_interval): 32 | """ 33 | 34 | :param bar_interval: 35 | :return: 36 | """ 37 | if bar_interval == Interval.MINUTE: 38 | seconds = 60 39 | elif bar_interval == Interval.MINUTE_5: 40 | seconds = 300 41 | elif bar_interval == Interval.MINUTE_30: 42 | seconds = 1800 43 | elif bar_interval == Interval.HOUR: 44 | seconds = 3600 45 | elif bar_interval == Interval.DAILY: 46 | seconds = 3600 * 24 47 | elif bar_interval == Interval.WEEKLY: 48 | seconds = 3600 * 24 * 7 49 | else: 50 | raise ValueError("unknown bar interval %s" % bar_interval) 51 | return seconds 52 | 53 | 54 | def _direction(direction: Direction): 55 | return DIRECTION_DICT.get(direction) 56 | 57 | 58 | def _offset(offset: Offset): 59 | return OFFSET_DICT.get(offset) 60 | 61 | 62 | class JQGateway(BaseGateway): 63 | 64 | def __init__(self, event_engine: EventEngine): 65 | super().__init__(event_engine, "JQ") 66 | self._looper = Thread(target=self._run) 67 | 68 | self._bar_requests: List[SubscribeBarRequest] = [] 69 | self._bars_df: List[DataFrame] = [] 70 | auth("13516771087", "www1995com") 71 | 72 | def get_kline_data(self, kline_request: KLineRequest): 73 | exchange = kline_request.exchange 74 | interval = kline_request.bar_interval 75 | 76 | df = get_bars(kline_request.symbol, kline_request.data_length, unit=kline_request.bar_interval.value, 77 | fields=['date', 'open', 'high', 'low', 'close','volume'], 78 | include_now=False, end_dt=None, fq_ref_date=datetime.date.today(), df=True) 79 | data: List[BarData] = [] 80 | for ix, row in df.iterrows(): 81 | bar = self.dfrow2bar(exchange, kline_request.symbol, interval, row) 82 | data.append(bar) 83 | 84 | return data 85 | 86 | def dfrow2bar(self, exchange, symbol, interval, row): 87 | date = row["date"] 88 | if interval == Interval.MINUTE: 89 | date = date - datetime.timedelta(minutes=1) 90 | elif interval == Interval.MINUTE_5: 91 | date = date - datetime.timedelta(minutes=5) 92 | elif interval == Interval.MINUTE_30: 93 | date = date - datetime.timedelta(minutes=30) 94 | return BarData( 95 | symbol=symbol, 96 | exchange=exchange, 97 | interval=interval, 98 | datetime=date, 99 | open_price=row["open"], 100 | high_price=row["high"], 101 | low_price=row["low"], 102 | close_price=row["close"], 103 | volume=row["volume"], 104 | gateway_name=self.gateway_name 105 | ) 106 | 107 | def connect(self, setting: dict): 108 | # self._looper.start() 109 | self._run() 110 | 111 | def subscribe_bar(self, req: SubscribeBarRequest): 112 | seconds = _bar_interval_2_seconds(req.interval) 113 | symbol = f'{req.exchange.value}.{req.symbol}' 114 | self._bar_requests.append(req) 115 | 116 | def send_order(self, req: OrderRequest): 117 | symbol = f'{req.exchange.value}.{req.symbol}' 118 | direction = _direction(req.direction) 119 | offset = _offset(req.offset) 120 | 121 | def query_position(self): 122 | pass 123 | 124 | def _run(self): 125 | # 初始化k线 126 | 127 | # bars = get_bars('300253.XSHE', 24000, unit='1m', 128 | # fields=['date', 'open', 'high', 'low', 'close'], 129 | # include_now=False, end_dt=None, fq_ref_date=None, df=True) 130 | # print(bars) 131 | while True: 132 | import time 133 | # self._tqapi.wait_update() 134 | # for i, bar_df in enumerate(self._bars_df): 135 | # if self._tqapi.is_changing(bar_df.iloc[-1], "datetime"): 136 | # row = bar_df.iloc[-2] 137 | # bar_request = self._bar_requests[i] 138 | # bar = self.dfrow2bar(bar_request.exchange, bar_request.symbol, bar_request.interval, row) 139 | # self.on_bar(bar) 140 | -------------------------------------------------------------------------------- /gateway/tq/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-05-31" -------------------------------------------------------------------------------- /gateway/tq/tq_gateway.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | from threading import Thread 4 | from typing import List 5 | 6 | from pandas import DataFrame 7 | 8 | from event.engine import EventEngine 9 | from trader.constant import Interval, Exchange, Direction, Offset 10 | from trader.gateway import BaseGateway 11 | from trader.object import KLineRequest, BarData, SubscribeBarRequest, OrderRequest 12 | 13 | OFFSET_DICT = { 14 | Offset.OPEN: "OPEN", 15 | Offset.CLOSE: "CLOSE", 16 | Offset.CLOSETODAY: "CLOSETODAY", 17 | } 18 | 19 | DIRECTION_DICT = { 20 | Direction.LONG: "BUY", 21 | Direction.SHORT: "SELL" 22 | } 23 | 24 | __author__ = "hanweiwei" 25 | __date__ = "2019-05-31" 26 | 27 | from tqsdk import api, TqSim, TqBacktest 28 | 29 | 30 | def _bar_interval_2_seconds(bar_interval): 31 | """ 32 | 33 | :param bar_interval: 34 | :return: 35 | """ 36 | if bar_interval == Interval.MINUTE: 37 | seconds = 60 38 | elif bar_interval == Interval.MINUTE_5: 39 | seconds = 300 40 | elif bar_interval == Interval.MINUTE_30: 41 | seconds = 1800 42 | elif bar_interval == Interval.HOUR: 43 | seconds = 3600 44 | elif bar_interval == Interval.DAILY: 45 | seconds = 3600 * 24 46 | elif bar_interval == Interval.WEEKLY: 47 | seconds = 3600 * 24 * 7 48 | else: 49 | raise ValueError("unknown bar interval %s" % bar_interval) 50 | return seconds 51 | 52 | 53 | def _direction(direction: Direction): 54 | return DIRECTION_DICT.get(direction) 55 | 56 | 57 | def _offset(offset: Offset): 58 | return OFFSET_DICT.get(offset) 59 | 60 | 61 | class TQGateway(BaseGateway): 62 | 63 | def __init__(self, event_engine: EventEngine): 64 | super().__init__(event_engine, "TQ") 65 | self._tqapi = api.TqApi(TqSim(), backtest=TqBacktest(start_dt=datetime.date(2019, 3, 20), 66 | end_dt=datetime.date(2019, 6, 20))) 67 | self._looper = Thread(target=self._run) 68 | 69 | self._bar_requests: List[SubscribeBarRequest] = [] 70 | self._bars_df: List[DataFrame] = [] 71 | 72 | def get_kline_data(self, kline_request: KLineRequest): 73 | exchange = kline_request.exchange 74 | interval = kline_request.bar_interval 75 | seconds = _bar_interval_2_seconds(kline_request.bar_interval) 76 | symbol = f'{kline_request.exchange.value}.{kline_request.symbol}' 77 | df = self._tqapi.get_kline_serial(symbol, duration_seconds=seconds, 78 | data_length=kline_request.data_length) 79 | data: List[BarData] = [] 80 | for ix, row in df.iterrows(): 81 | bar = self.dfrow2bar(exchange, kline_request.symbol, interval, row) 82 | data.append(bar) 83 | 84 | return data 85 | 86 | def dfrow2bar(self, exchange, symbol, interval, row): 87 | return BarData( 88 | symbol=symbol, 89 | exchange=exchange, 90 | interval=interval, 91 | datetime=datetime.datetime.fromtimestamp(int(row["datetime"] / 1000000000)), 92 | open_price=row["open"], 93 | high_price=row["high"], 94 | low_price=row["low"], 95 | close_price=row["close"], 96 | volume=row["volume"], 97 | gateway_name=self.gateway_name 98 | ) 99 | 100 | def connect(self, setting: dict): 101 | # self._looper.start() 102 | self._run() 103 | 104 | def subscribe_bar(self, req: SubscribeBarRequest): 105 | seconds = _bar_interval_2_seconds(req.interval) 106 | symbol = f'{req.exchange.value}.{req.symbol}' 107 | df = self._tqapi.get_kline_serial(symbol, duration_seconds=seconds) 108 | self._bars_df.append(df) 109 | self._bar_requests.append(req) 110 | 111 | def send_order(self, req: OrderRequest): 112 | symbol = f'{req.exchange.value}.{req.symbol}' 113 | direction = _direction(req.direction) 114 | offset = _offset(req.offset) 115 | order = self._tqapi.insert_order(symbol=symbol, direction=direction, offset=offset, volume=int(req.volume)) 116 | 117 | def query_position(self): 118 | pass 119 | 120 | def _run(self): 121 | while True: 122 | self._tqapi.wait_update() 123 | for i, bar_df in enumerate(self._bars_df): 124 | if self._tqapi.is_changing(bar_df.iloc[-1], "datetime"): 125 | row = bar_df.iloc[-2] 126 | bar_request = self._bar_requests[i] 127 | bar = self.dfrow2bar(bar_request.exchange, bar_request.symbol, bar_request.interval, row) 128 | self.on_bar(bar) 129 | -------------------------------------------------------------------------------- /jq/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from __future__ import absolute_import 4 | 5 | __author__ = "hanweiwei" 6 | __date__ = "2020/10/13" -------------------------------------------------------------------------------- /jq/jq_test.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from __future__ import absolute_import 4 | 5 | __author__ = "hanweiwei" 6 | __date__ = "2020/10/13" 7 | 8 | from jqdatasdk import * 9 | 10 | if __name__ == '__main__': 11 | auth('13516771087', 'www1995com') 12 | print get_query_count() -------------------------------------------------------------------------------- /jq/north_money_bool_band.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from __future__ import absolute_import 4 | 5 | __author__ = "hanweiwei" 6 | __date__ = "2020/10/18" 7 | 8 | """ 9 | 北上资金布林轨,择时使用 10 | """ 11 | 12 | from jqdatasdk import * 13 | import matplotlib.pyplot as pl 14 | import datetime 15 | 16 | import matplotlib.dates as mdates 17 | 18 | STDEV_N = 2 # 计算布林轨的标准差倍数 19 | WINDOW = 60 # 计算布林轨的窗口长度 20 | 21 | 22 | def get_boll(end_date): 23 | """ 24 | 获取北向资金布林带 25 | """ 26 | 27 | _base_days = get_trade_days(end_date=end_date, count=100) # 基础 28 | 29 | _mfs = [] 30 | _MiD = [] # 日平均线数组 31 | _UP = [] 32 | _DW = [] 33 | _DT = [] 34 | for i in _base_days: 35 | table = finance.STK_ML_QUOTA # 市场通成交与额度信息 36 | q = query( 37 | table.day, table.quota_daily, table.quota_daily_balance 38 | ).filter( 39 | table.link_id.in_(['310001', '310002']), table.day <= i # 沪股通、深股通 40 | ) \ 41 | .order_by(table.day.desc()) \ 42 | .limit(70) 43 | 44 | money_df = finance.run_query(q) 45 | money_df['net_amount'] = money_df['quota_daily'] - money_df['quota_daily_balance'] # 每日额度-每日剩余额度=净买入额 46 | # 分组求和 47 | money_df = money_df.groupby('day')[['net_amount']].sum().iloc[-WINDOW:] # 过去g.window天求和 48 | mid = money_df['net_amount'].mean() 49 | stdev = money_df['net_amount'].std() 50 | upper = mid + STDEV_N * stdev 51 | lower = mid - STDEV_N * stdev 52 | mf = money_df['net_amount'].iloc[-1] 53 | 54 | _DT.append(i) 55 | _mfs.append(mf) 56 | _UP.append(upper) 57 | _DW.append(lower) 58 | _MiD.append(mid) 59 | 60 | return _DT, _mfs, _UP, _DW, _MiD 61 | 62 | 63 | def main(): 64 | auth("13516771087", "www1995com") 65 | print(get_query_count()) 66 | pre_date = (datetime.date.today() - datetime.timedelta(1)).strftime('%Y-%m-%d') 67 | # pre_date = datetime.datetime(year=2016, month=4, day=1).strftime('%Y-%m-%d') 68 | dts, mfs, ups, dws, mids = get_boll(pre_date) 69 | 70 | pl.title("North Money Band") 71 | pl.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d')) 72 | pl.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=1)) 73 | pl.plot(dts, mids, "y", lw=0.5) 74 | pl.plot(dts, ups, "r", lw=0.5) 75 | pl.plot(dts, dws, "g", lw=0.5) 76 | pl.plot(dts, mfs, lw=1) 77 | pl.show() 78 | 79 | 80 | if __name__ == '__main__': 81 | main() 82 | -------------------------------------------------------------------------------- /mytushare/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2018/11/16" -------------------------------------------------------------------------------- /mytushare/api.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | import json 4 | 5 | import requests 6 | import requests_cache 7 | 8 | from app import app_log 9 | from tools import time_tools 10 | 11 | __author__ = "hanweiwei" 12 | __date__ = "2018/11/16" 13 | 14 | TOKEN = "24b9e438d14d30648fa32dd6b457500ddc041ff0e17378d0b2083d7d" 15 | 16 | MINUTE_1 = "1MIN" 17 | MINUTE_5 = "5MIN" 18 | MINUTE_30 = "30MIN" 19 | MINUTE_60 = "60MIN" 20 | DAY_1 = "1D" 21 | 22 | KLINE_URL = "http://api.tushare.pro" 23 | 24 | requests_cache.install_cache() 25 | s = requests.Session() 26 | 27 | 28 | def getKLineData(stock_code, scale, start_date, end_date): 29 | """ 30 | 查询K线 31 | :param stock_code: 股票代码 深圳 000001.SZ 上海 600001.SS 32 | :param scale: 周期 1min 5min 30min 60min 1d 33 | :param start_date: str 34 | :param end_date: str 35 | :return: DataFrame 36 | """ 37 | 38 | start_date = parse_date(start_date) 39 | end_date = parse_date(end_date) 40 | 41 | api_params = { 42 | "ts_code": stock_code, 43 | "start_date": start_date, 44 | "end_date": end_date, 45 | "adj": "qfq", 46 | "freq": scale 47 | } 48 | params = {"api_name": "hist_bar", "token": TOKEN, "params": api_params, "fields": "open,high,low,close"} 49 | 50 | app_log.info("REQUEST START:\n%s\%s", KLINE_URL, api_params) 51 | 52 | response = s.post(KLINE_URL, headers={'Connection': 'keep-alive', 53 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"} 54 | , json=params) 55 | contents = response.json() 56 | # app_log.info("REQUEST END:\n%s" % response) 57 | # 58 | # chart = contents["chart"] 59 | # if chart["error"]: 60 | # raise Exception(contents["error"]) 61 | # contents = chart["result"][0] 62 | # quote = contents["indicators"]["quote"][0] 63 | # datetime_index = pd.to_datetime(contents["timestamp"], unit="s", utc=True) 64 | # datetime_index = datetime_index.tz_convert(pytz.timezone("Asia/Shanghai")) 65 | # data = pd.DataFrame({ 66 | # "high": quote["high"] 67 | # , "low": quote["low"] 68 | # , "open": quote["open"] 69 | # , "close": quote["close"] 70 | # }, index=datetime_index) 71 | # 72 | # data = data.dropna(how='any') 73 | # data['time'] = data.index 74 | return contents 75 | 76 | 77 | def parse_date(date): 78 | """ 79 | 80 | :param date: timestamp or date or str or datetime 81 | :return: 82 | """ 83 | if date: 84 | if isinstance(date, str): 85 | date = time_tools.datetime2timestamp(datetime.datetime.strptime(date, "%Y%m%d")) 86 | elif isinstance(date, datetime.date): 87 | date = time_tools.date2timestamp(date) 88 | elif isinstance(date, datetime.datetime): 89 | date = time_tools.date2timestamp(date) 90 | elif isinstance(date, int): 91 | pass 92 | else: 93 | raise ValueError("date type error, must be timestamp, date, datetime, str") 94 | return date 95 | 96 | 97 | if __name__ == '__main__': 98 | data = getKLineData("601788.SS", scale=MINUTE_5, start_date="20181111", end_date="20181114") 99 | app_log.info("\n%s", json.dumps(data,ensure_ascii=False)) 100 | # print getKLineData("601788.SS", scale=MINUTE_5, start_date="20181109", end_date="20181111") 101 | -------------------------------------------------------------------------------- /mytushare/api2.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | import json 4 | 5 | import requests 6 | import requests_cache 7 | 8 | from app import app_log 9 | from tools import time_tools 10 | 11 | __author__ = "hanweiwei" 12 | __date__ = "2018/11/16" 13 | TOKEN = "24b9e438d14d30648fa32dd6b457500ddc041ff0e17378d0b2083d7d" 14 | 15 | MINUTE_1 = "1MIN" 16 | MINUTE_5 = "5MIN" 17 | MINUTE_30 = "30MIN" 18 | MINUTE_60 = "60MIN" 19 | DAY_1 = "1D" 20 | 21 | import tushare as tu 22 | 23 | tu.set_token(TOKEN) 24 | pro_api = tu.pro_api() 25 | 26 | 27 | def getKLineData(stock_code, scale, start_date, end_date): 28 | """ 29 | 查询K线 30 | :param stock_code: 股票代码 深圳 000001.SZ 上海 600001.SS 31 | :param scale: 周期 1min 5min 30min 60min 1d 32 | :param start_date: str 33 | :param end_date: str 34 | :return: DataFrame 35 | """ 36 | return tu.pro_bar(stock_code, pro_api, start_date, end_date, freq=scale, adj="qfq") 37 | 38 | 39 | if __name__ == '__main__': 40 | data = getKLineData("000001.SZ", scale=None, start_date="20181101", end_date="20181118") 41 | app_log.info("\n%s", data) 42 | # print getKLineData("601788.SS", scale=MINUTE_5, start_date="20181109", end_date="20181111") 43 | -------------------------------------------------------------------------------- /mytushare/cache.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/mytushare/cache.sqlite -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | pandas-datareader 3 | tqsdk 4 | sqlalchemy 5 | flask 6 | flask_cors 7 | jqdatasdk -------------------------------------------------------------------------------- /sina/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2018/10/25" -------------------------------------------------------------------------------- /sina/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/sina/__init__.pyc -------------------------------------------------------------------------------- /sina/api.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import urllib2 3 | import pandas as pd 4 | import re 5 | 6 | from app import app_log 7 | 8 | __author__ = "hanweiwei" 9 | __date__ = "2018/10/25" 10 | 11 | KLINE_URL = "https://money.finance.sina.com.cn/quotes_service/api/jsonp_v2.php/data=/CN_MarketData.getKLineData?symbol=%s&scale=%s&ma=%s&datalen=%s" 12 | 13 | MINUTE_5 = 5 14 | MINUTE_15 = 15 15 | MINUTE_30 = 30 16 | MINUTE_60 = 60 17 | DAY = 240 18 | DAY_5 = DAY * 5 19 | WEEK = DAY * 7 20 | MONTH = DAY * 30 21 | 22 | 23 | def getKLineData(stock_code="SZ000001", scale=DAY, ma="no", count=100): 24 | """ 25 | 查询K线 26 | :param stock_code: 股票代码 SZ深圳000001 SH上证600001 27 | :param scale: 周期 5m 15m 30m 60m 1day 5day 1week 1month 28 | :param ma: 移动平均线 ,分隔 29 | :param count: 数量 30 | :return: DataFrame 31 | """ 32 | contents = urllib2.urlopen(KLINE_URL % (stock_code, scale, ma, count)).read() 33 | contents = contents[6:-1] 34 | 35 | contents = re.sub(r",(\w+):", lambda s: ',"%s":' % contents[s.start(1):s.end(1)], contents) 36 | contents = re.sub(r"{(\w+):", lambda s: '{"%s":' % contents[s.start(1):s.end(1)], contents) 37 | app_log.info("获取数据成功") 38 | return pd.read_json(contents) 39 | 40 | 41 | if __name__ == '__main__': 42 | print getKLineData("SH000001", scale=MINUTE_60, count=100) 43 | -------------------------------------------------------------------------------- /sina/api.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/sina/api.pyc -------------------------------------------------------------------------------- /static/data.json: -------------------------------------------------------------------------------- 1 | {"pens": [{"start": 12, "end": 18}, {"start": 18, "end": 37}, {"start": 37, "end": 70}, {"start": 70, "end": 152}, {"start": 152, "end": 163}, {"start": 163, "end": 219}, {"start": 219, "end": 236}, {"start": 236, "end": 261}], "kline": [{"volume": 862446700, "includeLow": 2873.822, "includeIndex": -1, "high": 2879.943, "includeHigh": 2879.943, "low": 2873.822, "includeTail": 0, "fenxing": 0, "close": 2879.943, "open": 2874.036, "day": "2019-05-17 14:55:00"}, {"volume": 646028700, "includeLow": 2879.681, "includeIndex": 1, "high": 2882.296, "includeHigh": 2882.296, "low": 2879.681, "includeTail": 0, "fenxing": 0, "close": 2882.296, "open": 2879.805, "day": "2019-05-17 15:00:00"}, {"volume": 1831886800, "includeLow": 2879.681, "includeIndex": 1, "high": 2882.629, "includeHigh": 2882.629, "low": 2868.759, "includeTail": 1, "fenxing": 1, "close": 2869.155, "open": 2874.801, "day": "2019-05-20 09:35:00"}, {"volume": 1434762700, "includeLow": 2865.643, "includeIndex": 3, "high": 2879.574, "includeHigh": 2879.574, "low": 2865.643, "includeTail": 0, "fenxing": 0, "close": 2879.043, "open": 2868.72, "day": "2019-05-20 09:40:00"}, {"volume": 984259900, "includeLow": 2865.643, "includeIndex": 3, "high": 2879.193, "includeHigh": 2879.193, "low": 2868.417, "includeTail": 1, "fenxing": 0, "close": 2868.417, "open": 2879.193, "day": "2019-05-20 09:45:00"}, {"volume": 1475071100, "includeLow": 2846.872, "includeIndex": 5, "high": 2869.335, "includeHigh": 2869.335, "low": 2846.872, "includeTail": 0, "fenxing": 0, "close": 2849.505, "open": 2868.828, "day": "2019-05-20 09:50:00"}, {"volume": 1012699500, "includeLow": 2846.872, "includeIndex": 5, "high": 2854.092, "includeHigh": 2854.092, "low": 2846.886, "includeTail": 1, "fenxing": 0, "close": 2854.092, "open": 2849.148, "day": "2019-05-20 09:55:00"}, {"volume": 837953800, "includeLow": 2842.608, "includeIndex": 7, "high": 2853.507, "includeHigh": 2853.507, "low": 2842.608, "includeTail": 0, "fenxing": 0, "close": 2849.528, "open": 2853.233, "day": "2019-05-20 10:00:00"}, {"volume": 555324600, "includeLow": 2842.608, "includeIndex": 7, "high": 2851.205, "includeHigh": 2851.205, "low": 2843.625, "includeTail": 0, "fenxing": 0, "close": 2843.866, "open": 2849.471, "day": "2019-05-20 10:05:00"}, {"volume": 629139600, "includeLow": 2841.501, "includeIndex": 7, "high": 2851.308, "includeHigh": 2851.205, "low": 2841.501, "includeTail": 0, "fenxing": 0, "close": 2850.416, "open": 2843.419, "day": "2019-05-20 10:10:00"}, {"volume": 404904100, "includeLow": 2841.501, "includeIndex": 7, "high": 2849.976, "includeHigh": 2849.976, "low": 2844.462, "includeTail": 1, "fenxing": 0, "close": 2845.631, "open": 2849.434, "day": "2019-05-20 10:15:00"}, {"volume": 436819100, "includeLow": 2838.958, "includeIndex": -1, "high": 2845.597, "includeHigh": 2845.597, "low": 2838.958, "includeTail": 0, "fenxing": 0, "close": 2841.246, "open": 2845.597, "day": "2019-05-20 10:20:00"}, {"volume": 379548300, "includeLow": 2838.453, "includeIndex": -1, "high": 2842.298, "includeHigh": 2842.298, "low": 2838.453, "includeTail": 0, "fenxing": 2, "close": 2840.022, "open": 2841.929, "day": "2019-05-20 10:25:00"}, {"volume": 470136200, "includeLow": 2839.392, "includeIndex": -1, "high": 2852.332, "includeHigh": 2852.332, "low": 2839.392, "includeTail": 0, "fenxing": 0, "close": 2852.241, "open": 2840.206, "day": "2019-05-20 10:30:00"}, {"volume": 457881100, "includeLow": 2852.471, "includeIndex": -1, "high": 2861.977, "includeHigh": 2861.977, "low": 2852.471, "includeTail": 0, "fenxing": 0, "close": 2861.22, "open": 2852.494, "day": "2019-05-20 10:35:00"}, {"volume": 356212500, "includeLow": 2853.189, "includeIndex": -1, "high": 2862.01, "includeHigh": 2862.01, "low": 2853.189, "includeTail": 0, "fenxing": 1, "close": 2853.632, "open": 2861.676, "day": "2019-05-20 10:40:00"}, {"volume": 297061900, "includeLow": 2852.867, "includeIndex": -1, "high": 2861.23, "includeHigh": 2861.23, "low": 2852.867, "includeTail": 0, "fenxing": 2, "close": 2861.23, "open": 2853.574, "day": "2019-05-20 10:45:00"}, {"volume": 546778600, "includeLow": 2862.162, "includeIndex": -1, "high": 2874.226, "includeHigh": 2874.226, "low": 2862.162, "includeTail": 0, "fenxing": 0, "close": 2874.134, "open": 2862.162, "day": "2019-05-20 10:50:00"}, {"volume": 476949000, "includeLow": 2869.2, "includeIndex": -1, "high": 2878.486, "includeHigh": 2878.486, "low": 2869.2, "includeTail": 0, "fenxing": 1, "close": 2869.266, "open": 2874.567, "day": "2019-05-20 10:55:00"}, {"volume": 272936100, "includeLow": 2868.501, "includeIndex": -1, "high": 2871.255, "includeHigh": 2871.255, "low": 2868.501, "includeTail": 0, "fenxing": 0, "close": 2870.252, "open": 2868.992, "day": "2019-05-20 11:00:00"}, {"volume": 269608000, "includeLow": 2864.95, "includeIndex": -1, "high": 2870.72, "includeHigh": 2870.72, "low": 2864.95, "includeTail": 0, "fenxing": 0, "close": 2864.95, "open": 2869.877, "day": "2019-05-20 11:05:00"}, {"volume": 253240400, "includeLow": 2858.935, "includeIndex": -1, "high": 2865.439, "includeHigh": 2865.439, "low": 2858.935, "includeTail": 0, "fenxing": 2, "close": 2861.495, "open": 2865.439, "day": "2019-05-20 11:10:00"}, {"volume": 224076000, "includeLow": 2861.37, "includeIndex": -1, "high": 2866.911, "includeHigh": 2866.911, "low": 2861.37, "includeTail": 0, "fenxing": 0, "close": 2865.241, "open": 2861.37, "day": "2019-05-20 11:15:00"}, {"volume": 199796500, "includeLow": 2865.139, "includeIndex": 23, "high": 2868.045, "includeHigh": 2868.045, "low": 2865.139, "includeTail": 0, "fenxing": 0, "close": 2867.462, "open": 2865.405, "day": "2019-05-20 11:20:00"}, {"volume": 227659500, "includeLow": 2865.139, "includeIndex": 23, "high": 2868.865, "includeHigh": 2868.865, "low": 2861.909, "includeTail": 1, "fenxing": 1, "close": 2864.424, "open": 2867.658, "day": "2019-05-20 11:25:00"}, {"volume": 149396800, "includeLow": 2862.908, "includeIndex": 25, "high": 2865.571, "includeHigh": 2865.571, "low": 2862.908, "includeTail": 0, "fenxing": 0, "close": 2864.214, "open": 2864.984, "day": "2019-05-20 11:30:00"}, {"volume": 230139700, "includeLow": 2861.2, "includeIndex": 25, "high": 2866.227, "includeHigh": 2865.571, "low": 2861.2, "includeTail": 0, "fenxing": 0, "close": 2861.2, "open": 2865.276, "day": "2019-05-20 13:05:00"}, {"volume": 184844000, "includeLow": 2860.812, "includeIndex": 25, "high": 2867.878, "includeHigh": 2865.571, "low": 2860.812, "includeTail": 0, "fenxing": 0, "close": 2867.129, "open": 2861.401, "day": "2019-05-20 13:10:00"}, {"volume": 183995500, "includeLow": 2860.618, "includeIndex": 25, "high": 2867.151, "includeHigh": 2865.571, "low": 2860.618, "includeTail": 1, "fenxing": 0, "close": 2860.618, "open": 2867.151, "day": "2019-05-20 13:15:00"}, {"volume": 202403200, "includeLow": 2858.053, "includeIndex": -1, "high": 2860.788, "includeHigh": 2860.788, "low": 2858.053, "includeTail": 0, "fenxing": 0, "close": 2858.305, "open": 2860.531, "day": "2019-05-20 13:20:00"}, {"volume": 209293500, "includeLow": 2855.299, "includeIndex": -1, "high": 2858.719, "includeHigh": 2858.719, "low": 2855.299, "includeTail": 0, "fenxing": 0, "close": 2855.534, "open": 2858.266, "day": "2019-05-20 13:25:00"}, {"volume": 189568700, "includeLow": 2855.197, "includeIndex": 31, "high": 2857.934, "includeHigh": 2857.934, "low": 2855.197, "includeTail": 0, "fenxing": 0, "close": 2856.476, "open": 2855.197, "day": "2019-05-20 13:30:00"}, {"volume": 220619200, "includeLow": 2853.693, "includeIndex": 31, "high": 2858.012, "includeHigh": 2857.934, "low": 2853.693, "includeTail": 1, "fenxing": 2, "close": 2857.936, "open": 2856.178, "day": "2019-05-20 13:35:00"}, {"volume": 210708100, "includeLow": 2856.942, "includeIndex": -1, "high": 2860.663, "includeHigh": 2860.663, "low": 2856.942, "includeTail": 0, "fenxing": 1, "close": 2858.839, "open": 2857.812, "day": "2019-05-20 13:40:00"}, {"volume": 174770000, "includeLow": 2856.484, "includeIndex": -1, "high": 2859.662, "includeHigh": 2859.662, "low": 2856.484, "includeTail": 0, "fenxing": 0, "close": 2857.419, "open": 2859.163, "day": "2019-05-20 13:45:00"}, {"volume": 272136600, "includeLow": 2852.619, "includeIndex": -1, "high": 2857.242, "includeHigh": 2857.242, "low": 2852.619, "includeTail": 0, "fenxing": 0, "close": 2855.113, "open": 2856.787, "day": "2019-05-20 13:50:00"}, {"volume": 238406500, "includeLow": 2850.07, "includeIndex": -1, "high": 2855.689, "includeHigh": 2855.689, "low": 2850.07, "includeTail": 0, "fenxing": 0, "close": 2850.07, "open": 2855.689, "day": "2019-05-20 13:55:00"}, {"volume": 251636300, "includeLow": 2849.359, "includeIndex": -1, "high": 2853.29, "includeHigh": 2853.29, "low": 2849.359, "includeTail": 0, "fenxing": 2, "close": 2853.227, "open": 2849.89, "day": "2019-05-20 14:00:00"}, {"volume": 193190300, "includeLow": 2852.652, "includeIndex": -1, "high": 2855.359, "includeHigh": 2855.359, "low": 2852.652, "includeTail": 0, "fenxing": 0, "close": 2854.472, "open": 2852.652, "day": "2019-05-20 14:05:00"}, {"volume": 303567200, "includeLow": 2854.639, "includeIndex": -1, "high": 2858.711, "includeHigh": 2858.711, "low": 2854.639, "includeTail": 0, "fenxing": 1, "close": 2857.169, "open": 2855.106, "day": "2019-05-20 14:10:00"}, {"volume": 245269600, "includeLow": 2853.365, "includeIndex": -1, "high": 2857.568, "includeHigh": 2857.568, "low": 2853.365, "includeTail": 0, "fenxing": 0, "close": 2853.422, "open": 2857.308, "day": "2019-05-20 14:15:00"}, {"volume": 260011200, "includeLow": 2851.415, "includeIndex": 41, "high": 2853.376, "includeHigh": 2853.376, "low": 2851.415, "includeTail": 0, "fenxing": 0, "close": 2851.415, "open": 2853.237, "day": "2019-05-20 14:20:00"}, {"volume": 323074700, "includeLow": 2851.195, "includeIndex": 41, "high": 2857.744, "includeHigh": 2853.376, "low": 2851.195, "includeTail": 1, "fenxing": 2, "close": 2857.526, "open": 2851.195, "day": "2019-05-20 14:25:00"}, {"volume": 293225600, "includeLow": 2855.313, "includeIndex": -1, "high": 2858.136, "includeHigh": 2858.136, "low": 2855.313, "includeTail": 0, "fenxing": 0, "close": 2856.518, "open": 2857.517, "day": "2019-05-20 14:30:00"}, {"volume": 314917600, "includeLow": 2856.198, "includeIndex": -1, "high": 2861.491, "includeHigh": 2861.491, "low": 2856.198, "includeTail": 0, "fenxing": 0, "close": 2861.216, "open": 2856.747, "day": "2019-05-20 14:35:00"}, {"volume": 464034000, "includeLow": 2860.919, "includeIndex": 45, "high": 2868.391, "includeHigh": 2868.391, "low": 2860.919, "includeTail": 0, "fenxing": 0, "close": 2866.88, "open": 2861.331, "day": "2019-05-20 14:40:00"}, {"volume": 362895300, "includeLow": 2865.336, "includeIndex": 45, "high": 2867.898, "includeHigh": 2868.391, "low": 2865.336, "includeTail": 1, "fenxing": 0, "close": 2865.939, "open": 2867.3, "day": "2019-05-20 14:45:00"}, {"volume": 413477000, "includeLow": 2865.67, "includeIndex": -1, "high": 2868.692, "includeHigh": 2868.692, "low": 2865.67, "includeTail": 0, "fenxing": 0, "close": 2868.175, "open": 2866.238, "day": "2019-05-20 14:50:00"}, {"volume": 524767000, "includeLow": 2867.322, "includeIndex": -1, "high": 2870.881, "includeHigh": 2870.881, "low": 2867.322, "includeTail": 0, "fenxing": 0, "close": 2870.881, "open": 2867.61, "day": "2019-05-20 14:55:00"}, {"volume": 494293000, "includeLow": 2870.339, "includeIndex": 49, "high": 2872.188, "includeHigh": 2872.188, "low": 2870.339, "includeTail": 0, "fenxing": 0, "close": 2870.605, "open": 2870.729, "day": "2019-05-20 15:00:00"}, {"volume": 1422069400, "includeLow": 2870.339, "includeIndex": 49, "high": 2880.081, "includeHigh": 2880.081, "low": 2862.738, "includeTail": 1, "fenxing": 0, "close": 2880.081, "open": 2867.707, "day": "2019-05-21 09:35:00"}, {"volume": 940352600, "includeLow": 2875.985, "includeIndex": -1, "high": 2880.783, "includeHigh": 2880.783, "low": 2875.985, "includeTail": 0, "fenxing": 0, "close": 2880.507, "open": 2879.794, "day": "2019-05-21 09:40:00"}, {"volume": 813493800, "includeLow": 2876.892, "includeIndex": -1, "high": 2887.064, "includeHigh": 2887.064, "low": 2876.892, "includeTail": 0, "fenxing": 0, "close": 2887.064, "open": 2880.683, "day": "2019-05-21 09:45:00"}, {"volume": 727740800, "includeLow": 2885.855, "includeIndex": 53, "high": 2889.03, "includeHigh": 2889.03, "low": 2885.855, "includeTail": 0, "fenxing": 0, "close": 2886.102, "open": 2887.544, "day": "2019-05-21 09:50:00"}, {"volume": 607339900, "includeLow": 2885.855, "includeIndex": 53, "high": 2889.364, "includeHigh": 2889.364, "low": 2885.118, "includeTail": 1, "fenxing": 1, "close": 2885.31, "open": 2886.807, "day": "2019-05-21 09:55:00"}, {"volume": 464478200, "includeLow": 2884.048, "includeIndex": -1, "high": 2888.289, "includeHigh": 2888.289, "low": 2884.048, "includeTail": 0, "fenxing": 2, "close": 2887.985, "open": 2884.541, "day": "2019-05-21 10:00:00"}, {"volume": 398454400, "includeLow": 2885.369, "includeIndex": -1, "high": 2889.729, "includeHigh": 2889.729, "low": 2885.369, "includeTail": 0, "fenxing": 1, "close": 2885.461, "open": 2888.34, "day": "2019-05-21 10:05:00"}, {"volume": 512783200, "includeLow": 2880.146, "includeIndex": -1, "high": 2886.647, "includeHigh": 2886.647, "low": 2880.146, "includeTail": 0, "fenxing": 0, "close": 2880.146, "open": 2885.753, "day": "2019-05-21 10:10:00"}, {"volume": 418074500, "includeLow": 2879.298, "includeIndex": -1, "high": 2881.857, "includeHigh": 2881.857, "low": 2879.298, "includeTail": 0, "fenxing": 2, "close": 2881.54, "open": 2879.97, "day": "2019-05-21 10:15:00"}, {"volume": 359239900, "includeLow": 2880.71, "includeIndex": 59, "high": 2886.576, "includeHigh": 2886.576, "low": 2880.71, "includeTail": 0, "fenxing": 0, "close": 2885.002, "open": 2880.974, "day": "2019-05-21 10:20:00"}, {"volume": 306281000, "includeLow": 2880.987, "includeIndex": 59, "high": 2885.314, "includeHigh": 2886.576, "low": 2880.987, "includeTail": 0, "fenxing": 0, "close": 2881.277, "open": 2885.12, "day": "2019-05-21 10:25:00"}, {"volume": 412523100, "includeLow": 2880.987, "includeIndex": 59, "high": 2893.288, "includeHigh": 2893.288, "low": 2880.743, "includeTail": 1, "fenxing": 0, "close": 2893.288, "open": 2880.743, "day": "2019-05-21 10:30:00"}, {"volume": 571380000, "includeLow": 2893.305, "includeIndex": 62, "high": 2900.095, "includeHigh": 2900.095, "low": 2893.305, "includeTail": 0, "fenxing": 0, "close": 2897.881, "open": 2893.305, "day": "2019-05-21 10:35:00"}, {"volume": 390586600, "includeLow": 2894.287, "includeIndex": 62, "high": 2898.933, "includeHigh": 2900.095, "low": 2894.287, "includeTail": 1, "fenxing": 0, "close": 2898.933, "open": 2897.549, "day": "2019-05-21 10:40:00"}, {"volume": 412065400, "includeLow": 2898.246, "includeIndex": -1, "high": 2901.592, "includeHigh": 2901.592, "low": 2898.246, "includeTail": 0, "fenxing": 0, "close": 2901.47, "open": 2899.395, "day": "2019-05-21 10:45:00"}, {"volume": 516523900, "includeLow": 2902.21, "includeIndex": -1, "high": 2905.911, "includeHigh": 2905.911, "low": 2902.21, "includeTail": 0, "fenxing": 1, "close": 2904.373, "open": 2902.21, "day": "2019-05-21 10:50:00"}, {"volume": 359014400, "includeLow": 2901.119, "includeIndex": -1, "high": 2903.585, "includeHigh": 2903.585, "low": 2901.119, "includeTail": 0, "fenxing": 2, "close": 2902.251, "open": 2903.325, "day": "2019-05-21 10:55:00"}, {"volume": 444372200, "includeLow": 2901.89, "includeIndex": -1, "high": 2910.312, "includeHigh": 2910.312, "low": 2901.89, "includeTail": 0, "fenxing": 0, "close": 2910.312, "open": 2901.89, "day": "2019-05-21 11:00:00"}, {"volume": 715538300, "includeLow": 2910.743, "includeIndex": -1, "high": 2917.986, "includeHigh": 2917.986, "low": 2910.743, "includeTail": 0, "fenxing": 0, "close": 2916.927, "open": 2910.743, "day": "2019-05-21 11:05:00"}, {"volume": 536507400, "includeLow": 2914.277, "includeIndex": 69, "high": 2919.237, "includeHigh": 2919.237, "low": 2914.277, "includeTail": 0, "fenxing": 0, "close": 2918.606, "open": 2916.558, "day": "2019-05-21 11:10:00"}, {"volume": 373822900, "includeLow": 2914.452, "includeIndex": 69, "high": 2918.622, "includeHigh": 2919.237, "low": 2914.452, "includeTail": 1, "fenxing": 1, "close": 2914.658, "open": 2918.621, "day": "2019-05-21 11:15:00"}, {"volume": 403458600, "includeLow": 2909.196, "includeIndex": 71, "high": 2918.153, "includeHigh": 2918.153, "low": 2909.196, "includeTail": 0, "fenxing": 0, "close": 2909.929, "open": 2914.781, "day": "2019-05-21 11:20:00"}, {"volume": 262544200, "includeLow": 2909.196, "includeIndex": 71, "high": 2914.005, "includeHigh": 2914.005, "low": 2909.468, "includeTail": 1, "fenxing": 2, "close": 2912.05, "open": 2909.468, "day": "2019-05-21 11:25:00"}, {"volume": 302221400, "includeLow": 2911.103, "includeIndex": -1, "high": 2914.123, "includeHigh": 2914.123, "low": 2911.103, "includeTail": 0, "fenxing": 0, "close": 2914.123, "open": 2912.304, "day": "2019-05-21 11:30:00"}, {"volume": 642969700, "includeLow": 2911.522, "includeIndex": 74, "high": 2915.608, "includeHigh": 2915.608, "low": 2911.522, "includeTail": 0, "fenxing": 0, "close": 2913.063, "open": 2915.608, "day": "2019-05-21 13:05:00"}, {"volume": 355385700, "includeLow": 2911.525, "includeIndex": 74, "high": 2913.873, "includeHigh": 2915.608, "low": 2911.525, "includeTail": 0, "fenxing": 0, "close": 2913.17, "open": 2913.337, "day": "2019-05-21 13:10:00"}, {"volume": 362719100, "includeLow": 2911.529, "includeIndex": 74, "high": 2914.174, "includeHigh": 2915.608, "low": 2911.529, "includeTail": 1, "fenxing": 1, "close": 2912.106, "open": 2913.642, "day": "2019-05-21 13:15:00"}, {"volume": 461097700, "includeLow": 2904.481, "includeIndex": 77, "high": 2912.138, "includeHigh": 2912.138, "low": 2904.481, "includeTail": 0, "fenxing": 0, "close": 2906.63, "open": 2911.698, "day": "2019-05-21 13:20:00"}, {"volume": 318400000, "includeLow": 2904.481, "includeIndex": 77, "high": 2907.454, "includeHigh": 2907.454, "low": 2905.834, "includeTail": 0, "fenxing": 0, "close": 2907.037, "open": 2906.597, "day": "2019-05-21 13:25:00"}, {"volume": 265226100, "includeLow": 2904.481, "includeIndex": 77, "high": 2907.323, "includeHigh": 2907.323, "low": 2905.934, "includeTail": 1, "fenxing": 2, "close": 2906.395, "open": 2906.704, "day": "2019-05-21 13:30:00"}, {"volume": 356710000, "includeLow": 2906.17, "includeIndex": 80, "high": 2910.383, "includeHigh": 2910.383, "low": 2906.17, "includeTail": 0, "fenxing": 0, "close": 2910.278, "open": 2906.17, "day": "2019-05-21 13:35:00"}, {"volume": 292472000, "includeLow": 2906.246, "includeIndex": 80, "high": 2909.709, "includeHigh": 2910.383, "low": 2906.246, "includeTail": 1, "fenxing": 1, "close": 2907.328, "open": 2909.709, "day": "2019-05-21 13:40:00"}, {"volume": 333691100, "includeLow": 2901.725, "includeIndex": 82, "high": 2907.452, "includeHigh": 2907.452, "low": 2901.725, "includeTail": 0, "fenxing": 0, "close": 2902.006, "open": 2907.184, "day": "2019-05-21 13:45:00"}, {"volume": 250574700, "includeLow": 2901.725, "includeIndex": 82, "high": 2905.256, "includeHigh": 2905.256, "low": 2901.859, "includeTail": 1, "fenxing": 2, "close": 2905.203, "open": 2901.859, "day": "2019-05-21 13:50:00"}, {"volume": 259623100, "includeLow": 2905.105, "includeIndex": -1, "high": 2906.865, "includeHigh": 2906.865, "low": 2905.105, "includeTail": 0, "fenxing": 0, "close": 2905.942, "open": 2905.804, "day": "2019-05-21 13:55:00"}, {"volume": 369875100, "includeLow": 2905.895, "includeIndex": 85, "high": 2912.021, "includeHigh": 2912.021, "low": 2905.895, "includeTail": 0, "fenxing": 0, "close": 2911.436, "open": 2906.031, "day": "2019-05-21 14:00:00"}, {"volume": 304282200, "includeLow": 2908.86, "includeIndex": 85, "high": 2911.853, "includeHigh": 2912.021, "low": 2908.86, "includeTail": 1, "fenxing": 1, "close": 2908.954, "open": 2911.853, "day": "2019-05-21 14:05:00"}, {"volume": 278788700, "includeLow": 2904.927, "includeIndex": -1, "high": 2909.053, "includeHigh": 2909.053, "low": 2904.927, "includeTail": 0, "fenxing": 0, "close": 2905.554, "open": 2908.579, "day": "2019-05-21 14:10:00"}, {"volume": 335334400, "includeLow": 2900.281, "includeIndex": 88, "high": 2905.199, "includeHigh": 2905.199, "low": 2900.281, "includeTail": 0, "fenxing": 0, "close": 2901.282, "open": 2904.916, "day": "2019-05-21 14:15:00"}, {"volume": 233629800, "includeLow": 2900.281, "includeIndex": 88, "high": 2904.431, "includeHigh": 2904.431, "low": 2901.267, "includeTail": 1, "fenxing": 2, "close": 2903.879, "open": 2901.906, "day": "2019-05-21 14:20:00"}, {"volume": 230768200, "includeLow": 2902.209, "includeIndex": 90, "high": 2904.755, "includeHigh": 2904.755, "low": 2902.209, "includeTail": 0, "fenxing": 0, "close": 2903.552, "open": 2904.477, "day": "2019-05-21 14:25:00"}, {"volume": 268071000, "includeLow": 2902.474, "includeIndex": 90, "high": 2904.541, "includeHigh": 2904.755, "low": 2902.474, "includeTail": 1, "fenxing": 0, "close": 2904.191, "open": 2902.647, "day": "2019-05-21 14:30:00"}, {"volume": 392788000, "includeLow": 2904.521, "includeIndex": -1, "high": 2908.417, "includeHigh": 2908.417, "low": 2904.521, "includeTail": 0, "fenxing": 1, "close": 2905.247, "open": 2904.521, "day": "2019-05-21 14:35:00"}, {"volume": 299989300, "includeLow": 2903.026, "includeIndex": -1, "high": 2905.717, "includeHigh": 2905.717, "low": 2903.026, "includeTail": 0, "fenxing": 2, "close": 2903.981, "open": 2905.521, "day": "2019-05-21 14:40:00"}, {"volume": 335463200, "includeLow": 2903.721, "includeIndex": 94, "high": 2906.518, "includeHigh": 2906.518, "low": 2903.721, "includeTail": 0, "fenxing": 0, "close": 2905.597, "open": 2904.152, "day": "2019-05-21 14:45:00"}, {"volume": 415662500, "includeLow": 2904.048, "includeIndex": 94, "high": 2906.221, "includeHigh": 2906.518, "low": 2904.048, "includeTail": 0, "fenxing": 0, "close": 2904.048, "open": 2905.648, "day": "2019-05-21 14:50:00"}, {"volume": 537285900, "includeLow": 2904.317, "includeIndex": 94, "high": 2905.893, "includeHigh": 2906.518, "low": 2904.317, "includeTail": 1, "fenxing": 0, "close": 2905.263, "open": 2904.366, "day": "2019-05-21 14:55:00"}, {"volume": 559624700, "includeLow": 2905.576, "includeIndex": -1, "high": 2906.963, "includeHigh": 2906.963, "low": 2905.576, "includeTail": 0, "fenxing": 1, "close": 2905.969, "open": 2906.136, "day": "2019-05-21 15:00:00"}, {"volume": 1607968800, "includeLow": 2898.13, "includeIndex": -1, "high": 2905.524, "includeHigh": 2905.524, "low": 2898.13, "includeTail": 0, "fenxing": 0, "close": 2900.621, "open": 2905.524, "day": "2019-05-22 09:35:00"}, {"volume": 1022414700, "includeLow": 2892.347, "includeIndex": -1, "high": 2899.511, "includeHigh": 2899.511, "low": 2892.347, "includeTail": 0, "fenxing": 0, "close": 2892.938, "open": 2899.511, "day": "2019-05-22 09:40:00"}, {"volume": 863424800, "includeLow": 2890.073, "includeIndex": -1, "high": 2896.735, "includeHigh": 2896.735, "low": 2890.073, "includeTail": 0, "fenxing": 2, "close": 2896.587, "open": 2893.195, "day": "2019-05-22 09:45:00"}, {"volume": 727474700, "includeLow": 2891.822, "includeIndex": 101, "high": 2896.934, "includeHigh": 2896.934, "low": 2891.822, "includeTail": 0, "fenxing": 0, "close": 2892.073, "open": 2896.934, "day": "2019-05-22 09:50:00"}, {"volume": 681652400, "includeLow": 2891.822, "includeIndex": 101, "high": 2900.16, "includeHigh": 2900.16, "low": 2891.794, "includeTail": 1, "fenxing": 0, "close": 2900.143, "open": 2892.208, "day": "2019-05-22 09:55:00"}, {"volume": 646639200, "includeLow": 2899.505, "includeIndex": -1, "high": 2904.427, "includeHigh": 2904.427, "low": 2899.505, "includeTail": 0, "fenxing": 0, "close": 2902.71, "open": 2900.56, "day": "2019-05-22 10:00:00"}, {"volume": 523585000, "includeLow": 2900.62, "includeIndex": 104, "high": 2905.841, "includeHigh": 2905.841, "low": 2900.62, "includeTail": 0, "fenxing": 0, "close": 2904.9, "open": 2902.731, "day": "2019-05-22 10:05:00"}, {"volume": 481342900, "includeLow": 2901.174, "includeIndex": 104, "high": 2905.522, "includeHigh": 2905.841, "low": 2901.174, "includeTail": 1, "fenxing": 0, "close": 2902.117, "open": 2905.147, "day": "2019-05-22 10:10:00"}, {"volume": 539387200, "includeLow": 2902.108, "includeIndex": -1, "high": 2911.314, "includeHigh": 2911.314, "low": 2902.108, "includeTail": 0, "fenxing": 0, "close": 2910.124, "open": 2902.251, "day": "2019-05-22 10:15:00"}, {"volume": 384655700, "includeLow": 2907.295, "includeIndex": -1, "high": 2912.395, "includeHigh": 2912.395, "low": 2907.295, "includeTail": 0, "fenxing": 1, "close": 2908.404, "open": 2910.442, "day": "2019-05-22 10:20:00"}, {"volume": 390404000, "includeLow": 2903.643, "includeIndex": -1, "high": 2909.94, "includeHigh": 2909.94, "low": 2903.643, "includeTail": 0, "fenxing": 0, "close": 2903.842, "open": 2908.327, "day": "2019-05-22 10:25:00"}, {"volume": 304043000, "includeLow": 2903.126, "includeIndex": -1, "high": 2904.876, "includeHigh": 2904.876, "low": 2903.126, "includeTail": 0, "fenxing": 0, "close": 2903.545, "open": 2903.528, "day": "2019-05-22 10:30:00"}, {"volume": 350126800, "includeLow": 2901.095, "includeIndex": -1, "high": 2903.788, "includeHigh": 2903.788, "low": 2901.095, "includeTail": 0, "fenxing": 0, "close": 2901.755, "open": 2903.788, "day": "2019-05-22 10:35:00"}, {"volume": 426897200, "includeLow": 2898.002, "includeIndex": -1, "high": 2902.337, "includeHigh": 2902.337, "low": 2898.002, "includeTail": 0, "fenxing": 0, "close": 2898.002, "open": 2901.216, "day": "2019-05-22 10:40:00"}, {"volume": 410300400, "includeLow": 2897.452, "includeIndex": -1, "high": 2901.042, "includeHigh": 2901.042, "low": 2897.452, "includeTail": 0, "fenxing": 0, "close": 2898.519, "open": 2897.977, "day": "2019-05-22 10:45:00"}, {"volume": 341671700, "includeLow": 2896.74, "includeIndex": -1, "high": 2899.449, "includeHigh": 2899.449, "low": 2896.74, "includeTail": 0, "fenxing": 2, "close": 2899.449, "open": 2898.316, "day": "2019-05-22 10:50:00"}, {"volume": 290240500, "includeLow": 2897.484, "includeIndex": -1, "high": 2899.794, "includeHigh": 2899.794, "low": 2897.484, "includeTail": 0, "fenxing": 0, "close": 2897.936, "open": 2899.778, "day": "2019-05-22 10:55:00"}, {"volume": 390553700, "includeLow": 2897.491, "includeIndex": 115, "high": 2907.458, "includeHigh": 2907.458, "low": 2897.491, "includeTail": 0, "fenxing": 0, "close": 2904.302, "open": 2897.547, "day": "2019-05-22 11:00:00"}, {"volume": 213017100, "includeLow": 2902.008, "includeIndex": 115, "high": 2905.275, "includeHigh": 2907.458, "low": 2902.008, "includeTail": 0, "fenxing": 0, "close": 2903.929, "open": 2903.904, "day": "2019-05-22 11:05:00"}, {"volume": 207215300, "includeLow": 2903.059, "includeIndex": 115, "high": 2905.999, "includeHigh": 2907.458, "low": 2903.059, "includeTail": 0, "fenxing": 0, "close": 2905.598, "open": 2903.572, "day": "2019-05-22 11:10:00"}, {"volume": 238187100, "includeLow": 2904.536, "includeIndex": 115, "high": 2907.429, "includeHigh": 2907.458, "low": 2904.536, "includeTail": 1, "fenxing": 1, "close": 2904.536, "open": 2905.797, "day": "2019-05-22 11:15:00"}, {"volume": 168984400, "includeLow": 2904.014, "includeIndex": -1, "high": 2905.811, "includeHigh": 2905.811, "low": 2904.014, "includeTail": 0, "fenxing": 2, "close": 2904.184, "open": 2905.24, "day": "2019-05-22 11:20:00"}, {"volume": 180419300, "includeLow": 2904.037, "includeIndex": -1, "high": 2907.135, "includeHigh": 2907.135, "low": 2904.037, "includeTail": 0, "fenxing": 0, "close": 2906.795, "open": 2904.037, "day": "2019-05-22 11:25:00"}, {"volume": 220196500, "includeLow": 2905.204, "includeIndex": -1, "high": 2907.345, "includeHigh": 2907.345, "low": 2905.204, "includeTail": 0, "fenxing": 1, "close": 2905.204, "open": 2906.88, "day": "2019-05-22 11:30:00"}, {"volume": 338466000, "includeLow": 2902.46, "includeIndex": -1, "high": 2906.829, "includeHigh": 2906.829, "low": 2902.46, "includeTail": 0, "fenxing": 0, "close": 2902.834, "open": 2906.239, "day": "2019-05-22 13:05:00"}, {"volume": 208677800, "includeLow": 2899.287, "includeIndex": 123, "high": 2903.064, "includeHigh": 2903.064, "low": 2899.287, "includeTail": 0, "fenxing": 0, "close": 2900.191, "open": 2902.851, "day": "2019-05-22 13:10:00"}, {"volume": 194964000, "includeLow": 2899.287, "includeIndex": 123, "high": 2901.668, "includeHigh": 2901.668, "low": 2899.343, "includeTail": 1, "fenxing": 2, "close": 2901.498, "open": 2900.202, "day": "2019-05-22 13:15:00"}, {"volume": 187388700, "includeLow": 2899.84, "includeIndex": -1, "high": 2902.168, "includeHigh": 2902.168, "low": 2899.84, "includeTail": 0, "fenxing": 1, "close": 2899.84, "open": 2901.529, "day": "2019-05-22 13:20:00"}, {"volume": 256265500, "includeLow": 2897.232, "includeIndex": -1, "high": 2900.891, "includeHigh": 2900.891, "low": 2897.232, "includeTail": 0, "fenxing": 0, "close": 2897.232, "open": 2899.792, "day": "2019-05-22 13:25:00"}, {"volume": 431717500, "includeLow": 2892.897, "includeIndex": -1, "high": 2896.984, "includeHigh": 2896.984, "low": 2892.897, "includeTail": 0, "fenxing": 0, "close": 2893.291, "open": 2896.511, "day": "2019-05-22 13:30:00"}, {"volume": 449908900, "includeLow": 2888.612, "includeIndex": 128, "high": 2893.835, "includeHigh": 2893.835, "low": 2888.612, "includeTail": 0, "fenxing": 0, "close": 2890.519, "open": 2893.036, "day": "2019-05-22 13:35:00"}, {"volume": 277462000, "includeLow": 2888.612, "includeIndex": 128, "high": 2892.671, "includeHigh": 2892.671, "low": 2890.776, "includeTail": 1, "fenxing": 0, "close": 2890.947, "open": 2890.794, "day": "2019-05-22 13:40:00"}, {"volume": 463422400, "includeLow": 2885.806, "includeIndex": -1, "high": 2891.06, "includeHigh": 2891.06, "low": 2885.806, "includeTail": 0, "fenxing": 0, "close": 2885.993, "open": 2891.06, "day": "2019-05-22 13:45:00"}, {"volume": 367552800, "includeLow": 2885.685, "includeIndex": 131, "high": 2890.98, "includeHigh": 2890.98, "low": 2885.685, "includeTail": 0, "fenxing": 0, "close": 2889.322, "open": 2886.204, "day": "2019-05-22 13:50:00"}, {"volume": 267229700, "includeLow": 2885.685, "includeIndex": 131, "high": 2889.291, "includeHigh": 2889.291, "low": 2886.475, "includeTail": 1, "fenxing": 2, "close": 2889.215, "open": 2889.207, "day": "2019-05-22 13:55:00"}, {"volume": 216160300, "includeLow": 2888.573, "includeIndex": -1, "high": 2891.561, "includeHigh": 2891.561, "low": 2888.573, "includeTail": 0, "fenxing": 0, "close": 2891.561, "open": 2889.506, "day": "2019-05-22 14:00:00"}, {"volume": 223683300, "includeLow": 2889.491, "includeIndex": -1, "high": 2892.666, "includeHigh": 2892.666, "low": 2889.491, "includeTail": 0, "fenxing": 1, "close": 2889.792, "open": 2891.299, "day": "2019-05-22 14:05:00"}, {"volume": 391560000, "includeLow": 2882.86, "includeIndex": -1, "high": 2890.004, "includeHigh": 2890.004, "low": 2882.86, "includeTail": 0, "fenxing": 0, "close": 2883.539, "open": 2889.431, "day": "2019-05-22 14:10:00"}, {"volume": 306703900, "includeLow": 2882.846, "includeIndex": -1, "high": 2885.253, "includeHigh": 2885.253, "low": 2882.846, "includeTail": 0, "fenxing": 0, "close": 2882.92, "open": 2883.628, "day": "2019-05-22 14:15:00"}, {"volume": 538726400, "includeLow": 2879.644, "includeIndex": -1, "high": 2883.758, "includeHigh": 2883.758, "low": 2879.644, "includeTail": 0, "fenxing": 2, "close": 2883.57, "open": 2882.886, "day": "2019-05-22 14:20:00"}, {"volume": 360188600, "includeLow": 2883.275, "includeIndex": -1, "high": 2886.11, "includeHigh": 2886.11, "low": 2883.275, "includeTail": 0, "fenxing": 0, "close": 2886.11, "open": 2883.658, "day": "2019-05-22 14:25:00"}, {"volume": 352361400, "includeLow": 2883.82, "includeIndex": -1, "high": 2886.255, "includeHigh": 2886.255, "low": 2883.82, "includeTail": 0, "fenxing": 0, "close": 2884.413, "open": 2886.249, "day": "2019-05-22 14:30:00"}, {"volume": 314531500, "includeLow": 2884.326, "includeIndex": -1, "high": 2887.901, "includeHigh": 2887.901, "low": 2884.326, "includeTail": 0, "fenxing": 0, "close": 2887.427, "open": 2884.326, "day": "2019-05-22 14:35:00"}, {"volume": 397658900, "includeLow": 2887.197, "includeIndex": 141, "high": 2893.033, "includeHigh": 2893.033, "low": 2887.197, "includeTail": 0, "fenxing": 0, "close": 2891.178, "open": 2887.299, "day": "2019-05-22 14:40:00"}, {"volume": 374451500, "includeLow": 2890.247, "includeIndex": 141, "high": 2892.271, "includeHigh": 2893.033, "low": 2890.247, "includeTail": 1, "fenxing": 1, "close": 2890.962, "open": 2890.855, "day": "2019-05-22 14:45:00"}, {"volume": 401656100, "includeLow": 2888.243, "includeIndex": -1, "high": 2890.638, "includeHigh": 2890.638, "low": 2888.243, "includeTail": 0, "fenxing": 2, "close": 2888.934, "open": 2890.629, "day": "2019-05-22 14:50:00"}, {"volume": 527443800, "includeLow": 2888.603, "includeIndex": -1, "high": 2890.955, "includeHigh": 2890.955, "low": 2888.603, "includeTail": 0, "fenxing": 0, "close": 2890.473, "open": 2888.603, "day": "2019-05-22 14:55:00"}, {"volume": 531944800, "includeLow": 2890.168, "includeIndex": -1, "high": 2891.74, "includeHigh": 2891.74, "low": 2890.168, "includeTail": 0, "fenxing": 1, "close": 2891.705, "open": 2890.282, "day": "2019-05-22 15:00:00"}, {"volume": 1639545800, "includeLow": 2863.492, "includeIndex": -1, "high": 2880.843, "includeHigh": 2880.843, "low": 2863.492, "includeTail": 0, "fenxing": 0, "close": 2865.709, "open": 2880.843, "day": "2019-05-23 09:35:00"}, {"volume": 1158949200, "includeLow": 2857.974, "includeIndex": -1, "high": 2865.84, "includeHigh": 2865.84, "low": 2857.974, "includeTail": 0, "fenxing": 2, "close": 2862.941, "open": 2865.781, "day": "2019-05-23 09:40:00"}, {"volume": 792898200, "includeLow": 2858.497, "includeIndex": 148, "high": 2866.01, "includeHigh": 2866.01, "low": 2858.497, "includeTail": 0, "fenxing": 0, "close": 2859.365, "open": 2863.069, "day": "2019-05-23 09:45:00"}, {"volume": 736468300, "includeLow": 2859.233, "includeIndex": 148, "high": 2865.278, "includeHigh": 2866.01, "low": 2859.233, "includeTail": 1, "fenxing": 1, "close": 2863.186, "open": 2859.394, "day": "2019-05-23 09:50:00"}, {"volume": 577159100, "includeLow": 2857.976, "includeIndex": 150, "high": 2862.835, "includeHigh": 2862.835, "low": 2857.976, "includeTail": 0, "fenxing": 0, "close": 2860.623, "open": 2862.756, "day": "2019-05-23 09:55:00"}, {"volume": 525598900, "includeLow": 2857.976, "includeIndex": 150, "high": 2862.342, "includeHigh": 2862.342, "low": 2859.465, "includeTail": 1, "fenxing": 0, "close": 2859.661, "open": 2860.621, "day": "2019-05-23 10:00:00"}, {"volume": 607772100, "includeLow": 2854.674, "includeIndex": -1, "high": 2859.384, "includeHigh": 2859.384, "low": 2854.674, "includeTail": 0, "fenxing": 2, "close": 2857.752, "open": 2859.384, "day": "2019-05-23 10:05:00"}, {"volume": 433407600, "includeLow": 2855.385, "includeIndex": -1, "high": 2859.506, "includeHigh": 2859.506, "low": 2855.385, "includeTail": 0, "fenxing": 0, "close": 2859.394, "open": 2857.391, "day": "2019-05-23 10:10:00"}, {"volume": 378672600, "includeLow": 2858.735, "includeIndex": -1, "high": 2862.905, "includeHigh": 2862.905, "low": 2858.735, "includeTail": 0, "fenxing": 0, "close": 2862.238, "open": 2859.527, "day": "2019-05-23 10:15:00"}, {"volume": 319021700, "includeLow": 2861.206, "includeIndex": 155, "high": 2864.123, "includeHigh": 2864.123, "low": 2861.206, "includeTail": 0, "fenxing": 0, "close": 2861.905, "open": 2862.815, "day": "2019-05-23 10:20:00"}, {"volume": 293136800, "includeLow": 2861.206, "includeIndex": 155, "high": 2866.399, "includeHigh": 2866.399, "low": 2860.052, "includeTail": 1, "fenxing": 0, "close": 2866.399, "open": 2861.752, "day": "2019-05-23 10:25:00"}, {"volume": 297726600, "includeLow": 2864.238, "includeIndex": 157, "high": 2866.892, "includeHigh": 2866.892, "low": 2864.238, "includeTail": 0, "fenxing": 0, "close": 2864.98, "open": 2866.041, "day": "2019-05-23 10:30:00"}, {"volume": 436672100, "includeLow": 2864.238, "includeIndex": 157, "high": 2873.268, "includeHigh": 2873.268, "low": 2863.266, "includeTail": 1, "fenxing": 0, "close": 2873.05, "open": 2865.087, "day": "2019-05-23 10:35:00"}, {"volume": 396613700, "includeLow": 2871.141, "includeIndex": -1, "high": 2874.141, "includeHigh": 2874.141, "low": 2871.141, "includeTail": 0, "fenxing": 1, "close": 2871.79, "open": 2872.874, "day": "2019-05-23 10:40:00"}, {"volume": 253949300, "includeLow": 2870.539, "includeIndex": 160, "high": 2873.071, "includeHigh": 2873.071, "low": 2870.539, "includeTail": 0, "fenxing": 0, "close": 2871.136, "open": 2871.818, "day": "2019-05-23 10:45:00"}, {"volume": 318447800, "includeLow": 2870.509, "includeIndex": 160, "high": 2876.445, "includeHigh": 2873.071, "low": 2870.509, "includeTail": 1, "fenxing": 2, "close": 2876.056, "open": 2870.509, "day": "2019-05-23 10:50:00"}, {"volume": 466772300, "includeLow": 2875.764, "includeIndex": 162, "high": 2885.139, "includeHigh": 2885.139, "low": 2875.764, "includeTail": 0, "fenxing": 0, "close": 2883.444, "open": 2875.813, "day": "2019-05-23 10:55:00"}, {"volume": 283406300, "includeLow": 2876.231, "includeIndex": 162, "high": 2883.153, "includeHigh": 2885.139, "low": 2876.231, "includeTail": 1, "fenxing": 1, "close": 2876.755, "open": 2883.153, "day": "2019-05-23 11:00:00"}, {"volume": 240534300, "includeLow": 2872.303, "includeIndex": -1, "high": 2876.482, "includeHigh": 2876.482, "low": 2872.303, "includeTail": 0, "fenxing": 0, "close": 2872.587, "open": 2876.482, "day": "2019-05-23 11:05:00"}, {"volume": 235967000, "includeLow": 2868.699, "includeIndex": -1, "high": 2873.069, "includeHigh": 2873.069, "low": 2868.699, "includeTail": 0, "fenxing": 0, "close": 2869.41, "open": 2872.066, "day": "2019-05-23 11:10:00"}, {"volume": 218513800, "includeLow": 2867.045, "includeIndex": 166, "high": 2869.979, "includeHigh": 2869.979, "low": 2867.045, "includeTail": 0, "fenxing": 0, "close": 2869.132, "open": 2869.233, "day": "2019-05-23 11:15:00"}, {"volume": 179926100, "includeLow": 2867.045, "includeIndex": 166, "high": 2869.197, "includeHigh": 2869.197, "low": 2867.814, "includeTail": 1, "fenxing": 0, "close": 2868.87, "open": 2869.155, "day": "2019-05-23 11:20:00"}, {"volume": 205769000, "includeLow": 2865.71, "includeIndex": -1, "high": 2868.424, "includeHigh": 2868.424, "low": 2865.71, "includeTail": 0, "fenxing": 0, "close": 2865.884, "open": 2867.752, "day": "2019-05-23 11:25:00"}, {"volume": 268380600, "includeLow": 2862.722, "includeIndex": -1, "high": 2867.43, "includeHigh": 2867.43, "low": 2862.722, "includeTail": 0, "fenxing": 2, "close": 2867.258, "open": 2865.501, "day": "2019-05-23 11:30:00"}, {"volume": 257336700, "includeLow": 2863.675, "includeIndex": 170, "high": 2868.843, "includeHigh": 2868.843, "low": 2863.675, "includeTail": 0, "fenxing": 0, "close": 2863.894, "open": 2867.135, "day": "2019-05-23 13:05:00"}, {"volume": 233296900, "includeLow": 2863.675, "includeIndex": 170, "high": 2872.383, "includeHigh": 2872.383, "low": 2863.42, "includeTail": 0, "fenxing": 0, "close": 2869.345, "open": 2864.127, "day": "2019-05-23 13:10:00"}, {"volume": 173605300, "includeLow": 2866.723, "includeIndex": 170, "high": 2869.065, "includeHigh": 2872.383, "low": 2866.723, "includeTail": 1, "fenxing": 1, "close": 2868.444, "open": 2869.065, "day": "2019-05-23 13:15:00"}, {"volume": 180625700, "includeLow": 2866.331, "includeIndex": 173, "high": 2868.993, "includeHigh": 2868.993, "low": 2866.331, "includeTail": 0, "fenxing": 0, "close": 2868.646, "open": 2868.403, "day": "2019-05-23 13:20:00"}, {"volume": 295803200, "includeLow": 2862.414, "includeIndex": 173, "high": 2869.122, "includeHigh": 2868.993, "low": 2862.414, "includeTail": 1, "fenxing": 0, "close": 2862.691, "open": 2868.166, "day": "2019-05-23 13:25:00"}, {"volume": 314880400, "includeLow": 2862.094, "includeIndex": -1, "high": 2865.52, "includeHigh": 2865.52, "low": 2862.094, "includeTail": 0, "fenxing": 2, "close": 2865.16, "open": 2863.018, "day": "2019-05-23 13:30:00"}, {"volume": 301408000, "includeLow": 2864.89, "includeIndex": -1, "high": 2870.184, "includeHigh": 2870.184, "low": 2864.89, "includeTail": 0, "fenxing": 0, "close": 2870.184, "open": 2865.108, "day": "2019-05-23 13:35:00"}, {"volume": 311799800, "includeLow": 2866.04, "includeIndex": -1, "high": 2871.001, "includeHigh": 2871.001, "low": 2866.04, "includeTail": 0, "fenxing": 1, "close": 2866.376, "open": 2869.643, "day": "2019-05-23 13:40:00"}, {"volume": 267665200, "includeLow": 2865.131, "includeIndex": -1, "high": 2866.925, "includeHigh": 2866.925, "low": 2865.131, "includeTail": 0, "fenxing": 0, "close": 2865.435, "open": 2865.948, "day": "2019-05-23 13:45:00"}, {"volume": 331050400, "includeLow": 2863.525, "includeIndex": -1, "high": 2866.262, "includeHigh": 2866.262, "low": 2863.525, "includeTail": 0, "fenxing": 0, "close": 2863.525, "open": 2865.813, "day": "2019-05-23 13:50:00"}, {"volume": 477346600, "includeLow": 2859.738, "includeIndex": 180, "high": 2863.742, "includeHigh": 2863.742, "low": 2859.738, "includeTail": 0, "fenxing": 0, "close": 2861.673, "open": 2863.742, "day": "2019-05-23 13:55:00"}, {"volume": 262291200, "includeLow": 2859.738, "includeIndex": 180, "high": 2862.665, "includeHigh": 2862.665, "low": 2861.019, "includeTail": 1, "fenxing": 0, "close": 2861.097, "open": 2861.347, "day": "2019-05-23 14:00:00"}, {"volume": 295073400, "includeLow": 2859.333, "includeIndex": -1, "high": 2862.088, "includeHigh": 2862.088, "low": 2859.333, "includeTail": 0, "fenxing": 0, "close": 2859.333, "open": 2861.144, "day": "2019-05-23 14:05:00"}, {"volume": 483564700, "includeLow": 2854.976, "includeIndex": 183, "high": 2859.288, "includeHigh": 2859.288, "low": 2854.976, "includeTail": 0, "fenxing": 0, "close": 2857.617, "open": 2859.27, "day": "2019-05-23 14:10:00"}, {"volume": 299623900, "includeLow": 2854.976, "includeIndex": 183, "high": 2858.77, "includeHigh": 2858.77, "low": 2856.707, "includeTail": 1, "fenxing": 2, "close": 2857.935, "open": 2857.998, "day": "2019-05-23 14:15:00"}, {"volume": 323969400, "includeLow": 2856.768, "includeIndex": -1, "high": 2861.857, "includeHigh": 2861.857, "low": 2856.768, "includeTail": 0, "fenxing": 0, "close": 2861.703, "open": 2857.712, "day": "2019-05-23 14:20:00"}, {"volume": 288697000, "includeLow": 2861.641, "includeIndex": -1, "high": 2864.745, "includeHigh": 2864.745, "low": 2861.641, "includeTail": 0, "fenxing": 1, "close": 2862.017, "open": 2861.715, "day": "2019-05-23 14:25:00"}, {"volume": 261261700, "includeLow": 2857.638, "includeIndex": -1, "high": 2862.3, "includeHigh": 2862.3, "low": 2857.638, "includeTail": 0, "fenxing": 0, "close": 2857.792, "open": 2861.982, "day": "2019-05-23 14:30:00"}, {"volume": 394827200, "includeLow": 2853.1, "includeIndex": -1, "high": 2857.425, "includeHigh": 2857.425, "low": 2853.1, "includeTail": 0, "fenxing": 0, "close": 2853.459, "open": 2857.3, "day": "2019-05-23 14:35:00"}, {"volume": 404214900, "includeLow": 2852.697, "includeIndex": -1, "high": 2854.695, "includeHigh": 2854.695, "low": 2852.697, "includeTail": 0, "fenxing": 0, "close": 2853.278, "open": 2853.537, "day": "2019-05-23 14:40:00"}, {"volume": 663991500, "includeLow": 2847.613, "includeIndex": -1, "high": 2853.341, "includeHigh": 2853.341, "low": 2847.613, "includeTail": 0, "fenxing": 0, "close": 2847.939, "open": 2853.341, "day": "2019-05-23 14:45:00"}, {"volume": 513873200, "includeLow": 2846.956, "includeIndex": -1, "high": 2850.843, "includeHigh": 2850.843, "low": 2846.956, "includeTail": 0, "fenxing": 2, "close": 2849.536, "open": 2847.424, "day": "2019-05-23 14:50:00"}, {"volume": 585215600, "includeLow": 2848.588, "includeIndex": -1, "high": 2851.685, "includeHigh": 2851.685, "low": 2848.588, "includeTail": 0, "fenxing": 0, "close": 2851.438, "open": 2849.198, "day": "2019-05-23 14:55:00"}, {"volume": 543400600, "includeLow": 2851.551, "includeIndex": 193, "high": 2853.239, "includeHigh": 2853.239, "low": 2851.551, "includeTail": 0, "fenxing": 0, "close": 2852.515, "open": 2851.789, "day": "2019-05-23 15:00:00"}, {"volume": 1509061300, "includeLow": 2851.551, "includeIndex": 193, "high": 2866.766, "includeHigh": 2866.766, "low": 2847.836, "includeTail": 1, "fenxing": 0, "close": 2861.495, "open": 2847.836, "day": "2019-05-24 09:35:00"}, {"volume": 833435400, "includeLow": 2861.035, "includeIndex": -1, "high": 2870.773, "includeHigh": 2870.773, "low": 2861.035, "includeTail": 0, "fenxing": 0, "close": 2870.581, "open": 2861.035, "day": "2019-05-24 09:40:00"}, {"volume": 618686900, "includeLow": 2866.929, "includeIndex": -1, "high": 2871.856, "includeHigh": 2871.856, "low": 2866.929, "includeTail": 0, "fenxing": 1, "close": 2868.369, "open": 2870.362, "day": "2019-05-24 09:45:00"}, {"volume": 702356000, "includeLow": 2856.783, "includeIndex": 197, "high": 2868.124, "includeHigh": 2868.124, "low": 2856.783, "includeTail": 0, "fenxing": 0, "close": 2861.99, "open": 2868.124, "day": "2019-05-24 09:50:00"}, {"volume": 508129500, "includeLow": 2856.783, "includeIndex": 197, "high": 2863.021, "includeHigh": 2863.021, "low": 2858.448, "includeTail": 1, "fenxing": 2, "close": 2863.021, "open": 2861.543, "day": "2019-05-24 09:55:00"}, {"volume": 552279700, "includeLow": 2860.164, "includeIndex": -1, "high": 2868.198, "includeHigh": 2868.198, "low": 2860.164, "includeTail": 0, "fenxing": 1, "close": 2861.369, "open": 2862.884, "day": "2019-05-24 10:00:00"}, {"volume": 381784800, "includeLow": 2859.627, "includeIndex": -1, "high": 2862.306, "includeHigh": 2862.306, "low": 2859.627, "includeTail": 0, "fenxing": 0, "close": 2860.091, "open": 2861.808, "day": "2019-05-24 10:05:00"}, {"volume": 559147100, "includeLow": 2853.142, "includeIndex": -1, "high": 2860.112, "includeHigh": 2860.112, "low": 2853.142, "includeTail": 0, "fenxing": 0, "close": 2853.142, "open": 2859.919, "day": "2019-05-24 10:10:00"}, {"volume": 601028000, "includeLow": 2847.54, "includeIndex": -1, "high": 2855.025, "includeHigh": 2855.025, "low": 2847.54, "includeTail": 0, "fenxing": 2, "close": 2854.215, "open": 2852.554, "day": "2019-05-24 10:15:00"}, {"volume": 307707800, "includeLow": 2852.568, "includeIndex": 203, "high": 2855.584, "includeHigh": 2855.584, "low": 2852.568, "includeTail": 0, "fenxing": 0, "close": 2855.052, "open": 2854.128, "day": "2019-05-24 10:20:00"}, {"volume": 260487000, "includeLow": 2852.568, "includeIndex": 203, "high": 2856.406, "includeHigh": 2856.406, "low": 2852.356, "includeTail": 1, "fenxing": 1, "close": 2852.505, "open": 2855.294, "day": "2019-05-24 10:25:00"}, {"volume": 312941600, "includeLow": 2851.25, "includeIndex": -1, "high": 2855.412, "includeHigh": 2855.412, "low": 2851.25, "includeTail": 0, "fenxing": 0, "close": 2853.171, "open": 2852.822, "day": "2019-05-24 10:30:00"}, {"volume": 263523100, "includeLow": 2849.867, "includeIndex": -1, "high": 2853.235, "includeHigh": 2853.235, "low": 2849.867, "includeTail": 0, "fenxing": 0, "close": 2852.11, "open": 2853.107, "day": "2019-05-24 10:35:00"}, {"volume": 255793400, "includeLow": 2849.039, "includeIndex": -1, "high": 2852.343, "includeHigh": 2852.343, "low": 2849.039, "includeTail": 0, "fenxing": 2, "close": 2850.061, "open": 2852.343, "day": "2019-05-24 10:40:00"}, {"volume": 262630100, "includeLow": 2849.409, "includeIndex": 208, "high": 2855.297, "includeHigh": 2855.297, "low": 2849.409, "includeTail": 0, "fenxing": 0, "close": 2854.463, "open": 2849.409, "day": "2019-05-24 10:45:00"}, {"volume": 178238700, "includeLow": 2851.859, "includeIndex": 208, "high": 2854.465, "includeHigh": 2855.297, "low": 2851.859, "includeTail": 0, "fenxing": 0, "close": 2852.096, "open": 2854.291, "day": "2019-05-24 10:50:00"}, {"volume": 167187600, "includeLow": 2851.963, "includeIndex": 208, "high": 2855.119, "includeHigh": 2855.297, "low": 2851.963, "includeTail": 1, "fenxing": 0, "close": 2855.119, "open": 2851.996, "day": "2019-05-24 10:55:00"}, {"volume": 360605000, "includeLow": 2855.157, "includeIndex": -1, "high": 2863.595, "includeHigh": 2863.595, "low": 2855.157, "includeTail": 0, "fenxing": 0, "close": 2863.24, "open": 2855.214, "day": "2019-05-24 11:00:00"}, {"volume": 245358300, "includeLow": 2863.379, "includeIndex": -1, "high": 2865.052, "includeHigh": 2865.052, "low": 2863.379, "includeTail": 0, "fenxing": 1, "close": 2863.379, "open": 2863.764, "day": "2019-05-24 11:05:00"}, {"volume": 245333200, "includeLow": 2859.059, "includeIndex": -1, "high": 2864.006, "includeHigh": 2864.006, "low": 2859.059, "includeTail": 0, "fenxing": 0, "close": 2859.241, "open": 2863.905, "day": "2019-05-24 11:10:00"}, {"volume": 174330700, "includeLow": 2856.677, "includeIndex": -1, "high": 2859.627, "includeHigh": 2859.627, "low": 2856.677, "includeTail": 0, "fenxing": 0, "close": 2858.49, "open": 2859.355, "day": "2019-05-24 11:15:00"}, {"volume": 210487000, "includeLow": 2855.745, "includeIndex": -1, "high": 2858.594, "includeHigh": 2858.594, "low": 2855.745, "includeTail": 0, "fenxing": 2, "close": 2858.434, "open": 2857.111, "day": "2019-05-24 11:20:00"}, {"volume": 231531000, "includeLow": 2856.625, "includeIndex": -1, "high": 2858.926, "includeHigh": 2858.926, "low": 2856.625, "includeTail": 0, "fenxing": 1, "close": 2856.625, "open": 2858.508, "day": "2019-05-24 11:25:00"}, {"volume": 280668200, "includeLow": 2850.897, "includeIndex": -1, "high": 2856.435, "includeHigh": 2856.435, "low": 2850.897, "includeTail": 0, "fenxing": 0, "close": 2851.367, "open": 2856.328, "day": "2019-05-24 11:30:00"}, {"volume": 328201800, "includeLow": 2849.527, "includeIndex": -1, "high": 2851.503, "includeHigh": 2851.503, "low": 2849.527, "includeTail": 0, "fenxing": 0, "close": 2849.63, "open": 2851.503, "day": "2019-05-24 13:05:00"}, {"volume": 269793400, "includeLow": 2846.322, "includeIndex": -1, "high": 2850.286, "includeHigh": 2850.286, "low": 2846.322, "includeTail": 0, "fenxing": 2, "close": 2848.113, "open": 2850.234, "day": "2019-05-24 13:10:00"}, {"volume": 185695200, "includeLow": 2848.146, "includeIndex": 220, "high": 2850.834, "includeHigh": 2850.834, "low": 2848.146, "includeTail": 0, "fenxing": 0, "close": 2850.585, "open": 2848.63, "day": "2019-05-24 13:15:00"}, {"volume": 172012000, "includeLow": 2848.146, "includeIndex": 220, "high": 2851.136, "includeHigh": 2851.136, "low": 2847.661, "includeTail": 0, "fenxing": 0, "close": 2848.131, "open": 2850.105, "day": "2019-05-24 13:20:00"}, {"volume": 221813100, "includeLow": 2848.146, "includeIndex": 220, "high": 2851.218, "includeHigh": 2851.218, "low": 2847.104, "includeTail": 1, "fenxing": 0, "close": 2851.048, "open": 2848.172, "day": "2019-05-24 13:25:00"}, {"volume": 193178300, "includeLow": 2850.69, "includeIndex": -1, "high": 2854.159, "includeHigh": 2854.159, "low": 2850.69, "includeTail": 0, "fenxing": 0, "close": 2854.159, "open": 2850.898, "day": "2019-05-24 13:30:00"}, {"volume": 242765300, "includeLow": 2853.938, "includeIndex": -1, "high": 2857.431, "includeHigh": 2857.431, "low": 2853.938, "includeTail": 0, "fenxing": 0, "close": 2856.104, "open": 2854.126, "day": "2019-05-24 13:35:00"}, {"volume": 162502700, "includeLow": 2854.898, "includeIndex": 225, "high": 2857.453, "includeHigh": 2857.453, "low": 2854.898, "includeTail": 0, "fenxing": 0, "close": 2856.808, "open": 2856.42, "day": "2019-05-24 13:40:00"}, {"volume": 213668500, "includeLow": 2854.898, "includeIndex": 225, "high": 2858.257, "includeHigh": 2858.257, "low": 2853.881, "includeTail": 1, "fenxing": 1, "close": 2854.091, "open": 2857.131, "day": "2019-05-24 13:45:00"}, {"volume": 182013600, "includeLow": 2852.35, "includeIndex": -1, "high": 2855.052, "includeHigh": 2855.052, "low": 2852.35, "includeTail": 0, "fenxing": 2, "close": 2854.696, "open": 2854.785, "day": "2019-05-24 13:50:00"}, {"volume": 177235800, "includeLow": 2854.955, "includeIndex": -1, "high": 2857.563, "includeHigh": 2857.563, "low": 2854.955, "includeTail": 0, "fenxing": 1, "close": 2856.075, "open": 2855.241, "day": "2019-05-24 13:55:00"}, {"volume": 176871100, "includeLow": 2854.725, "includeIndex": -1, "high": 2855.995, "includeHigh": 2855.995, "low": 2854.725, "includeTail": 0, "fenxing": 2, "close": 2854.826, "open": 2855.691, "day": "2019-05-24 14:00:00"}, {"volume": 222794000, "includeLow": 2854.946, "includeIndex": -1, "high": 2858.38, "includeHigh": 2858.38, "low": 2854.946, "includeTail": 0, "fenxing": 1, "close": 2856.711, "open": 2854.946, "day": "2019-05-24 14:05:00"}, {"volume": 307744900, "includeLow": 2850.523, "includeIndex": -1, "high": 2857.216, "includeHigh": 2857.216, "low": 2850.523, "includeTail": 0, "fenxing": 0, "close": 2850.523, "open": 2857.118, "day": "2019-05-24 14:10:00"}, {"volume": 280990800, "includeLow": 2848.405, "includeIndex": 232, "high": 2851.53, "includeHigh": 2851.53, "low": 2848.405, "includeTail": 0, "fenxing": 0, "close": 2848.631, "open": 2850.592, "day": "2019-05-24 14:15:00"}, {"volume": 317875800, "includeLow": 2847.643, "includeIndex": 232, "high": 2851.93, "includeHigh": 2851.53, "low": 2847.643, "includeTail": 1, "fenxing": 2, "close": 2851.93, "open": 2849.011, "day": "2019-05-24 14:20:00"}, {"volume": 260396200, "includeLow": 2851.898, "includeIndex": -1, "high": 2855.991, "includeHigh": 2855.991, "low": 2851.898, "includeTail": 0, "fenxing": 0, "close": 2855.991, "open": 2852.153, "day": "2019-05-24 14:25:00"}, {"volume": 448089800, "includeLow": 2856.166, "includeIndex": 235, "high": 2863.868, "includeHigh": 2863.868, "low": 2856.166, "includeTail": 0, "fenxing": 0, "close": 2863.583, "open": 2856.331, "day": "2019-05-24 14:30:00"}, {"volume": 300718100, "includeLow": 2857.138, "includeIndex": 235, "high": 2863.836, "includeHigh": 2863.868, "low": 2857.138, "includeTail": 1, "fenxing": 1, "close": 2857.692, "open": 2863.778, "day": "2019-05-24 14:35:00"}, {"volume": 247498100, "includeLow": 2856.633, "includeIndex": -1, "high": 2858.454, "includeHigh": 2858.454, "low": 2856.633, "includeTail": 0, "fenxing": 0, "close": 2856.709, "open": 2857.684, "day": "2019-05-24 14:40:00"}, {"volume": 349442800, "includeLow": 2855.135, "includeIndex": -1, "high": 2857.114, "includeHigh": 2857.114, "low": 2855.135, "includeTail": 0, "fenxing": 0, "close": 2855.924, "open": 2857.114, "day": "2019-05-24 14:45:00"}, {"volume": 427318100, "includeLow": 2852.913, "includeIndex": -1, "high": 2855.958, "includeHigh": 2855.958, "low": 2852.913, "includeTail": 0, "fenxing": 0, "close": 2853.287, "open": 2855.306, "day": "2019-05-24 14:50:00"}, {"volume": 529523700, "includeLow": 2851.206, "includeIndex": 240, "high": 2853.297, "includeHigh": 2853.297, "low": 2851.206, "includeTail": 0, "fenxing": 0, "close": 2852.186, "open": 2853.297, "day": "2019-05-24 14:55:00"}, {"volume": 474704300, "includeLow": 2851.206, "includeIndex": 240, "high": 2853.273, "includeHigh": 2853.273, "low": 2851.606, "includeTail": 0, "fenxing": 0, "close": 2852.995, "open": 2852.256, "day": "2019-05-24 15:00:00"}, {"volume": 1185171500, "includeLow": 2840.715, "includeIndex": 240, "high": 2855.769, "includeHigh": 2853.273, "low": 2840.715, "includeTail": 0, "fenxing": 0, "close": 2840.952, "open": 2851.281, "day": "2019-05-27 09:35:00"}, {"volume": 906867100, "includeLow": 2840.282, "includeIndex": 240, "high": 2858.14, "includeHigh": 2853.273, "low": 2840.282, "includeTail": 1, "fenxing": 2, "close": 2854.98, "open": 2841.218, "day": "2019-05-27 09:40:00"}, {"volume": 562908600, "includeLow": 2851.547, "includeIndex": -1, "high": 2856.835, "includeHigh": 2856.835, "low": 2851.547, "includeTail": 0, "fenxing": 1, "close": 2856.835, "open": 2854.646, "day": "2019-05-27 09:45:00"}, {"volume": 480479000, "includeLow": 2848.907, "includeIndex": -1, "high": 2856.441, "includeHigh": 2856.441, "low": 2848.907, "includeTail": 0, "fenxing": 0, "close": 2849.651, "open": 2856.441, "day": "2019-05-27 09:50:00"}, {"volume": 396045000, "includeLow": 2846.923, "includeIndex": -1, "high": 2850.655, "includeHigh": 2850.655, "low": 2846.923, "includeTail": 0, "fenxing": 2, "close": 2850.319, "open": 2849.767, "day": "2019-05-27 09:55:00"}, {"volume": 356482100, "includeLow": 2848.709, "includeIndex": -1, "high": 2851.915, "includeHigh": 2851.915, "low": 2848.709, "includeTail": 0, "fenxing": 1, "close": 2850.369, "open": 2850.508, "day": "2019-05-27 10:00:00"}, {"volume": 313279100, "includeLow": 2846.542, "includeIndex": 248, "high": 2850.356, "includeHigh": 2850.356, "low": 2846.542, "includeTail": 0, "fenxing": 0, "close": 2847.289, "open": 2850.151, "day": "2019-05-27 10:05:00"}, {"volume": 316048100, "includeLow": 2846.542, "includeIndex": 248, "high": 2850.03, "includeHigh": 2850.03, "low": 2847.269, "includeTail": 0, "fenxing": 0, "close": 2848.449, "open": 2847.557, "day": "2019-05-27 10:10:00"}, {"volume": 269332400, "includeLow": 2846.542, "includeIndex": 248, "high": 2849.523, "includeHigh": 2849.523, "low": 2846.722, "includeTail": 1, "fenxing": 0, "close": 2847.195, "open": 2848.078, "day": "2019-05-27 10:15:00"}, {"volume": 284708800, "includeLow": 2844.323, "includeIndex": -1, "high": 2848.122, "includeHigh": 2848.122, "low": 2844.323, "includeTail": 0, "fenxing": 0, "close": 2844.323, "open": 2847.075, "day": "2019-05-27 10:20:00"}, {"volume": 389770000, "includeLow": 2839.868, "includeIndex": -1, "high": 2844.743, "includeHigh": 2844.743, "low": 2839.868, "includeTail": 0, "fenxing": 2, "close": 2843.524, "open": 2844.55, "day": "2019-05-27 10:25:00"}, {"volume": 270240500, "includeLow": 2843.056, "includeIndex": -1, "high": 2846.11, "includeHigh": 2846.11, "low": 2843.056, "includeTail": 0, "fenxing": 0, "close": 2844.955, "open": 2843.395, "day": "2019-05-27 10:30:00"}, {"volume": 285526900, "includeLow": 2843.686, "includeIndex": 254, "high": 2850.352, "includeHigh": 2850.352, "low": 2843.686, "includeTail": 0, "fenxing": 0, "close": 2849.709, "open": 2845.28, "day": "2019-05-27 10:35:00"}, {"volume": 236379000, "includeLow": 2846.178, "includeIndex": 254, "high": 2850.329, "includeHigh": 2850.352, "low": 2846.178, "includeTail": 1, "fenxing": 1, "close": 2846.178, "open": 2850.329, "day": "2019-05-27 10:40:00"}, {"volume": 253206700, "includeLow": 2844.932, "includeIndex": -1, "high": 2847.824, "includeHigh": 2847.824, "low": 2844.932, "includeTail": 0, "fenxing": 0, "close": 2846.297, "open": 2845.78, "day": "2019-05-27 10:45:00"}, {"volume": 204690800, "includeLow": 2843.57, "includeIndex": -1, "high": 2846.312, "includeHigh": 2846.312, "low": 2843.57, "includeTail": 0, "fenxing": 0, "close": 2843.57, "open": 2846.246, "day": "2019-05-27 10:50:00"}, {"volume": 248391100, "includeLow": 2841.666, "includeIndex": -1, "high": 2845.112, "includeHigh": 2845.112, "low": 2841.666, "includeTail": 0, "fenxing": 0, "close": 2843.629, "open": 2843.401, "day": "2019-05-27 10:55:00"}, {"volume": 234439700, "includeLow": 2839.891, "includeIndex": -1, "high": 2843.349, "includeHigh": 2843.349, "low": 2839.891, "includeTail": 0, "fenxing": 0, "close": 2840.352, "open": 2843.349, "day": "2019-05-27 11:00:00"}, {"volume": 202380300, "includeLow": 2838.703, "includeIndex": -1, "high": 2840.703, "includeHigh": 2840.703, "low": 2838.703, "includeTail": 0, "fenxing": 0, "close": 2839.776, "open": 2840.116, "day": "2019-05-27 11:05:00"}, {"volume": 399140200, "includeLow": 2833.036, "includeIndex": -1, "high": 2839.942, "includeHigh": 2839.942, "low": 2833.036, "includeTail": 0, "fenxing": 2, "close": 2839.298, "open": 2839.942, "day": "2019-05-27 11:10:00"}, {"volume": 244896300, "includeLow": 2838.954, "includeIndex": 262, "high": 2844.377, "includeHigh": 2844.377, "low": 2838.954, "includeTail": 0, "fenxing": 0, "close": 2842.983, "open": 2838.954, "day": "2019-05-27 11:15:00"}, {"volume": 149100000, "includeLow": 2840.135, "includeIndex": 262, "high": 2842.946, "includeHigh": 2844.377, "low": 2840.135, "includeTail": 1, "fenxing": 0, "close": 2841.356, "open": 2842.884, "day": "2019-05-27 11:20:00"}, {"volume": 283492200, "includeLow": 2840.887, "includeIndex": -1, "high": 2849.953, "includeHigh": 2849.953, "low": 2840.887, "includeTail": 0, "fenxing": 0, "close": 2849.953, "open": 2840.965, "day": "2019-05-27 11:25:00"}, {"volume": 561879100, "includeLow": 2850.776, "includeIndex": -1, "high": 2860.642, "includeHigh": 2860.642, "low": 2850.776, "includeTail": 0, "fenxing": 0, "close": 2860.509, "open": 2850.776, "day": "2019-05-27 11:30:00"}, {"volume": 624070000, "includeLow": 2860.734, "includeIndex": -1, "high": 2865.8, "includeHigh": 2865.8, "low": 2860.734, "includeTail": 0, "fenxing": 0, "close": 2863.504, "open": 2860.883, "day": "2019-05-27 13:05:00"}, {"volume": 406149100, "includeLow": 2863.35, "includeIndex": -1, "high": 2866.224, "includeHigh": 2866.224, "low": 2863.35, "includeTail": 0, "fenxing": 0, "close": 2864.711, "open": 2863.734, "day": "2019-05-27 13:10:00"}, {"volume": 660211200, "includeLow": 2864.417, "includeIndex": -1, "high": 2877.342, "includeHigh": 2877.342, "low": 2864.417, "includeTail": 0, "fenxing": 0, "close": 2877.342, "open": 2864.417, "day": "2019-05-27 13:15:00"}, {"volume": 416279400, "includeLow": 2873.367, "includeIndex": 269, "high": 2878.103, "includeHigh": 2878.103, "low": 2873.367, "includeTail": 0, "fenxing": 0, "close": 2873.55, "open": 2877.556, "day": "2019-05-27 13:20:00"}, {"volume": 426314000, "includeLow": 2873.508, "includeIndex": 269, "high": 2877.255, "includeHigh": 2878.103, "low": 2873.508, "includeTail": 1, "fenxing": 0, "close": 2876.943, "open": 2873.672, "day": "2019-05-27 13:25:00"}, {"volume": 574446200, "includeLow": 2876.701, "includeIndex": -1, "high": 2884.96, "includeHigh": 2884.96, "low": 2876.701, "includeTail": 0, "fenxing": 0, "close": 2884.96, "open": 2876.827, "day": "2019-05-27 13:30:00"}, {"volume": 542385800, "includeLow": 2882.749, "includeIndex": 272, "high": 2887.38, "includeHigh": 2887.38, "low": 2882.749, "includeTail": 0, "fenxing": 0, "close": 2883.233, "open": 2884.988, "day": "2019-05-27 13:35:00"}, {"volume": 378072700, "includeLow": 2882.892, "includeIndex": 272, "high": 2884.363, "includeHigh": 2887.38, "low": 2882.892, "includeTail": 1, "fenxing": 1, "close": 2883.509, "open": 2883.356, "day": "2019-05-27 13:40:00"}, {"volume": 365180500, "includeLow": 2878.882, "includeIndex": -1, "high": 2883.192, "includeHigh": 2883.192, "low": 2878.882, "includeTail": 0, "fenxing": 2, "close": 2878.882, "open": 2883.192, "day": "2019-05-27 13:45:00"}, {"volume": 217402800, "includeLow": 2879.06, "includeIndex": -1, "high": 2883.481, "includeHigh": 2883.481, "low": 2879.06, "includeTail": 0, "fenxing": 0, "close": 2882.745, "open": 2879.06, "day": "2019-05-27 13:50:00"}]} -------------------------------------------------------------------------------- /static/key.txt: -------------------------------------------------------------------------------- 1 | Ni22sQDNsxAGMywENGxQjY3kzYiBOWzMzNiNWN4IY2jFzNih -------------------------------------------------------------------------------- /static/kline.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 缠论 6 | 7 | 8 |
9 | 序号: 10 | 时间: 11 | 12 | 开盘: 13 | 14 | 收盘: 15 | 16 | 最高: 17 | 18 | 最低: 19 | 次级别笔序号: 20 | 21 |
22 |
23 | 24 |
25 | 26 | 27 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /static/pens.json: -------------------------------------------------------------------------------- 1 | [{"start": 2, "end": 50}, {"start": 50, "end": 159}, {"start": 159, "end": 166}, {"start": 166, "end": 183}, {"start": 183, "end": 200}, {"start": 200, "end": 213}, {"start": 213, "end": 242}, {"start": 242, "end": 254}] -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2018/10/24" -------------------------------------------------------------------------------- /test/cache.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/test/cache.sqlite -------------------------------------------------------------------------------- /test/test_pandas_datareader.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | import pandas_datareader as pdr 4 | import requests_cache 5 | from pandas_datareader.yahoo.fx import YahooFXReader 6 | # from pandas_datareader.tests.yahoo import test_yahoo 7 | 8 | expire_after = datetime.timedelta(days=3) 9 | session = requests_cache.CachedSession(cache_name='cache', backend='sqlite', expire_after=expire_after) 10 | 11 | __author__ = "hanweiwei" 12 | __date__ = "2018/10/24" 13 | 14 | if __name__ == '__main__': 15 | # print pdr.get_data_yahoo('600797.SS', session=session) 16 | yfr = YahooFXReader("601788.SS",interval="1m") 17 | print yfr.read() 18 | # test_yahoo. -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2018/11/12" -------------------------------------------------------------------------------- /tools/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/tools/__init__.pyc -------------------------------------------------------------------------------- /tools/time_tools.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import time 3 | 4 | import datetime 5 | 6 | __author__ = "hanweiwei" 7 | __date__ = "2018/9/20" 8 | 9 | 10 | def timestamp(): 11 | return int(time.time()) 12 | 13 | 14 | def is_today(t): 15 | today = datetime.datetime.today() 16 | return t.day == today.day and t.month == today.month and t.year == today.year 17 | 18 | 19 | def timedelta(old, new): 20 | return new - old 21 | 22 | 23 | def datetime2timestamp(datetime_param): 24 | """ 25 | 26 | :param datetime_param: 27 | :type datetime_param: datetime.datetime 28 | :return: 29 | """ 30 | return int(time.mktime(datetime_param.timetuple())) 31 | 32 | 33 | def date2timestamp(date_param): 34 | """ 35 | 36 | :param datetime_param: 37 | :type datetime_param: datetime.date 38 | :return: 39 | """ 40 | return int(time.mktime(date_param.timetuple())) 41 | 42 | 43 | def m1_2_m5(m1: datetime.datetime): 44 | return m1.replace(minute=m1.minute - m1.minute % 5) 45 | 46 | def m5_2_m30(m1: datetime.datetime): 47 | return m1.replace(minute=m1.minute - m1.minute % 30) 48 | -------------------------------------------------------------------------------- /tools/time_tools.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/tools/time_tools.pyc -------------------------------------------------------------------------------- /trader/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-05-31" -------------------------------------------------------------------------------- /trader/constant.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from enum import Enum 3 | 4 | __author__ = "hanweiwei" 5 | __date__ = "2019-05-31" 6 | 7 | 8 | class Interval(Enum): 9 | """ 10 | Interval of bar data. 11 | """ 12 | SECOND_10 = "10s" 13 | MINUTE = "1m" 14 | MINUTE_5 = "5m" 15 | MINUTE_30 = "30m" 16 | HOUR = "1h" 17 | DAILY = "d" 18 | WEEKLY = "w" 19 | 20 | 21 | class Direction(Enum): 22 | """ 23 | Direction of order/trade/position. 24 | """ 25 | LONG = "多" 26 | SHORT = "空" 27 | NET = "净" 28 | 29 | 30 | class Offset(Enum): 31 | """ 32 | Offset of order/trade. 33 | """ 34 | NONE = "" 35 | OPEN = "开" 36 | CLOSE = "平" 37 | CLOSETODAY = "平今" 38 | CLOSEYESTERDAY = "平昨" 39 | 40 | 41 | class Status(Enum): 42 | """ 43 | Order status. 44 | """ 45 | SUBMITTING = "提交中" 46 | NOTTRADED = "未成交" 47 | PARTTRADED = "部分成交" 48 | ALLTRADED = "全部成交" 49 | CANCELLED = "已撤销" 50 | REJECTED = "拒单" 51 | 52 | 53 | class Product(Enum): 54 | """ 55 | Product class. 56 | """ 57 | EQUITY = "股票" 58 | FUTURES = "期货" 59 | OPTION = "期权" 60 | INDEX = "指数" 61 | FOREX = "外汇" 62 | SPOT = "现货" 63 | ETF = "ETF" 64 | BOND = "债券" 65 | WARRANT = "权证" 66 | SPREAD = "价差" 67 | FUND = "基金" 68 | 69 | 70 | class OrderType(Enum): 71 | """ 72 | Order type. 73 | """ 74 | LIMIT = "限价" 75 | MARKET = "市价" 76 | STOP = "STOP" 77 | FAK = "FAK" 78 | FOK = "FOK" 79 | 80 | 81 | class OptionType(Enum): 82 | """ 83 | Option type. 84 | """ 85 | CALL = "看涨期权" 86 | PUT = "看跌期权" 87 | 88 | 89 | class Exchange(Enum): 90 | """ 91 | Exchange. 92 | """ 93 | # Chinese 94 | CFFEX = "CFFEX" # China Financial Futures Exchange 95 | SHFE = "SHFE" # Shanghai Futures Exchange 96 | CZCE = "CZCE" # Zhengzhou Commodity Exchange 97 | DCE = "DCE" # Dalian Commodity Exchange 98 | INE = "INE" # Shanghai International Energy Exchange 99 | SSE = "SSE" # Shanghai Stock Exchange 100 | SZSE = "SZSE" # Shenzhen Stock Exchange 101 | SGE = "SGE" # Shanghai Gold Exchange 102 | WXE = "WXE" # Wuxi Steel Exchange 103 | 104 | # Global 105 | SMART = "SMART" # Smart Router for US stocks 106 | NYMEX = "NYMEX" # New York Mercantile Exchange 107 | GLOBEX = "GLOBEX" # Globex of CME 108 | IDEALPRO = "IDEALPRO" # Forex ECN of Interactive Brokers 109 | CME = "CME" # Chicago Mercantile Exchange 110 | ICE = "ICE" # Intercontinental Exchange 111 | SEHK = "SEHK" # Stock Exchange of Hong Kong 112 | HKFE = "HKFE" # Hong Kong Futures Exchange 113 | SGX = "SGX" # Singapore Global Exchange 114 | CBOT = "CBT" # Chicago Board of Trade 115 | DME = "DME" # Dubai Mercantile Exchange 116 | EUREX = "EUX" # Eurex Exchange 117 | APEX = "APEX" # Asia Pacific Exchange 118 | LME = "LME" # London Metal Exchange 119 | BMD = "BMD" # Bursa Malaysia Derivatives 120 | TOCOM = "TOCOM" # Tokyo Commodity Exchange 121 | EUNX = "EUNX" # Euronext Exchange 122 | 123 | # CryptoCurrency 124 | BITMEX = "BITMEX" 125 | OKEX = "OKEX" 126 | HUOBI = "HUOBI" 127 | BITFINEX = "BITFINEX" 128 | 129 | AGU = "AGU" 130 | 131 | 132 | class Currency(Enum): 133 | """ 134 | Currency. 135 | """ 136 | USD = "USD" 137 | HKD = "HKD" 138 | CNY = "CNY" 139 | -------------------------------------------------------------------------------- /trader/event.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-06-08" 5 | 6 | EVENT_TICK = "eTick." 7 | EVENT_TRADE = "eTrade." 8 | EVENT_ORDER = "eOrder." 9 | EVENT_POSITION = "ePosition." 10 | EVENT_ACCOUNT = "eAccount." 11 | EVENT_CONTRACT = "eContract." 12 | EVENT_LOG = "eLog" 13 | EVENT_BAR = "eBar" -------------------------------------------------------------------------------- /trader/gateway.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from abc import abstractmethod 3 | 4 | from event.engine import EventEngine, Event 5 | from trader.event import EVENT_TICK, EVENT_TRADE, EVENT_ORDER, EVENT_ACCOUNT, EVENT_POSITION, EVENT_LOG, EVENT_CONTRACT, \ 6 | EVENT_BAR 7 | from trader.object import KLineRequest, BarData, TickData, TradeData, OrderData, PositionData, LogData, AccountData, \ 8 | ContractData, CancelRequest, OrderRequest, SubscribeRequest, SubscribeBarRequest 9 | 10 | from typing import List, Any, Sequence 11 | 12 | __author__ = "hanweiwei" 13 | __date__ = "2019-05-31" 14 | 15 | 16 | class BaseGateway(object): 17 | 18 | def __init__(self, event_engine: EventEngine, gateway_name: str): 19 | """""" 20 | self.event_engine = event_engine 21 | self.gateway_name = gateway_name 22 | 23 | @abstractmethod 24 | def get_kline_data(self, kline_request: KLineRequest): 25 | """ 26 | 27 | :param kline_request: 28 | :type kline_request:KLineRequest 29 | :rtype: List[BarData] 30 | :return: 31 | 32 | """ 33 | raise NotImplementedError 34 | 35 | def on_event(self, type: str, data: Any = None): 36 | """ 37 | General event push. 38 | """ 39 | event = Event(type, data) 40 | self.event_engine.put(event) 41 | 42 | def on_bar(self, bar: BarData): 43 | """ 44 | bar event push. 45 | bar event of a specific vt_symbol is also pushed. 46 | """ 47 | self.on_event(EVENT_BAR, bar) 48 | self.on_event(EVENT_BAR + bar.vt_symbol, bar) 49 | 50 | def on_tick(self, tick: TickData): 51 | """ 52 | Tick event push. 53 | Tick event of a specific vt_symbol is also pushed. 54 | """ 55 | self.on_event(EVENT_TICK, tick) 56 | self.on_event(EVENT_TICK + tick.vt_symbol, tick) 57 | 58 | def on_trade(self, trade: TradeData): 59 | """ 60 | Trade event push. 61 | Trade event of a specific vt_symbol is also pushed. 62 | """ 63 | self.on_event(EVENT_TRADE, trade) 64 | self.on_event(EVENT_TRADE + trade.vt_symbol, trade) 65 | 66 | def on_order(self, order: OrderData): 67 | """ 68 | Order event push. 69 | Order event of a specific vt_orderid is also pushed. 70 | """ 71 | self.on_event(EVENT_ORDER, order) 72 | self.on_event(EVENT_ORDER + order.vt_orderid, order) 73 | 74 | def on_position(self, position: PositionData): 75 | """ 76 | Position event push. 77 | Position event of a specific vt_symbol is also pushed. 78 | """ 79 | self.on_event(EVENT_POSITION, position) 80 | self.on_event(EVENT_POSITION + position.vt_symbol, position) 81 | 82 | def on_account(self, account: AccountData): 83 | """ 84 | Account event push. 85 | Account event of a specific vt_accountid is also pushed. 86 | """ 87 | self.on_event(EVENT_ACCOUNT, account) 88 | self.on_event(EVENT_ACCOUNT + account.vt_accountid, account) 89 | 90 | def on_log(self, log: LogData): 91 | """ 92 | Log event push. 93 | """ 94 | self.on_event(EVENT_LOG, log) 95 | 96 | def on_contract(self, contract: ContractData): 97 | """ 98 | Contract event push. 99 | """ 100 | self.on_event(EVENT_CONTRACT, contract) 101 | 102 | @abstractmethod 103 | def connect(self, setting: dict): 104 | """ 105 | Start gateway connection. 106 | to implement this method, you must: 107 | * connect to server if necessary 108 | * log connected if all necessary connection is established 109 | * do the following query and response corresponding on_xxxx and write_log 110 | * contracts : on_contract 111 | * account asset : on_account 112 | * account holding: on_position 113 | * orders of account: on_order 114 | * trades of account: on_trade 115 | * if any of query above is failed, write log. 116 | future plan: 117 | response callback/change status instead of write_log 118 | """ 119 | pass 120 | 121 | @abstractmethod 122 | def close(self): 123 | """ 124 | Close gateway connection. 125 | """ 126 | pass 127 | 128 | @abstractmethod 129 | def subscribe(self, req: SubscribeRequest): 130 | """ 131 | Subscribe tick data update. 132 | """ 133 | pass 134 | 135 | @abstractmethod 136 | def subscribe_bar(self, req: SubscribeBarRequest): 137 | """ 138 | Subscribe bar data update. 139 | """ 140 | pass 141 | 142 | @abstractmethod 143 | def send_order(self, req: OrderRequest) -> str: 144 | """ 145 | Send a new order to server. 146 | implementation should finish the tasks blow: 147 | * create an OrderData from req using OrderRequest.create_order_data 148 | * assign a unique(gateway instance scope) id to OrderData.orderid 149 | * send request to server 150 | * if request is sent, OrderData.status should be set to Status.SUBMITTING 151 | * if request is failed to sent, OrderData.status should be set to Status.REJECTED 152 | * response on_order: 153 | * return OrderData.vt_orderid 154 | :return str vt_orderid for created OrderData 155 | """ 156 | pass 157 | 158 | @abstractmethod 159 | def cancel_order(self, req: CancelRequest): 160 | """ 161 | Cancel an existing order. 162 | implementation should finish the tasks blow: 163 | * send request to server 164 | """ 165 | pass 166 | 167 | def send_orders(self, reqs: Sequence[OrderRequest]): 168 | """ 169 | Send a batch of orders to server. 170 | Use a for loop of send_order function by default. 171 | Reimplement this function if batch order supported on server. 172 | """ 173 | vt_orderids = [] 174 | 175 | for req in reqs: 176 | vt_orderid = self.send_order(req) 177 | vt_orderids.append(vt_orderid) 178 | 179 | return vt_orderids 180 | 181 | def cancel_orders(self, reqs: Sequence[CancelRequest]): 182 | """ 183 | Cancel a batch of orders to server. 184 | Use a for loop of cancel_order function by default. 185 | Reimplement this function if batch cancel supported on server. 186 | """ 187 | for req in reqs: 188 | self.cancel_order(req) 189 | 190 | @abstractmethod 191 | def query_account(self): 192 | """ 193 | Query account balance. 194 | """ 195 | pass 196 | 197 | @abstractmethod 198 | def query_position(self): 199 | """ 200 | Query holding positions. 201 | """ 202 | pass 203 | 204 | # def query_history(self, req: HistoryRequest): 205 | # """ 206 | # Query bar history data. 207 | # """ 208 | # pass 209 | 210 | def get_default_setting(self): 211 | """ 212 | Return default setting dict. 213 | """ 214 | return self.default_setting -------------------------------------------------------------------------------- /trader/object.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from dataclasses import dataclass 3 | from datetime import datetime 4 | from logging import INFO 5 | 6 | from trader.constant import Exchange, Interval, OrderType, Direction, Offset, Status, Product, OptionType 7 | 8 | ACTIVE_STATUSES = set([Status.SUBMITTING, Status.NOTTRADED, Status.PARTTRADED]) 9 | 10 | __author__ = "hanweiwei" 11 | __date__ = "2019-06-02" 12 | 13 | 14 | @dataclass 15 | class KLineRequest: 16 | """ 17 | Request sending to specific gateway for kline. 18 | """ 19 | 20 | symbol: str 21 | exchange: Exchange 22 | bar_interval: Interval 23 | data_length: int 24 | 25 | def __post_init__(self): 26 | """""" 27 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 28 | 29 | 30 | @dataclass 31 | class BaseData: 32 | """ 33 | Any data object needs a gateway_name as source 34 | and should inherit base data. 35 | """ 36 | 37 | gateway_name: str 38 | 39 | 40 | @dataclass 41 | class BarData(BaseData): 42 | """ 43 | Candlestick bar data of a certain trading period. 44 | """ 45 | 46 | symbol: str 47 | exchange: Exchange 48 | datetime: datetime 49 | 50 | interval: Interval = None 51 | volume: float = 0 52 | open_price: float = 0 53 | high_price: float = 0 54 | low_price: float = 0 55 | close_price: float = 0 56 | 57 | def __post_init__(self): 58 | """""" 59 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 60 | 61 | 62 | @dataclass 63 | class TickData(BaseData): 64 | """ 65 | Tick data contains information about: 66 | * last trade in market 67 | * orderbook snapshot 68 | * intraday market statistics. 69 | """ 70 | 71 | symbol: str 72 | exchange: Exchange 73 | datetime: datetime 74 | 75 | name: str = "" 76 | volume: float = 0 77 | last_price: float = 0 78 | last_volume: float = 0 79 | limit_up: float = 0 80 | limit_down: float = 0 81 | 82 | open_price: float = 0 83 | high_price: float = 0 84 | low_price: float = 0 85 | pre_close: float = 0 86 | 87 | bid_price_1: float = 0 88 | bid_price_2: float = 0 89 | bid_price_3: float = 0 90 | bid_price_4: float = 0 91 | bid_price_5: float = 0 92 | 93 | ask_price_1: float = 0 94 | ask_price_2: float = 0 95 | ask_price_3: float = 0 96 | ask_price_4: float = 0 97 | ask_price_5: float = 0 98 | 99 | bid_volume_1: float = 0 100 | bid_volume_2: float = 0 101 | bid_volume_3: float = 0 102 | bid_volume_4: float = 0 103 | bid_volume_5: float = 0 104 | 105 | ask_volume_1: float = 0 106 | ask_volume_2: float = 0 107 | ask_volume_3: float = 0 108 | ask_volume_4: float = 0 109 | ask_volume_5: float = 0 110 | 111 | def __post_init__(self): 112 | """""" 113 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 114 | 115 | 116 | @dataclass 117 | class OrderData(BaseData): 118 | """ 119 | Order data contains information for tracking lastest status 120 | of a specific order. 121 | """ 122 | 123 | symbol: str 124 | exchange: Exchange 125 | orderid: str 126 | 127 | type: OrderType = OrderType.LIMIT 128 | direction: Direction = "" 129 | offset: Offset = Offset.NONE 130 | price: float = 0 131 | volume: float = 0 132 | traded: float = 0 133 | status: Status = Status.SUBMITTING 134 | time: str = "" 135 | 136 | def __post_init__(self): 137 | """""" 138 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 139 | self.vt_orderid = f"{self.gateway_name}.{self.orderid}" 140 | 141 | def is_active(self): 142 | """ 143 | Check if the order is active. 144 | """ 145 | if self.status in ACTIVE_STATUSES: 146 | return True 147 | else: 148 | return False 149 | 150 | def create_cancel_request(self): 151 | """ 152 | Create cancel request object from order. 153 | """ 154 | req = CancelRequest( 155 | orderid=self.orderid, symbol=self.symbol, exchange=self.exchange 156 | ) 157 | return req 158 | 159 | 160 | @dataclass 161 | class TradeData(BaseData): 162 | """ 163 | Trade data contains information of a fill of an order. One order 164 | can have several trade fills. 165 | """ 166 | 167 | symbol: str 168 | exchange: Exchange 169 | orderid: str 170 | tradeid: str 171 | direction: Direction = "" 172 | 173 | offset: Offset = Offset.NONE 174 | price: float = 0 175 | volume: float = 0 176 | time: str = "" 177 | 178 | def __post_init__(self): 179 | """""" 180 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 181 | self.vt_orderid = f"{self.gateway_name}.{self.orderid}" 182 | self.vt_tradeid = f"{self.gateway_name}.{self.tradeid}" 183 | 184 | 185 | @dataclass 186 | class PositionData(BaseData): 187 | """ 188 | Positon data is used for tracking each individual position holding. 189 | """ 190 | 191 | symbol: str 192 | exchange: Exchange 193 | direction: Direction 194 | 195 | volume: float = 0 196 | frozen: float = 0 197 | price: float = 0 198 | pnl: float = 0 199 | yd_volume: float = 0 200 | 201 | def __post_init__(self): 202 | """""" 203 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 204 | self.vt_positionid = f"{self.vt_symbol}.{self.direction}" 205 | 206 | 207 | @dataclass 208 | class AccountData(BaseData): 209 | """ 210 | Account data contains information about balance, frozen and 211 | available. 212 | """ 213 | 214 | accountid: str 215 | 216 | balance: float = 0 217 | frozen: float = 0 218 | 219 | def __post_init__(self): 220 | """""" 221 | self.available = self.balance - self.frozen 222 | self.vt_accountid = f"{self.gateway_name}.{self.accountid}" 223 | 224 | 225 | @dataclass 226 | class LogData(BaseData): 227 | """ 228 | Log data is used for recording log messages on GUI or in log files. 229 | """ 230 | 231 | msg: str 232 | level: int = INFO 233 | 234 | def __post_init__(self): 235 | """""" 236 | self.time = datetime.now() 237 | 238 | 239 | @dataclass 240 | class ContractData(BaseData): 241 | """ 242 | Contract data contains basic information about each contract traded. 243 | """ 244 | 245 | symbol: str 246 | exchange: Exchange 247 | name: str 248 | product: Product 249 | size: int 250 | pricetick: float 251 | 252 | min_volume: float = 1 # minimum trading volume of the contract 253 | stop_supported: bool = False # whether server supports stop order 254 | net_position: bool = False # whether gateway uses net position volume 255 | history_data: bool = False # whether gateway provides bar history data 256 | 257 | option_strike: float = 0 258 | option_underlying: str = "" # vt_symbol of underlying contract 259 | option_type: OptionType = None 260 | option_expiry: datetime = None 261 | 262 | def __post_init__(self): 263 | """""" 264 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 265 | 266 | 267 | @dataclass 268 | class OrderRequest: 269 | """ 270 | Request sending to specific gateway for creating a new order. 271 | """ 272 | 273 | symbol: str 274 | exchange: Exchange 275 | direction: Direction 276 | type: OrderType 277 | volume: float 278 | price: float = 0 279 | offset: Offset = Offset.NONE 280 | 281 | def __post_init__(self): 282 | """""" 283 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 284 | 285 | def create_order_data(self, orderid: str, gateway_name: str): 286 | """ 287 | Create order data from request. 288 | """ 289 | order = OrderData( 290 | symbol=self.symbol, 291 | exchange=self.exchange, 292 | orderid=orderid, 293 | type=self.type, 294 | direction=self.direction, 295 | offset=self.offset, 296 | price=self.price, 297 | volume=self.volume, 298 | gateway_name=gateway_name, 299 | ) 300 | return order 301 | 302 | 303 | @dataclass 304 | class CancelRequest: 305 | """ 306 | Request sending to specific gateway for canceling an existing order. 307 | """ 308 | 309 | orderid: str 310 | symbol: str 311 | exchange: Exchange 312 | 313 | def __post_init__(self): 314 | """""" 315 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 316 | 317 | 318 | @dataclass 319 | class SubscribeRequest: 320 | """ 321 | Request sending to specific gateway for subscribing tick data update. 322 | """ 323 | 324 | symbol: str 325 | exchange: Exchange 326 | 327 | def __post_init__(self): 328 | """""" 329 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 330 | 331 | 332 | @dataclass 333 | class SubscribeBarRequest: 334 | """ 335 | Request sending to specific gateway for subscribing tick data update. 336 | """ 337 | 338 | symbol: str 339 | exchange: Exchange 340 | interval: Interval 341 | 342 | def __post_init__(self): 343 | """""" 344 | self.vt_symbol = f"{self.symbol}.{self.exchange.value}" 345 | -------------------------------------------------------------------------------- /web/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-06-07" -------------------------------------------------------------------------------- /web/encoder/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2019-06-07" -------------------------------------------------------------------------------- /web/encoder/alchemy_encoder.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import json 3 | from datetime import datetime 4 | from decimal import Decimal 5 | 6 | from flask import json 7 | 8 | from sqlalchemy.ext.declarative import DeclarativeMeta 9 | 10 | __author__ = "hanweiwei" 11 | __date__ = "2019-06-07" 12 | 13 | 14 | class AlchemyEncoder(json.JSONEncoder): 15 | # To serialize SQLalchemy objects 16 | def default(self, obj): 17 | if isinstance(obj.__class__, DeclarativeMeta): 18 | model_fields = {} 19 | for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']: 20 | data = obj.__getattribute__(field) 21 | try: 22 | json.dumps(data) # this will fail on non-encodable values, like other classes 23 | model_fields[field] = data 24 | except TypeError: 25 | model_fields[field] = None 26 | return model_fields 27 | if isinstance(obj, Decimal): 28 | return float(obj) 29 | if isinstance(obj, datetime): 30 | return obj.strftime("%Y-%m-%d %H:%M:%S") 31 | return json.JSONEncoder.default(self, obj) 32 | -------------------------------------------------------------------------------- /web/web_app.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from typing import List 3 | 4 | from chan.db.dao import ChanBarDataDao, PenDao, CentreDao 5 | from chan.db.object import ChanBarDataDO 6 | from chan.level import FiveMinuteLevel, parse_level 7 | from tools import time_tools 8 | from trader.constant import Exchange, Interval 9 | from web.encoder.alchemy_encoder import AlchemyEncoder 10 | from flask_cors import CORS 11 | from flask import request 12 | 13 | __author__ = "hanweiwei" 14 | __date__ = "2019-06-07" 15 | 16 | from flask import Flask, json 17 | 18 | app = Flask(__name__) 19 | CORS(app) 20 | app.json_encoder = AlchemyEncoder 21 | chan_bar_data_dao = ChanBarDataDao() 22 | pen_dao = PenDao() 23 | centre_dao = CentreDao() 24 | 25 | 26 | @app.route('/kline', methods=['GET']) 27 | def kline(): 28 | interval = request.args.get('interval', '5m') 29 | 30 | minute_level = parse_level(interval) 31 | secure = "603568.XSHG" 32 | bars: List[ChanBarDataDO] = chan_bar_data_dao.select("JQ", Exchange.AGU.value, secure, 33 | minute_level.get_level().value, 34 | 10000) 35 | pens = pen_dao.select("JQ", Exchange.AGU.value, secure, minute_level.get_level().value, 36 | bars[0].datetime) 37 | sub_pens = pen_dao.select("JQ", Exchange.AGU.value, secure, minute_level.get_child_level().value, 38 | bars[0].datetime) 39 | sub_centres = centre_dao.select("JQ", Exchange.AGU.value, secure, minute_level.get_level().value, 40 | bars[0].datetime) 41 | for pen in sub_pens: 42 | if pen.interval == "1m": 43 | pen.start_datetime = time_tools.m1_2_m5(pen.start_datetime) 44 | pen.end_datetime = time_tools.m1_2_m5(pen.end_datetime) 45 | else: 46 | pen.start_datetime = time_tools.m5_2_m30(pen.start_datetime) 47 | pen.end_datetime = time_tools.m5_2_m30(pen.end_datetime) 48 | 49 | for centre in sub_centres: 50 | if centre.interval == "1m": 51 | centre.start_datetime = time_tools.m1_2_m5(centre.start_datetime) 52 | centre.end_datetime = time_tools.m1_2_m5(centre.end_datetime) 53 | else: 54 | centre.start_datetime = time_tools.m5_2_m30(centre.start_datetime) 55 | centre.end_datetime = time_tools.m5_2_m30(centre.end_datetime) 56 | 57 | return json.dumps({ 58 | "pens": pens, 59 | "bars": bars, 60 | "sub_pens": sub_pens, 61 | "sub_centres": sub_centres 62 | }) 63 | 64 | 65 | def run_web_app(): 66 | app.run() 67 | 68 | 69 | if __name__ == '__main__': 70 | run_web_app() 71 | -------------------------------------------------------------------------------- /yahoo/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | __author__ = "hanweiwei" 4 | __date__ = "2018/11/12" -------------------------------------------------------------------------------- /yahoo/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/yahoo/__init__.pyc -------------------------------------------------------------------------------- /yahoo/api.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | import datetime 3 | 4 | import pandas as pd 5 | import pytz 6 | import requests 7 | import requests_cache 8 | 9 | from app import app_log 10 | from tools import time_tools 11 | 12 | __author__ = "hanweiwei" 13 | __date__ = "2018/11/12" 14 | 15 | KLINE_URL = "https://query1.finance.yahoo.com/v8/finance/chart/SYMBOL?" \ 16 | "symbol=SYMBOL" \ 17 | "&period1=PERIOD1" \ 18 | "&period2=PERIOD2" \ 19 | "&interval=INTERVAL" \ 20 | "&includePrePost=true" \ 21 | "&events=div%7Csplit%7Cearn" \ 22 | "&lang=zh-Hant-HK" \ 23 | "®ion=HK" \ 24 | "&crumb=x3Yc9Jik83n" \ 25 | "&corsDomain=hk.finance.yahoo.com" 26 | 27 | MINUTE_1 = "1m" 28 | MINUTE_2 = "2m" 29 | MINUTE_5 = "5m" 30 | MINUTE_15 = "15m" 31 | MINUTE_60 = "60m" 32 | DAY = "1d" 33 | 34 | requests_cache.install_cache() 35 | s = requests.Session() 36 | 37 | 38 | def getKLineData(stock_code, scale, start_date, end_date): 39 | """ 40 | 查询K线 41 | :param stock_code: 股票代码 深圳 000001.SZ 上海 600001.SS 42 | :param scale: 周期 1m 5m 15m 30m 60m 1d 1w 1mo 43 | :param start_date: timestamp or date or str or datetime 44 | :param end_date: timestamp or date or str or datetime 45 | :return: DataFrame 46 | """ 47 | 48 | start_date = parse_date(start_date) 49 | end_date = parse_date(end_date) 50 | 51 | now = datetime.datetime.now() 52 | if not start_date: 53 | start_date = time_tools.datetime2timestamp(now + datetime.timedelta(days=-100)) 54 | if not end_date: 55 | end_date = time_tools.datetime2timestamp(now) 56 | 57 | url = KLINE_URL \ 58 | .replace("SYMBOL", stock_code) \ 59 | .replace("PERIOD1", str(start_date)) \ 60 | .replace("PERIOD2", str(end_date)) \ 61 | .replace("INTERVAL", scale) 62 | app_log.info("REQUEST START:\n%s" % url) 63 | 64 | response = s.get(url, headers={'Connection': 'keep-alive', 65 | "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"}) 66 | contents = response.json() 67 | print(response.headers) 68 | app_log.info("REQUEST END:\n%s" % response) 69 | 70 | chart = contents["chart"] 71 | if chart["error"]: 72 | raise Exception(contents["error"]) 73 | contents = chart["result"][0] 74 | quote = contents["indicators"]["quote"][0] 75 | datetime_index = pd.to_datetime(contents["timestamp"], unit="s", utc=True) 76 | datetime_index = datetime_index.tz_convert(pytz.timezone("Asia/Shanghai")) 77 | data = pd.DataFrame({ 78 | "high": quote["high"] 79 | , "low": quote["low"] 80 | , "open": quote["open"] 81 | , "close": quote["close"] 82 | }, index=datetime_index) 83 | 84 | data = data.dropna(how='any') 85 | data['timestamp'] = data.index 86 | data['day'] = "" 87 | day_index = list(data.columns).index('day') 88 | 89 | for i in range(0, len(data)): 90 | day = data['timestamp'][i] 91 | data.iloc[i, day_index] = day.strftime("%Y-%m-%d %H:%M:%SZ") 92 | return data 93 | 94 | 95 | def parse_date(date): 96 | """ 97 | 98 | :param date: timestamp or date or str or datetime 99 | :return: 100 | """ 101 | if date: 102 | if isinstance(date, str): 103 | date = time_tools.datetime2timestamp(datetime.datetime.strptime(date, "%Y%m%d")) 104 | elif isinstance(date, datetime.date): 105 | date = time_tools.date2timestamp(date) 106 | elif isinstance(date, datetime.datetime): 107 | date = time_tools.date2timestamp(date) 108 | elif isinstance(date, int): 109 | pass 110 | else: 111 | raise ValueError("date type error, must be timestamp, date, datetime, str") 112 | return date 113 | 114 | 115 | if __name__ == '__main__': 116 | app_log.info("\n%s", getKLineData("601788.SS", scale=MINUTE_5, start_date="20181111", end_date="20181114")) 117 | # print getKLineData("601788.SS", scale=MINUTE_5, start_date="20181109", end_date="20181111") 118 | -------------------------------------------------------------------------------- /yahoo/api.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kymeii/ChanMaster/b25661ee1d69b756abcbe60e97a8d7c84fcad009/yahoo/api.pyc --------------------------------------------------------------------------------