def sync_collections(): from ybk.config import setup_config from ybk.models import Collection as C1 from ybk.models import Quote as Q1 setup_config() for c in C1.query(): print(c.exchange, c.symbol) td = Q1.count({'exchange': c.exchange, 'symbol': c.symbol, 'type_': '1d'}) + 1 if td == 1: if not c.offers_at: # 没录入过, 基本上会挂 continue # 如果K线不存在, 可能是交易行情无法获取, 直接用估算数字 td = (datetime.utcnow() - c.offers_at).days - 1 c2 = Collection({ 'exchange': c.exchange, 'symbol': c.symbol, 'name': c.name, 'trade_day': td, }) c2.upsert()
def parse(): nav = 'parse' url = request.args.get('url') num_parsed = Announcement.count({ 'parsed': True, 'type_': { '$in': ['offer', 'result'] } }) num_total = Announcement.count({'type_': {'$in': ['offer', 'result']}}) if url: announcement = Announcement.query_one({'url': url}) colls = list(Collection.query({'from_url': url})) for coll in colls: if coll.offers_at: coll.offers_at2 = coll.offers_at.strftime('%Y%m%d') if coll.offer_cash_ratio: coll.offer_cash_ratio = '{:2.0f}%'.format( coll.offer_cash_ratio * 100) if coll.offer_price: coll.offer_price = str(coll.offer_price) if coll.offer_price.endswith('.0'): coll.offer_price = coll.offer_price[:-2] all_done = num_parsed == num_total return render_template('admin/parse.html', **locals())
def load_symbols(): exchange = request.args.get('exchange', '') query = request.args.get('query', '') exclude = request.args.get('exclude', '') cond = { '$or': [{ 'name': { '$regex': query } }, { 'symbol': { '$regex': query } }] } if exchange: cond['exchange'] = exchange result = [ { 'text': '{}({})'.format(c.symbol, c.name), 'value': c.symbol } # '{}-{}'.format(c.symbol, c.name)} for c in Collection.query(cond, { 'name': 1, 'symbol': 1 }) ] if exclude: result = [r for r in result if exclude not in r['text']] return jsonify(result=result)
def parse_save(): exchange = request.form.get('exchange') status = request.form.get('status', '申购中') from_url = request.form.get('from_url') type_ = request.form.get('type') result = json.loads(request.form.get('result', [])) if not exchange: return jsonify(status=500, reason="字段不全") from_url = html.unescape(from_url) if type_ == 'offer': for coll in result: coll['exchange'] = exchange coll['status'] = status coll['from_url'] = from_url coll['offer_quantity'] = int(coll['offer_quantity']) coll['offer_price'] = float(coll['offer_price']) coll['offers_at'] = datetime.strptime(coll['offers_at'], '%Y%m%d') coll['offer_cash_ratio'] = int(coll['offer_cash_ratio'].replace( '%', '')) / 100. Collection(coll).upsert() Announcement.update_one({'_id': from_url}, {'$set': {'parsed': True}}) elif type_ == 'result': for coll in result: coll['exchange'] = exchange coll['status'] = status coll['from_url'] = from_url if coll.get('invest_cash'): coll['invest_cash'] = float(coll['invest_cash']) else: del coll['invest_cash'] if coll.get('invest_cash_return_ratio'): coll['invest_cash_return_ratio'] = float( coll['invest_cash_return_ratio'].replace('%', '')) / 100. else: del coll['invest_cash_return_ratio'] Collection(coll).upsert() Announcement.update_one({'_id': from_url}, {'$set': {'parsed': True}}) return jsonify(status=200)
def realtime(site): conf = get_conf(site) exchange = conf['abbr'] url = conf['quote']['realtime']['url'] type_ = conf['quote']['realtime']['type'] today = datetime.utcnow().replace( minute=0, second=0, microsecond=0) + timedelta(hours=8) if not url: log.warning('{}尚未配置实时行情url'.format(exchange)) return if today.hour < 9 or today.hour > 22: log.warning('不在9点到22点之间, 不做解析') return today = today.replace(hour=0) text = session.get(url, timeout=(3, 7)).text quotes = parse_quotes(type_, text) saved = 0 for q in quotes: Collection.update_one({'exchange': exchange, 'symbol': q['symbol'].strip()}, {'$set': {'name': q['name']}}, upsert=True) q['exchange'] = exchange q['quote_type'] = '1d' q['quote_at'] = today if q['open_'] in ['—', '-', None, '']: continue else: # 找到上一个交易日的数据, 如果和lclose不符则舍弃 # 需要保证数据每天更新/不足时需要把日线补足才能正常显示 lq = Quote.query_one({'exchange': exchange, 'symbol': q['symbol'].strip(), 'quote_type': '1d', 'quote_at': {'$lt': today}}, sort=[('quote_at', -1)]) if not lq or abs(lq.close - q['lclose']) < 0.01: Quote(q).upsert() saved += 1 log.info('{} 导入 {}/{} 条实时交易记录'.format(exchange, saved, len(quotes)))
def history_sysframe(exchange, url, force): for c in Collection.query({'exchange': exchange}): try: if not force and history_exists(c): continue # 拿到数据文件 theurl = ('{}/hqApplet/data/day/00{}.day.zip').format( url, c.symbol) log.info('fetching exchange {} url {}'.format(exchange, theurl)) r = session.get( theurl, timeout=(5, 10), verify=False) if r.status_code != 200: log.warning('{}_{}下载失败, 错误码: {}' ''.format(exchange, c.symbol, r.status_code)) continue content = gzip.decompress(r.content) # 解析之 num_rows = struct.unpack('>i', content[0:4])[0] kline_days = [] for i in range(num_rows): raw_row = content[4 + 40 * i: 4 + 40 * i + 40] row = struct.unpack('>i5f2ifi', raw_row) t = row[0] date = datetime(year=int(str(t)[0:2]) + 1997, month=int(str(t)[2:4]), day=int(str(t)[4:6]), minute=int(str(t)[6:8] or 0), second=int(str(t)[8:10] or 0)) # row[6]总是0, 不知道是啥 q = { 'exchange': exchange, 'symbol': c.symbol, 'quote_type': '1d', 'quote_at': date, 'open_': row[1], 'high': row[2], 'low': row[3], 'close': row[4], 'mean': row[5], 'volume': row[7], 'amount': row[8], 'quantity': row[9], } if kline_days: q['lclose'] = kline_days[-1]['close'] save_quotes(q, c, first_quote=False) else: save_quotes(q, c, first_quote=True) kline_days.append(q) except: log.exception('{}:{} 抓取失败'.format(c.exchange, c.symbol))
def history_sysframe(exchange, url, force): for c in Collection.query({'exchange': exchange}): try: if not force and history_exists(c): continue # 拿到数据文件 theurl = ('{}/hqApplet/data/day/00{}.day.zip').format( url, c.symbol) log.info('fetching exchange {} url {}'.format(exchange, theurl)) r = session.get(theurl, timeout=(5, 10), verify=False) if r.status_code != 200: log.warning('{}_{}下载失败, 错误码: {}' ''.format(exchange, c.symbol, r.status_code)) continue content = gzip.decompress(r.content) # 解析之 num_rows = struct.unpack('>i', content[0:4])[0] kline_days = [] for i in range(num_rows): raw_row = content[4 + 40 * i:4 + 40 * i + 40] row = struct.unpack('>i5f2ifi', raw_row) t = row[0] date = datetime(year=int(str(t)[0:2]) + 1997, month=int(str(t)[2:4]), day=int(str(t)[4:6]), minute=int(str(t)[6:8] or 0), second=int(str(t)[8:10] or 0)) # row[6]总是0, 不知道是啥 q = { 'exchange': exchange, 'symbol': c.symbol, 'quote_type': '1d', 'quote_at': date, 'open_': row[1], 'high': row[2], 'low': row[3], 'close': row[4], 'mean': row[5], 'volume': row[7], 'amount': row[8], 'quantity': row[9], } if kline_days: q['lclose'] = kline_days[-1]['close'] save_quotes(q, c, first_quote=False) else: save_quotes(q, c, first_quote=True) kline_days.append(q) except: log.exception('{}:{} 抓取失败'.format(c.exchange, c.symbol))
def analysis(): nav = 'analysis' exchange = request.args.get('exchange') exs = sorted(list(Exchange.query()), key=lambda x: x.abbr) exchanges = [e.abbr for e in exs] ratings = [ex.rating for ex in exs] if not exchange: exchange = exchanges[0] ex = None for ex in exs: if ex.abbr == exchange: break # invest history ih_dates = [] ih_values_self = [] ih_values_all = [] for h in ex.invest_cash_history: ih_dates.append(h['date'].strftime('%Y-%m-%d')) ih_values_self.append(h['invest_cash'] / 1e8) ih_values_all.append(h['total_cash'] / 1e8) # increase history inc_days = [] inc_series = [] symbols = [] for symbol, values in ex.increase_history.items(): if len(values) > len(inc_days): inc_days = list(range(1, len(values))) inc_series.append({ 'name': symbol, 'type': 'line', 'data': [v * 100 for v in values], }) symbols.append(symbol) # predict conf = get_conf(ex.abbr) today = datetime.utcnow() + timedelta(hours=8) today = today.replace(hour=0, minute=0, second=0, microsecond=0) before = today - timedelta(days=conf['cashout']) cashout_at = today + timedelta(days=conf['cashout']) colls = list( Collection.query({ 'exchange': ex.abbr, 'offers_at': { '$gte': before } })) locals()['zip'] = zip return render_template('frontend/analysis.html', **locals())
def collection(): nav = 'collection' search = request.args.get('search', '') exchange = request.args.get('exchange', '') page = int(request.args.get('page', 1) or 1) limit = 25 skip = limit * (page - 1) cond = {} if exchange: cond['exchange'] = exchange total = Collection.count(cond) pagination = Pagination(page, limit, total) collections = list( Collection.query(cond, sort=[('offers_at', -1)], skip=skip, limit=limit)) for c in collections: lp = Quote.latest_price(c.exchange, c.symbol) if lp and c.offer_price: c.total_increase = lp / c.offer_price - 1 return render_template('frontend/collection.html', **locals())
def load_symbols(): exchange = request.args.get('exchange', '') query = request.args.get('query', '') exclude = request.args.get('exclude', '') cond = {'$or': [{'name': {'$regex': query}}, {'symbol': {'$regex': query}}]} if exchange: cond['exchange'] = exchange result = [ {'text': '{}({})'.format(c.symbol, c.name), 'value': c.symbol} # '{}-{}'.format(c.symbol, c.name)} for c in Collection.query( cond, {'name': 1, 'symbol': 1})] if exclude: result = [r for r in result if exclude not in r['text']] return jsonify(result=result)
def analysis(): nav = 'analysis' exchange = request.args.get('exchange') exs = sorted(list(Exchange.query()), key=lambda x: x.abbr) exchanges = [e.abbr for e in exs] ratings = [ex.rating for ex in exs] if not exchange: exchange = exchanges[0] ex = None for ex in exs: if ex.abbr == exchange: break # invest history ih_dates = [] ih_values_self = [] ih_values_all = [] for h in ex.invest_cash_history: ih_dates.append(h['date'].strftime('%Y-%m-%d')) ih_values_self.append(h['invest_cash'] / 1e8) ih_values_all.append(h['total_cash'] / 1e8) # increase history inc_days = [] inc_series = [] symbols = [] for symbol, values in ex.increase_history.items(): if len(values) > len(inc_days): inc_days = list(range(1, len(values))) inc_series.append({ 'name': symbol, 'type': 'line', 'data': [v * 100 for v in values], }) symbols.append(symbol) # predict conf = get_conf(ex.abbr) today = datetime.utcnow() + timedelta(hours=8) today = today.replace(hour=0, minute=0, second=0, microsecond=0) before = today - timedelta(days=conf['cashout']) cashout_at = today + timedelta(days=conf['cashout']) colls = list(Collection.query({'exchange': ex.abbr, 'offers_at': {'$gte': before}})) locals()['zip'] = zip return render_template('frontend/analysis.html', **locals())
def parse(): nav = "parse" url = request.args.get("url") num_parsed = Announcement.count({"parsed": True, "type_": {"$in": ["offer", "result"]}}) num_total = Announcement.count({"type_": {"$in": ["offer", "result"]}}) if url: announcement = Announcement.query_one({"url": url}) colls = list(Collection.query({"from_url": url})) for coll in colls: if coll.offers_at: coll.offers_at2 = coll.offers_at.strftime("%Y%m%d") if coll.offer_cash_ratio: coll.offer_cash_ratio = "{:2.0f}%".format(coll.offer_cash_ratio * 100) if coll.offer_price: coll.offer_price = str(coll.offer_price) if coll.offer_price.endswith(".0"): coll.offer_price = coll.offer_price[:-2] all_done = num_parsed == num_total return render_template("admin/parse.html", **locals())
def parse(site): rabbrs = {v: k for k, v in ABBRS.items()} abbr = rabbrs[site] parser = importlib.__import__('ybk.parsers.{}'.format(site), fromlist=['Parser']).Parser() log.info('解析交易所 {}'.format(abbr)) num_parsed = 0 num_failed = 0 for a in Announcement.query({'exchange': abbr, 'parsed': {'$ne': True}}): log.info('parsing {}'.format(a.url)) try: for c in parser.parse(a.type_, a.html): c['from_url'] = a.url Collection(c).upsert() a.update({'$set': {'parsed': True}}) num_parsed += 1 except Exception as e: num_failed += 1 if not isinstance(e, NotImplementedError): log.exception('解析错误') continue log.info('解析完毕, {}个成功, {}个失败'.format(num_parsed, num_failed))
def history_winner(exchange, url, force): assert url.startswith('tcp://') host, port = url[6:].split(':') port = int(port) s = socket.socket() s.connect((host, port)) def get_day_data(symbol): bsymbol = symbol.encode('utf-8') bizdata = b''.join([ b'\xfe\x8f\x00\x00', b'\x00' * 12, b'\x01\x00', b',\x00', b'\x02\x04', b'\x00', b'\x00', b'\x00\x00\x00\x00', b'\x013' + bsymbol, b'\x03\x00', b'\x00\x00', b'\x00\x00\x00\x00\x00\x00\x00\x00', b'Z\x00\x10\x00\x013' + bsymbol, b'\x00\x00\x00\x00', b'\x00', b'\x00', ]) bdata = b''.join([ b'\x95\x00\x00\x95', b'11=524\x00', b'13=8\x00', b'5=36862\x00', b'4=109\x00', b'1=66\x008=', bizdata, b'\x00', b'1=40\x0069=markid=60ba7308cab942ee961536a74ec7c5f9\x00\x00', ]) s.sendall(bdata) batch = 8192 result = [] while True: result.append(s.recv(batch)) if len(result[-1]) != batch: break return b''.join(result) def parse_day_data(data): size = struct.unpack('>i', b'\x00' + data[1:4])[0] assert size + 4 == len(data) m = re.compile(b'1=(\d+)\x008=').search(data) if m: bizsize = int(m.group(1)) start = m.span()[1] try: bbiz = zlib.decompress(data[start:start + bizsize]) except: bbiz = data[start:start + bizsize] assert bbiz[:4] == b'\xfe\x8f\x00\x00' num_packs = struct.unpack('<H', bbiz[16:18])[0] sizes = [ struct.unpack('<H', bbiz[18 + 2 * i:20 + 2 * i])[0] for i in range(num_packs) ] for i in range(num_packs): start = 18 + 2 * num_packs + 0 if i == 0 else sizes[i - 1] if bbiz[start:start + 2] == b'\x02\x04': # K线数据 symbol = bbiz[start + 10:start + 16].decode('utf-8') count = struct.unpack('<I', bbiz[start + 16:start + 20])[0] kline_days = [] for i in range(count): begin = start + 20 + i * 32 row = struct.unpack('<Iiiiiiii', bbiz[begin:begin + 32]) q = { 'exchange': exchange, 'symbol': symbol, 'quote_type': '1d', 'quote_at': datetime.strptime(str(row[0]), "%Y%m%d"), 'open_': row[1] / 100, 'high': row[2] / 100, 'low': row[3] / 100, 'close': row[4] / 100, 'amount': row[5] / 1., 'volume': row[6], } if kline_days: q['lclose'] = kline_days[-1]['close'] save_quotes(q, c, first_quote=False) else: save_quotes(q, c, first_quote=True) kline_days.append(q) for c in Collection.query({'exchange': exchange}): try: if not force and history_exists(c): continue if '$' not in c.symbol: log.info('feching {}_{} on {}'.format(c.exchange, c.symbol, url)) data = get_day_data(c.symbol) parse_day_data(data) except: log.exception('{}_{}获取失败'.format(exchange, c.symbol))
def collection_list(): exchange = request.args.get('exchange', '') search = request.args.get('search', '') sort = request.args.get('sort', 'offers_at') order = request.args.get('order', 'desc') limit = int(request.args.get('limit', 25)) offset = int(request.args.get('offset', 0)) if sort in ['offers_at', 'exchange', 'name', 'symbol', 'offer_price', 'offer_quantity']: dbsort = [(sort, 1 if order == 'asc' else -1)] else: dbsort = None cond = {} if exchange: cond['exchange'] = exchange if search: cond['$or'] = [ {'exchange': {'$regex': search}}, {'name': {'$regex': search}}, {'symbol': {'$regex': search}}, ] total = Collection.count(cond) qs = Collection.find(cond) if dbsort: qs = [Collection(c) for c in qs.sort(dbsort).skip(offset).limit(limit)] rows = [{ 'offers_at': c.offers_at, 'exchange': c.exchange, 'name': c.name, 'symbol': c.symbol, 'offer_price': c.offer_price, 'offer_quantity': c.offer_quantity, 'offer_cash_ratio': c.offer_cash_ratio, 'offer_cash': c.offer_cash, 'result_ratio_cash': c.result_ratio_cash, } for c in qs] for d in rows: d['total_increase'] = None lp = Quote.latest_price(d['exchange'], d['symbol']) if lp and d['offer_price']: d['total_increase'] = lp / d['offer_price'] - 1 if not dbsort: rows = sorted(rows, key=lambda x: x.get(sort) or 0, reverse=order == 'desc') rows = rows[offset:offset + limit] for d in rows: d['offers_at'] = d['offers_at'].strftime( '%Y-%m-%d') if d['offers_at'] else None if d['offer_price']: d['offer_price'] = '{:.2f}'.format(d['offer_price']) if d['offer_cash_ratio']: d['offer_cash_ratio'] = '{:.0f}%'.format( d['offer_cash_ratio'] * 100) if d['offer_cash']: d['offer_cash'] = '{:.1f}'.format(d['offer_cash']) if d['result_ratio_cash']: d['result_ratio_cash'] = '{:.3f}%'.format( d['result_ratio_cash'] * 100) if d['total_increase']: d['total_increase'] = '{:.1f}%'.format( 100 * (d['total_increase'])) return jsonify(total=total, rows=rows)
def history_winner(exchange, url, force): assert url.startswith('tcp://') host, port = url[6:].split(':') port = int(port) s = socket.socket() s.connect((host, port)) def get_day_data(symbol): bsymbol = symbol.encode('utf-8') bizdata = b''.join([ b'\xfe\x8f\x00\x00', b'\x00' * 12, b'\x01\x00', b',\x00', b'\x02\x04', b'\x00', b'\x00', b'\x00\x00\x00\x00', b'\x013' + bsymbol, b'\x03\x00', b'\x00\x00', b'\x00\x00\x00\x00\x00\x00\x00\x00', b'Z\x00\x10\x00\x013' + bsymbol, b'\x00\x00\x00\x00', b'\x00', b'\x00', ]) bdata = b''.join([ b'\x95\x00\x00\x95', b'11=524\x00', b'13=8\x00', b'5=36862\x00', b'4=109\x00', b'1=66\x008=', bizdata, b'\x00', b'1=40\x0069=markid=60ba7308cab942ee961536a74ec7c5f9\x00\x00', ]) s.sendall(bdata) batch = 8192 result = [] while True: result.append(s.recv(batch)) if len(result[-1]) != batch: break return b''.join(result) def parse_day_data(data): size = struct.unpack('>i', b'\x00' + data[1:4])[0] assert size + 4 == len(data) m = re.compile(b'1=(\d+)\x008=').search(data) if m: bizsize = int(m.group(1)) start = m.span()[1] try: bbiz = zlib.decompress(data[start: start + bizsize]) except: bbiz = data[start: start + bizsize] assert bbiz[:4] == b'\xfe\x8f\x00\x00' num_packs = struct.unpack('<H', bbiz[16:18])[0] sizes = [struct.unpack('<H', bbiz[18 + 2 * i: 20 + 2 * i])[0] for i in range(num_packs)] for i in range(num_packs): start = 18 + 2 * num_packs + 0 if i == 0 else sizes[i - 1] if bbiz[start:start + 2] == b'\x02\x04': # K线数据 symbol = bbiz[start + 10: start + 16].decode('utf-8') count = struct.unpack( '<I', bbiz[start + 16: start + 20])[0] kline_days = [] for i in range(count): begin = start + 20 + i * 32 row = struct.unpack('<Iiiiiiii', bbiz[begin: begin + 32]) q = { 'exchange': exchange, 'symbol': symbol, 'quote_type': '1d', 'quote_at': datetime.strptime(str(row[0]), "%Y%m%d"), 'open_': row[1] / 100, 'high': row[2] / 100, 'low': row[3] / 100, 'close': row[4] / 100, 'amount': row[5] / 1., 'volume': row[6], } if kline_days: q['lclose'] = kline_days[-1]['close'] save_quotes(q, c, first_quote=False) else: save_quotes(q, c, first_quote=True) kline_days.append(q) for c in Collection.query({'exchange': exchange}): try: if not force and history_exists(c): continue if '$' not in c.symbol: log.info('feching {}_{} on {}'.format(c.exchange, c.symbol, url)) data = get_day_data(c.symbol) parse_day_data(data) except: log.exception('{}_{}获取失败'.format(exchange, c.symbol))
def update_trade_account(trade_account): ta = trade_account log.info('更新账号{}的信息'.format(ta._id)) if not ta.login_password: ta.verified = False ta.verify_message = '没有密码不能登录' else: try: t = Trader(ta.exchange, ta.login_name, ta.login_password) except KeyError: ta.verified = False ta.verify_message = '该交易所协议未破解' except Exception as e: ta.verified = False ta.verify_message = str(e) else: if not t.is_logged_in: ta.verified = False ta.verify_message = t.last_error else: ta.verified = True # update money ta.money = t.money() # update position position = t.position() if position is not None: for p in position: p['name'] = Collection.get_name( ta.exchange, p['symbol']) or '' ta.position = position # update orders orders = t.orders() aggorders = {} for o in orders: o['name'] = Collection.get_name( ta.exchange, o['symbol']) or '' st = (o['symbol'], o['type_']) if st not in aggorders: aggorders[st] = o else: # 把成交汇总一下 oo = aggorders[st] if oo['quantity'] > 0: amount = oo['price'] * oo['quantity'] + \ o['price'] * o['quantity'] oo['quantity'] += o['quantity'] oo['price'] = amount / oo['quantity'] orders = aggorders.values() ta.orders = orders # update order_status order_status = t.order_status() for o in order_status: o['name'] = Collection.get_name( ta.exchange, o['symbol']) or '' ta.order_status = order_status ta.upsert() user = User.query_one({'_id': ta.user}) accounting(user)
def trade_quote_history(): """ K线数据 """ exchange = request.args.get('exchange', '') symbol = request.args.get('symbol', '') c = Collection.query_one({'exchange': exchange, 'symbol': symbol}) period = request.args.get('period', '1d') # chart1 qs = list(Quote.cached(3600).query({'exchange': exchange, 'symbol': symbol, 'quote_type': period})) name = '{}({}_{})'.format(Collection.get_name(exchange, symbol), exchange, symbol) xdata = [q.quote_at.strftime('%Y/%m/%d') for q in qs] sdata = [ (q.open_, q.close, q.low, q.high,) for q in qs] adata = [int(q.amount / 10000) for q in qs] num_prefix = 100 - len(xdata) if num_prefix > 0: xdata = xdata + [''] * num_prefix sdata = sdata + [()] * num_prefix adata = adata + [0] * num_prefix option1 = { 'title': { 'text': name }, 'xAxis': [{ 'type': 'category', 'boundaryGap': True, 'axisTick': {'onGap': False}, 'splitLine': {'show': False}, 'data': xdata, }], 'legend': { 'data': [name, '成交额(万)'] }, 'dataZoom': { 'show': False, 'realtime': True, 'start': (len(adata) - 100) * 100 // len(adata), 'end': 100 }, 'series': [ {'name': '{}({}_{})'.format(c.name, c.exchange, c.symbol), 'type': 'k', 'data': sdata, }, {'name': '成交额(万)', 'type': 'bar', 'symbol': 'none', 'data': [], }, ], } # chart2 option2 = { 'title': { 'text': '', }, 'toolbox': { 'y': -30, 'show': True, 'feature': { 'mark': {'show': True}, 'dataZoom': {'show': True}, 'dataView': {'show': True, 'readOnly': False}, 'magicType': {'show': True, 'type': ['line', 'bar']}, 'restore': {'show': True}, 'saveAsImage': {'show': True} } }, 'tooltip': { 'trigger': 'axis', 'showDelay': 0, }, 'legend': { 'y': -30, 'data': ['成交额(万)'], }, 'xAxis': [ { 'type': 'category', 'position': 'top', 'boundaryGap': True, 'axisLabel': {'show': False}, 'axisTick': {'onGap': False}, 'splitLine': {'show': False}, 'data': xdata, } ], 'yAxis': { 'splitNumber': 3, }, 'series': [ { 'name': '成交额(万)', 'type': 'bar', 'symbol': 'none', 'data': adata, } ], 'grid': { 'x': 80, 'y': 5, 'x2': 20, 'y2': 40 }, 'dataZoom': { 'show': True, 'realtime': True, 'start': (len(adata) - 100) * 100 // len(adata), 'end': 100 }, } return jsonify(status=200, option1=option1, option2=option2)
def trade_quote_realtime(): """ 实时数据/列表 """ # 全部 -> all # 指数 -> index # 持仓 -> position # 自选 -> diy category = request.args.get('category', '').strip() search = request.args.get('search', '').strip() sort = request.args.get('sort', '').strip() order = request.args.get('order', 'asc').strip() today = Quote.cached(3600).query_one({ 'quote_type': '1d' }, sort=[('quote_at', -1)]).quote_at cond = {'quote_type': '1d', 'quote_at': today} colls = set() symbols = [] if search: pairs = Collection.search(search) symbols = [p[1] for p in pairs] colls = set(pairs) cond['symbol'] = {'$in': symbols} elif category == 'all': pass elif category == 'index': cs = list(Collection.cached(3600).query({'name': {'$regex': '指数$'}})) colls = set((c.exchange, c.symbol) for c in cs) symbols = [c.symbol for c in cs] cond['symbol'] = {'$in': symbols} elif category == 'position': position = Position.user_position(current_user._id) colls = set((p['exchange'], p['symbol']) for p in position) symbols = [p['symbol'] for p in position] cond['symbol'] = {'$in': symbols} elif category == 'diy': raise NotImplementedError qs = [ q for q in Quote.query(cond) if (not colls) or ((q.exchange, q.symbol) in colls) ] qs = [{ 'open_': q.open_, 'high': q.high, 'low': q.low, 'close': q.close, 'lclose': q.lclose, 'volume': q.volume, 'amount': q.amount, 'increase': 0 if not q.lclose else q.close / q.lclose - 1, 'exchange': q.exchange, 'symbol': q.symbol, 'name': Collection.get_name(q.exchange, q.symbol), } for q in qs] # sort if sort: qs = sorted(qs, key=lambda x: x[sort], reverse=order == 'desc') # format for q in qs: if q['lclose']: q['lclose'] = '{:.2f}'.format(q['lclose']) q['open_'] = '{:.2f}'.format(q['open_']) q['high'] = '{:.2f}'.format(q['high']) q['low'] = '{:.2f}'.format(q['low']) q['close'] = '{:.2f}'.format(q['close']) q['amount'] = '{:.1f}万'.format(q['amount'] / 10000) q['increase'] = '{:.1f}%'.format(q['increase'] * 100) # add no result symbols exist_pairs = set((q['exchange'], q['symbol']) for q in qs) for exchange, symbol in (colls - exist_pairs): qs.append({ 'open_': '-', 'high': '-', 'low': '-', 'close': '-', 'lclose': '-', 'volume': '-', 'amount': '-', 'increase': '-', 'exchange': exchange, 'symbol': symbol, 'name': Collection.get_name(exchange, symbol), }) return jsonify(status=200, total=len(qs), rows=qs)
def trade_quote_history(): """ K线数据 """ exchange = request.args.get('exchange', '') symbol = request.args.get('symbol', '') c = Collection.query_one({'exchange': exchange, 'symbol': symbol}) period = request.args.get('period', '1d') # chart1 qs = list( Quote.cached(3600).query({ 'exchange': exchange, 'symbol': symbol, 'quote_type': period })) name = '{}({}_{})'.format(Collection.get_name(exchange, symbol), exchange, symbol) xdata = [q.quote_at.strftime('%Y/%m/%d') for q in qs] sdata = [( q.open_, q.close, q.low, q.high, ) for q in qs] adata = [int(q.amount / 10000) for q in qs] num_prefix = 100 - len(xdata) if num_prefix > 0: xdata = xdata + [''] * num_prefix sdata = sdata + [()] * num_prefix adata = adata + [0] * num_prefix option1 = { 'title': { 'text': name }, 'xAxis': [{ 'type': 'category', 'boundaryGap': True, 'axisTick': { 'onGap': False }, 'splitLine': { 'show': False }, 'data': xdata, }], 'legend': { 'data': [name, '成交额(万)'] }, 'dataZoom': { 'show': False, 'realtime': True, 'start': (len(adata) - 100) * 100 // len(adata), 'end': 100 }, 'series': [ { 'name': '{}({}_{})'.format(c.name, c.exchange, c.symbol), 'type': 'k', 'data': sdata, }, { 'name': '成交额(万)', 'type': 'bar', 'symbol': 'none', 'data': [], }, ], } # chart2 option2 = { 'title': { 'text': '', }, 'toolbox': { 'y': -30, 'show': True, 'feature': { 'mark': { 'show': True }, 'dataZoom': { 'show': True }, 'dataView': { 'show': True, 'readOnly': False }, 'magicType': { 'show': True, 'type': ['line', 'bar'] }, 'restore': { 'show': True }, 'saveAsImage': { 'show': True } } }, 'tooltip': { 'trigger': 'axis', 'showDelay': 0, }, 'legend': { 'y': -30, 'data': ['成交额(万)'], }, 'xAxis': [{ 'type': 'category', 'position': 'top', 'boundaryGap': True, 'axisLabel': { 'show': False }, 'axisTick': { 'onGap': False }, 'splitLine': { 'show': False }, 'data': xdata, }], 'yAxis': { 'splitNumber': 3, }, 'series': [{ 'name': '成交额(万)', 'type': 'bar', 'symbol': 'none', 'data': adata, }], 'grid': { 'x': 80, 'y': 5, 'x2': 20, 'y2': 40 }, 'dataZoom': { 'show': True, 'realtime': True, 'start': (len(adata) - 100) * 100 // len(adata), 'end': 100 }, } return jsonify(status=200, option1=option1, option2=option2)
def update_trade_account(trade_account): ta = trade_account log.info('更新账号{}的信息'.format(ta._id)) if not ta.login_password: ta.verified = False ta.verify_message = '没有密码不能登录' else: try: t = Trader(ta.exchange, ta.login_name, ta.login_password) except KeyError: ta.verified = False ta.verify_message = '该交易所协议未破解' except Exception as e: ta.verified = False ta.verify_message = str(e) else: if not t.is_logged_in: ta.verified = False ta.verify_message = t.last_error else: ta.verified = True # update money ta.money = t.money() # update position position = t.position() if position is not None: for p in position: p['name'] = Collection.get_name( ta.exchange, p['symbol']) or '' ta.position = position # update orders orders = t.orders() aggorders = {} for o in orders: o['name'] = Collection.get_name(ta.exchange, o['symbol']) or '' st = (o['symbol'], o['type_']) if st not in aggorders: aggorders[st] = o else: # 把成交汇总一下 oo = aggorders[st] if oo['quantity'] > 0: amount = oo['price'] * oo['quantity'] + \ o['price'] * o['quantity'] oo['quantity'] += o['quantity'] oo['price'] = amount / oo['quantity'] orders = aggorders.values() ta.orders = orders # update order_status order_status = t.order_status() for o in order_status: o['name'] = Collection.get_name(ta.exchange, o['symbol']) or '' ta.order_status = order_status ta.upsert() user = User.query_one({'_id': ta.user}) accounting(user)
def calendar(): nav = 'calendar' starts_at = request.args.get('starts_at') ends_at = request.args.get('ends_at') if starts_at: starts_at = datetime.strptime(starts_at, '%Y%m%d') today = datetime.utcnow() + timedelta(hours=8) today = today.replace(hour=0, minute=0, second=0, microsecond=0) if not starts_at: starts_at = today - timedelta(days=3) ends_at = starts_at + timedelta(days=10) # 表头 heads = [] d = starts_at while d <= ends_at: heads.append( ('周' + '一二三四五六日'[d.weekday()], '{}/{}'.format(d.month, d.day))) d += timedelta(days=1) # 表身 exs = [] # 交易所所在行 rowdict = defaultdict(list) # 交易所 -> 每天有/没有 seen = set() ddict = {} for c in Collection.query( {'offers_at': { '$gte': starts_at, '$lte': ends_at }}, sort=[('offers_at', 1)]): if (c.exchange, c.offers_at) in seen: continue seen.add((c.exchange, c.offers_at)) if c.exchange not in exs: exs.append(c.exchange) d = ddict.get(c.exchange, starts_at) while d < c.cashout_at: if d >= c.offers_at and d < c.cashout_at: cs = list( Collection.query({ 'offers_at': c.offers_at, 'exchange': c.exchange })) ndays = (c.cashout_at - c.offers_at).days if c.offers_at + timedelta(days=ndays) > ends_at: ndays = (ends_at - c.offers_at).days + 1 rowdict[c.exchange].append({ 'colspan': ndays, 'exchange': c.exchange, 'count': len(cs), 'cs': cs, 'symbols': ','.join([c.symbol for c in cs]) }) ddict[c.exchange] = c.cashout_at break else: rowdict[c.exchange].append({'colspan': 1}) d += timedelta(days=1) banks = {} details = {} for ex in ddict: d = ddict[ex] while d <= ends_at: spans = sum(x['colspan'] for x in rowdict[ex]) if spans < 11: rowdict[ex].append({'colspan': 1}) d += timedelta(days=1) c = get_conf(ex) banks[ex] = c['opening']['bank'] details[ex] = {} for cell in rowdict[ex]: if 'cs' in cell: for c in cell['cs']: details[ex][c.symbol] = { 'name': c.name, 'price': c.offer_price, 'offer_cash': c.offer_cash or 0, 'expected_ratio': c.expected_result_cash_ratio or 0, 'expected_revenue': c.expected_annual_profit or 0, } if not exs: exs = ['无申购'] prev_starts_at = (starts_at - timedelta(days=10)).strftime('%Y%m%d') next_starts_at = (starts_at + timedelta(days=10)).strftime('%Y%m%d') thisdate = (datetime.utcnow() + timedelta(hours=8)) thisdate = '{}/{}'.format(thisdate.month, thisdate.day) return render_template('frontend/calendar.html', **locals())
def trade_quote_realtime(): """ 实时数据/列表 """ # 全部 -> all # 指数 -> index # 持仓 -> position # 自选 -> diy category = request.args.get('category', '').strip() search = request.args.get('search', '').strip() sort = request.args.get('sort', '').strip() order = request.args.get('order', 'asc').strip() today = Quote.cached(3600).query_one({'quote_type': '1d'}, sort=[('quote_at', -1)]).quote_at cond = {'quote_type': '1d', 'quote_at': today} colls = set() symbols = [] if search: pairs = Collection.search(search) symbols = [p[1] for p in pairs] colls = set(pairs) cond['symbol'] = {'$in': symbols} elif category == 'all': pass elif category == 'index': cs = list(Collection.cached(3600).query({'name': {'$regex': '指数$'}})) colls = set((c.exchange, c.symbol) for c in cs) symbols = [c.symbol for c in cs] cond['symbol'] = {'$in': symbols} elif category == 'position': position = Position.user_position(current_user._id) colls = set((p['exchange'], p['symbol']) for p in position) symbols = [p['symbol'] for p in position] cond['symbol'] = {'$in': symbols} elif category == 'diy': raise NotImplementedError qs = [q for q in Quote.query(cond) if (not colls) or ((q.exchange, q.symbol) in colls)] qs = [{ 'open_': q.open_, 'high': q.high, 'low': q.low, 'close': q.close, 'lclose': q.lclose, 'volume': q.volume, 'amount': q.amount, 'increase': 0 if not q.lclose else q.close / q.lclose - 1, 'exchange': q.exchange, 'symbol': q.symbol, 'name': Collection.get_name(q.exchange, q.symbol), } for q in qs] # sort if sort: qs = sorted(qs, key=lambda x: x[sort], reverse=order == 'desc') # format for q in qs: if q['lclose']: q['lclose'] = '{:.2f}'.format(q['lclose']) q['open_'] = '{:.2f}'.format(q['open_']) q['high'] = '{:.2f}'.format(q['high']) q['low'] = '{:.2f}'.format(q['low']) q['close'] = '{:.2f}'.format(q['close']) q['amount'] = '{:.1f}万'.format(q['amount'] / 10000) q['increase'] = '{:.1f}%'.format(q['increase'] * 100) # add no result symbols exist_pairs = set((q['exchange'], q['symbol']) for q in qs) for exchange, symbol in (colls - exist_pairs): qs.append({ 'open_': '-', 'high': '-', 'low': '-', 'close': '-', 'lclose': '-', 'volume': '-', 'amount': '-', 'increase': '-', 'exchange': exchange, 'symbol': symbol, 'name': Collection.get_name(exchange, symbol), }) return jsonify(status=200, total=len(qs), rows=qs)
def calendar(): nav = "calendar" starts_at = request.args.get("starts_at") ends_at = request.args.get("ends_at") if starts_at: starts_at = datetime.strptime(starts_at, "%Y%m%d") today = datetime.utcnow() + timedelta(hours=8) today = today.replace(hour=0, minute=0, second=0, microsecond=0) if not starts_at: starts_at = today - timedelta(days=3) ends_at = starts_at + timedelta(days=10) # 表头 heads = [] d = starts_at while d <= ends_at: heads.append(("周" + "一二三四五六日"[d.weekday()], "{}/{}".format(d.month, d.day))) d += timedelta(days=1) # 表身 exs = [] # 交易所所在行 rowdict = defaultdict(list) # 交易所 -> 每天有/没有 seen = set() ddict = {} for c in Collection.query({"offers_at": {"$gte": starts_at, "$lte": ends_at}}, sort=[("offers_at", 1)]): if (c.exchange, c.offers_at) in seen: continue seen.add((c.exchange, c.offers_at)) if c.exchange not in exs: exs.append(c.exchange) d = ddict.get(c.exchange, starts_at) while d < c.cashout_at: if d >= c.offers_at and d < c.cashout_at: cs = list(Collection.query({"offers_at": c.offers_at, "exchange": c.exchange})) ndays = (c.cashout_at - c.offers_at).days if c.offers_at + timedelta(days=ndays) > ends_at: ndays = (ends_at - c.offers_at).days + 1 rowdict[c.exchange].append( { "colspan": ndays, "exchange": c.exchange, "count": len(cs), "cs": cs, "symbols": ",".join([c.symbol for c in cs]), } ) ddict[c.exchange] = c.cashout_at break else: rowdict[c.exchange].append({"colspan": 1}) d += timedelta(days=1) banks = {} details = {} for ex in ddict: d = ddict[ex] while d <= ends_at: spans = sum(x["colspan"] for x in rowdict[ex]) if spans < 11: rowdict[ex].append({"colspan": 1}) d += timedelta(days=1) c = get_conf(ex) banks[ex] = c["opening"]["bank"] details[ex] = {} for cell in rowdict[ex]: if "cs" in cell: for c in cell["cs"]: details[ex][c.symbol] = { "name": c.name, "price": c.offer_price, "offer_cash": c.offer_cash or 0, "expected_ratio": c.expected_result_cash_ratio or 0, "expected_revenue": c.expected_annual_profit or 0, } if not exs: exs = ["无申购"] prev_starts_at = (starts_at - timedelta(days=10)).strftime("%Y%m%d") next_starts_at = (starts_at + timedelta(days=10)).strftime("%Y%m%d") thisdate = datetime.utcnow() + timedelta(hours=8) thisdate = "{}/{}".format(thisdate.month, thisdate.day) return render_template("frontend/calendar.html", **locals())