예제 #1
0
 def finish(self):
     order_map = self.portfolio.get_portfolio(PortfolioManager.STATE_ALL)
     for sid, order_info in order_map.items():
         print format_log("order_info", order_info)
         records = self.portfolio.get_trade_records(sid)
         for record in records:
             print format_log("trade_record", record)
예제 #2
0
 def finish(self):
     order_map = self.portfolio.get_portfolio(PortfolioManager.STATE_ALL)
     for sid, order_info in order_map.items():
         print format_log("order_info", order_info)
         records = self.portfolio.get_trade_records(sid)
         for record in records:
             print format_log("trade_record", record)
예제 #3
0
def refresh_rise_factor(redis_config, cur_day, past_datamap, riseset_key):
    redis_conn = redis.StrictRedis(redis_config['host'], redis_config['port'])
    rise_set = redis_conn.smembers(riseset_key)
    #print rise_set
    rf_zset_key = "risefactor-" + str(cur_day)

    for sid in rise_set:
        if sid not in past_datamap:
            continue

        past_data_value = past_datamap[sid]
        past_data = json.loads(past_data_value)

        daily_data_value = redis_conn.get("daily-" + str(sid) + "-" + str(cur_day))
        #print daily_data_value
        if daily_data_value is None:
            continue

        stock_daily_data = json.loads(daily_data_value)
        if stock_daily_data['close_price'] < stock_daily_data['open_price'] or stock_daily_data['high_price'] == stock_daily_data['open_price']: 
            redis_conn.zrem(rf_zset_key, sid)
            continue

        vary_portion = (stock_daily_data['close_price'] - stock_daily_data['open_price']) / stock_daily_data['open_price'] * 100
        volume_ratio = stock_daily_data['predict_volume'] / past_data['avg_volume']
        high_portion = (stock_daily_data['close_price'] - stock_daily_data['open_price']) / (stock_daily_data['high_price'] - stock_daily_data['open_price']);
        rise_factor = round(vary_portion * volume_ratio * high_portion, 1)

        print format_log("refresh_rise_factor", {'sid': sid, 'vary_portion': vary_portion, 'volume_ratio': volume_ratio, 'high_portion': high_portion, 'rise_factor': rise_factor})
        if rise_factor >= 3.0:
            redis_conn.zadd(rf_zset_key, rise_factor, sid)
예제 #4
0
def get_stock_realtime(stock_info):
    sid = stock_info[0]
    scode = stock_info[1]
    #url = "http://web.ifzq.gtimg.cn/appstock/app/UsMinute/query?_var=min_data_usWUBA&code=usWUBA.N&r=" + str(random.random())
    url = "http://data.gtimg.cn/flashdata/hushen/minute/" + scode + ".js?maxage=10&" + str(random.random())
    #print scode, url

    try:
        response = urllib2.urlopen(url, timeout=1)
        content = response.read()
    except Exception as e:
        print "err=get_stock_realtime sid=" + str(sid) + " exception=" + str(e.reason)
        return None

    content = content.strip(' ;"\n').replace("\\n\\", "")
    lines = content.split("\n")
    #print lines

    date_info = lines[1].split(":")
    day = int("20" + date_info[1])

    hq_time = list()
    hq_price = list()
    hq_volume = list()

    for line in lines[2:]:
        fields = line.split(" ")
        # 直接用小时+分组成的时间, 格式为HHMM
        time = int(fields[0])

        item = dict()
        item['sid'] = sid
        item['code'] = scode[2:]
        item['day'] = day
        item['time'] = time
        item['price'] = float(fields[1])
        item['volume'] = int(fields[2])

        if item['volume'] <= 0:
            continue

        hq_time.append(time)
        hq_price.append(item['price'])
        hq_volume.append(item['volume'])

    # 表示当天所有的成交量都为0, 当天停牌
    if len(hq_volume) == 0:
        return

    hq_dict = {'time': hq_time, 'price': hq_price, 'volume': hq_volume}
    #print hq_dict

    if 'REDIS' in config_info :
        key = "realtime-" + str(sid) + "-" + str(day)
        conn = redis.StrictRedis(config_info['REDIS']['host'], int(config_info['REDIS']['port']))
        conn.set(key, json.dumps(hq_dict), 86400)

    print format_log("fetch_realtime", {'sid': sid, 'scode': scode, 'time': hq_time[len(hq_time) - 1], 'price': hq_price[len(hq_price) - 1]})
    return hq_dict
예제 #5
0
    def fill_order(self, fill_event):
        sid = self.code2sid(fill_event['code'])
        if sid == 0:
            self.logger.error("err=invalid_code code=%s", fill_event['code'])
            return 0

        order_info = self.order_stock[sid]
        if order_info['state'] == PortfolioManager.STATE_CLOSED:
            self.logger.info("%s", format_log("ignore_closed_order",
                                              order_info))
            return 0

        # TODO: 暂时不支持追加买入/卖出订单, 或者做多平仓后做空
        if order_info[
                'state'] == PortfolioManager.STATE_WAIT_OPEN and order_info[
                    'op'] == fill_event['op']:
            order_info['state'] = PortfolioManager.STATE_OPENED
            order_info['quantity'] = fill_event['quantity']
            order_info['open_price'] = fill_event['price']
            order_info['open_cost'] = fill_event['cost']
        elif order_info[
                'state'] == PortfolioManager.STATE_WAIT_CLOSE and order_info[
                    'op'] != fill_event['op']:
            order_info[
                'quantity'] = order_info['quantity'] - fill_event['quantity']
            order_info['close_price'] = fill_event['price']
            order_info['close_cost'] = fill_event['cost']
            if order_info['quantity'] <= 0:
                order_info['state'] = PortfolioManager.STATE_CLOSED
                order_info['profit'] = order_info['close_cost'] - order_info[
                    'open_cost']
                order_info['profit_portion'] = (
                    order_info['close_cost'] -
                    order_info['open_cost']) / order_info['open_cost'] * 100
                self.logger.info("%s", format_log("order_profit", order_info))

        trade_info = {
            'code': fill_event['code'],
            'op': fill_event['op'],
            'quantity': fill_event['quantity'],
            'order_id': fill_event['order_id'],
            'price': fill_event['price'],
            'cost': fill_event['cost']
        }
        trade_info['sid'] = sid
        trade_info['order_time'] = fill_event['time']

        if sid not in self.traded_map:
            self.traded_map[sid] = list()
        self.traded_map[sid].append(trade_info)

        self.update_stat("FILL", fill_event)
        self.logger.info("%s", format_log("fill_order", trade_info))

        self.update_holdings_from_fill(fill_event)
        return sid
예제 #6
0
    def close(self, sid, close_item):
        opened_map = self.get_portfolio(PortfolioManager.STATE_OPENED)
        if sid not in opened_map:
            self.logger.info("op=stock_not_open sid=%d", sid)
            return False
        elif close_item['op'] == opened_map[sid]['op']:
            self.logger.info(
                "op=close_same_op sid=%d code=%s open_op=%d count=%d close_op=%d ",
                sid, opened_map[sid]['code'], opened_map[sid]['op'],
                opened_map[sid]['count'], close_item['op'])
            return False
        elif not self.check_allow("CLOSE", close_item):
            self.logger.info("%s %s",
                             format_log("close_not_allowed", close_item),
                             json.dumps(self.port_statinfo))
            return False

        # TODO: 推送下单消息, 默认为市价卖出, 设置close_price时极为触及市价卖出, 后续支持order_type指定订单类型(市价/限价)
        order_event = {
            'sid': sid,
            'order_type': 'MKT',
            'day': close_item['day'],
            'code': close_item['code'],
            'op': close_item['op'],
            'quantity': opened_map[sid]['quantity']
        }
        if 'price' in close_item:
            order_event['price'] = close_item['price']

        # TODO: 设置订单类型为触及市价
        self.redis_conn.rpush("order-queue", json.dumps(order_event))

        # 更新订单状态
        opened_map[sid]['state'] = PortfolioManager.STATE_WAIT_CLOSE
        order_event['time'] = close_item['time']
        # 更新持仓管理的统计
        self.update_stat("CLOSE", close_item)
        self.logger.info("%s", format_log("close_order", order_event))

        # TODO: 目前手动构造成交订单
        sign = 1 if close_item['op'] == MinuteTrend.OP_SHORT else -1
        close_cost = sign * order_event['price'] * order_event['quantity']

        fill_event = {
            'code': order_event['code'],
            'op': order_event['op'],
            'quantity': opened_map[sid]['quantity'],
            'price': order_event['price'],
            'cost': close_cost,
            'time': close_item['time']
        }
        fill_event['order_id'] = random.randint(100, 500)
        self.fill_order(fill_event)

        return True
예제 #7
0
    def rise_factor(self, item):
        sid = item['sid']
        sid_str = str(sid)
        if sid_str not in self.datamap['past_data']:
            self.logger.warning("op=non_exist_pastdata sid=%d day=%d", sid,
                                item['day'])
            return

        past_data_value = self.datamap['past_data'][sid_str]
        if past_data_value is None:
            return
        past_data = json.loads(past_data_value)

        open_vary_portion = (item['open_price'] - item['last_close_price']
                             ) / item['last_close_price'] * 100
        day_vary_portion = (item['close_price'] -
                            item['open_price']) / item['open_price'] * 100
        volume_ratio = item['predict_volume'] / past_data['avg_volume']
        if abs(item['open_price'] - item['high_price']) < 0.01:
            high_portion = item['close_price'] / item['high_price']
        else:
            high_portion = (item['close_price'] - item['open_price']) / (
                item['high_price'] - item['open_price'])
        rise_factor = round(day_vary_portion * volume_ratio * high_portion, 1)

        daily_policy_key = "daily-policy-" + str(sid) + "-" + str(item['day'])
        stock_rise_map = {
            'open_vary_portion': open_vary_portion,
            'day_vary_portion': day_vary_portion,
            'volume_ratio': volume_ratio,
            'high_portion': high_portion,
            'rise_factor': rise_factor
        }
        self.redis_conn.hmset(daily_policy_key, stock_rise_map)

        stock_rise_map['sid'] = sid
        stock_rise_map['day'] = item['day']
        self.logger.info(format_log("daily_rise_factor", stock_rise_map))

        # 当前价格比昨日收盘价上涨1%以上 且 高于开盘价
        rf_zset_key = "rf-" + str(item['day'])
        if item['vary_portion'] >= 1.00 and item['close_price'] > item[
                'open_price'] and rise_factor >= 3.0:
            self.redis_conn.zadd(rf_zset_key, rise_factor, sid)
            self.logger.info(format_log("add_rise_factor", stock_rise_map))
        else:
            self.redis_conn.zrem(rf_zset_key, sid)

        # 把涨幅超过1% 或 涨跌幅在1%以内的股票加入拉取分笔交易的集合
        daily_ts_key = "tsset-" + str(item['day'])
        if volume_ratio >= 2.0 and (item['vary_portion'] > 1.00
                                    or abs(item['vary_portion']) <= 1.00):
            self.redis_conn.sadd(daily_ts_key, item['sid'])
        else:
            self.redis_conn.srem(daily_ts_key, [item['sid']])
예제 #8
0
    def parse_stock_daily(self, line):
        parts = line.split("=")
        #print line, parts
        content = parts[1].strip('"')
        #print content

        fields = content.split("~")
        #print fields
        if len(fields) < 44:
            line_str = safestr(line)
            self.logger.error(
                format_log("daily_lack_fields", {
                    'line': line_str,
                    'content': content
                }))
            return None

        # 当日停牌则不能存入
        open_price = float(fields[5])
        close_price = float(fields[3])
        if open_price == 0.0 or close_price == 0.0:
            return None

        item = dict()

        try:
            item['name'] = safestr(fields[1])
            item['code'] = stock_code = fields[2]
            item['sid'] = int(self.datamap['code2id'][stock_code])
            item['day'] = self.day
            item['last_close_price'] = float(fields[4])
            item['open_price'] = open_price
            item['high_price'] = float(fields[33])
            item['low_price'] = float(fields[34])
            item['close_price'] = close_price
            # 当前时刻, 格式为HHMMSS
            item['time'] = fields[30][8:]
            item['vary_price'] = float(fields[31])
            item['vary_portion'] = float(fields[32])
            # 成交量转化为手
            item['volume'] = int(fields[36])
            item['predict_volume'] = get_predict_volume(
                item['volume'], item['time'])
            # 成交额转化为万元
            item['amount'] = int(fields[37])
            item['exchange_portion'] = fields[38]
            item['pe'] = fields[39]
            item['swing'] = fields[43]
            item['out_capital'] = fields[44]
        except IndexError:
            self.logger.error(format_log("parse_daily", {'content': content}))
            return None

        return item
예제 #9
0
    def open(self, sid, open_item):
        wait_open_map = self.get_portfolio(PortfolioManager.STATE_WAIT_OPEN)
        if sid in wait_open_map:
            self.logger.info("op=stock_wait_open sid=%d code=%s", sid, wait_open_map[sid]['code'])
            return False

        # 暂时不允许已建仓的股票再建仓
        opened_map = self.get_portfolio(PortfolioManager.STATE_OPENED)
        if sid in opened_map and open_item['op'] == opened_map[sid]['op']:
            self.logger.info("%s", format_log("stock_order_opened", opened_map[sid]))
            return False

        # 建仓是否允许
        if not self.check_allow("OPEN", open_item):
            self.logger.info("%s %s", format_log("open_not_allowed", open_item), json.dumps(self.port_statinfo))
            return False

        # TODO: cash仅在成交后才会扣减, 实际通过IB下单时, 会出现成交前多只股票都可以下单
        rest_money = self.holdings['cash']
        quantity = self.cal_quantity(sid, open_item['op'], rest_money, open_item['open_price'])
        if quantity <= 0:
            self.logger.info("op=no_enough_money sid=%d code=%s op=%d rest_money=%d open_price=%.2f", 
                sid, open_item['code'], open_item['op'], rest_money, open_item['open_price'])
            return False

        # TODO: 推送下单消息, 设置建仓价格 + 止损价格, 设置下单类型为限价单
        order_info =  {'sid': sid, 'day': open_item['day'], 'code': open_item['code'], 'op': open_item['op'], 'quantity': quantity, 'stop_price': open_item['stop_price']}
        order_event = dict(order_info)
        order_event['order_type'] = "LMT"
        order_event['price'] = open_item['open_price']
        push_result = self.redis_conn.rpush("order-queue", json.dumps(order_event))

        # 更新剩余的现金
        sign = 1 if open_item['op'] == MinuteTrend.OP_LONG else -1
        cost = sign * quantity * open_item['open_price']

        order_event['time'] = open_item['time']
        self.logger.info("%s cost=%d", format_log("open_order", order_event), cost)

        order_info['open_price'] = open_item['open_price']
        order_info['state'] = self.STATE_WAIT_OPEN
        self.order_stock[sid] = order_info

        # 更新持仓管理的统计
        self.update_stat("OPEN", open_item)

        # TODO: 目前先手动构造fill_event来成交
        fill_event = {'code': order_event['code'], 'op': order_event['op'], 'quantity': quantity, 'price': order_event['price'], 'cost': cost, 'time': open_item['time']}
        fill_event['order_id'] = random.randint(100, 500)
        self.fill_order(fill_event)

        return True
예제 #10
0
def core(config_info, queue, location, day):
    redis_config = config_info['REDIS']
    conn = redis.StrictRedis(redis_config['host'], redis_config['port'])
    count = 0
    return_list = []

    while True:
        try:
            pop_data = conn.blpop(queue, 1)
            if pop_data is None:
                time.sleep(1)
                break

            data = pop_data[1]
            item = json.loads(data)
            #print item
            if item is None:
                continue

            if 'daily_item' in item and item['daily_item']['day'] != day:
                logging.getLogger("chance").error(
                    "err=ignore_expired_item item_day=%d day=%d",
                    item['daily_item']['day'], day)
                break

            return_info = chance_return(config_info, location, day, item)
            #print return_info
            return_list.append(return_info)
            logging.getLogger("chance").info(
                "%s", format_log("chance_return", return_info))
            '''
            count += 1    
            if count % 5 == 0:
                break            
            '''
        except Exception as e:
            logging.getLogger("chance").exception("err=pop_item")

    return_list.sort(key=lambda x: (x['close_portion'], x['fall_portion']),
                     reverse=True)
    for return_item in return_list[0:50]:
        logging.getLogger("chance").info("%s",
                                         format_log("top_return", return_item))

    return_pd = pd.DataFrame(return_list)
    #print return_pd
    filename = "./return_" + str(day) + "_" + str(location) + ".csv"
    return_pd.to_csv(filename, index=False)
예제 #11
0
    def core(self, item):
        scode = item
        url = "http://qt.gtimg.cn/r=" + str(random.random()) + "q=" + scode
        #print url

        try:
            response = urllib2.urlopen(url, timeout=1)
            content = response.read()
        except urllib2.HTTPError as e:
            self.logger.warning("err=get_stock_daily scode=%s code=%s", scode,
                                str(e.code))
            return
        except urllib2.URLError as e:
            self.logger.warning("err=get_stock_daily scode=%s reason=%s",
                                scode, str(e.reason))
            return

        if content:
            content = safestr(content.decode('gbk'))
            #self.logger.info("desc=daily_content content=%s", content)
            lines = content.strip("\r\n").split(";")

            for line in lines:
                if 0 == len(line):
                    continue

                daily_item = self.parse_stock_daily(line)
                if daily_item is None:
                    continue

                # 追加到redis队列中
                if self.conn:
                    self.conn.rpush("daily-queue", json.dumps(daily_item))
                self.logger.info(format_log("fetch_daily", daily_item))
예제 #12
0
    def parse_stock_daily(self, line):
        try:
            parts = line.split("=")
            #print line, parts
            content = parts[1].strip('"')
            #print content

            fields = content.split("~")
            #print fields
            if len(fields) < 44:
                line_str = safestr(line)
                self.logger.error(format_log("daily_lack_fields", {'line': line_str, 'content': content}))
                return None

            # 当日停牌则不能存入
            open_price = float(fields[5])
            close_price = float(fields[3])
            if open_price == 0.0 or close_price == 0.0:
                return None

            item = dict()

            try:
                item['name'] = safestr(fields[1])
                '''
                stock_code = fields[2]
                if self.location == 3: # 美股返回为usWUBA.N
                    code_parts = stock_code.split(".")
                    stock_code = code_parts[0]
                '''
                item['code'] = fields[2]
                item['sid'] = int(self.datamap['code2id'][item['code']])
                item['day'] = self.day
                item['last_close_price'] = float(fields[4])
                item['open_price'] = open_price
                item['high_price'] = float(fields[33])
                item['low_price'] = float(fields[34])
                item['close_price'] = close_price
                # 当前时刻, 格式为HHMMSS
                item['time'] = fields[30][8:]
                item['vary_price'] = float(fields[31])
                item['vary_portion'] = float(fields[32])
                # 成交量转化为手
                item['volume'] = int(fields[36])
                item['predict_volume'] = get_predict_volume(item['volume'], item['time'], self.location)
                # 成交额转化为万元
                item['amount'] = int(fields[37])
                item['exchange_portion'] = fields[38]
                item['pe'] = fields[39]
                item['swing'] = fields[43]
                item['out_capital'] = fields[44]
            except Exception:
                self.logger.exception("err=parse_daily_index content=%s", content)
                return None
        except Exception:
            self.logger.exception("err=parse_daily_content line=%s content=%s", line, content)
            return None

        return item
예제 #13
0
def refresh_rise_factor(redis_config, cur_day, past_datamap, riseset_key):
    redis_conn = redis.StrictRedis(redis_config['host'], redis_config['port'])
    rise_set = redis_conn.smembers(riseset_key)
    #print rise_set
    rf_zset_key = "risefactor-" + str(cur_day)

    for sid in rise_set:
        if sid not in past_datamap:
            continue

        past_data_value = past_datamap[sid]
        past_data = json.loads(past_data_value)

        daily_data_value = redis_conn.get("daily-" + str(sid) + "-" +
                                          str(cur_day))
        #print daily_data_value
        if daily_data_value is None:
            continue

        stock_daily_data = json.loads(daily_data_value)
        if stock_daily_data['close_price'] < stock_daily_data[
                'open_price'] or stock_daily_data[
                    'high_price'] == stock_daily_data['open_price']:
            redis_conn.zrem(rf_zset_key, sid)
            continue

        vary_portion = (stock_daily_data['close_price'] -
                        stock_daily_data['open_price']
                        ) / stock_daily_data['open_price'] * 100
        volume_ratio = stock_daily_data['predict_volume'] / past_data[
            'avg_volume']
        high_portion = (
            stock_daily_data['close_price'] - stock_daily_data['open_price']
        ) / (stock_daily_data['high_price'] - stock_daily_data['open_price'])
        rise_factor = round(vary_portion * volume_ratio * high_portion, 1)

        print format_log(
            "refresh_rise_factor", {
                'sid': sid,
                'vary_portion': vary_portion,
                'volume_ratio': volume_ratio,
                'high_portion': high_portion,
                'rise_factor': rise_factor
            })
        if rise_factor >= 3.0:
            redis_conn.zadd(rf_zset_key, rise_factor, sid)
예제 #14
0
def core(config_info, queue, location, day):  
    redis_config = config_info['REDIS']
    conn = redis.StrictRedis(redis_config['host'], redis_config['port'])      
    count = 0
    return_list = []
    
    while True:
        try:
            pop_data = conn.blpop(queue, 1)
            if pop_data is None:
                time.sleep(1)
                break

            data = pop_data[1]
            item = json.loads(data)
            #print item
            if item is None:
                continue

            if 'daily_item' in item and item['daily_item']['day'] != day:
                logging.getLogger("chance").error("err=ignore_expired_item item_day=%d day=%d", item['daily_item']['day'], day)
                break
            
            return_info = chance_return(config_info, location, day, item)
            #print return_info
            return_list.append(return_info)
            logging.getLogger("chance").info("%s", format_log("chance_return", return_info))

            '''
            count += 1    
            if count % 5 == 0:
                break            
            '''
        except Exception as e:
            logging.getLogger("chance").exception("err=pop_item")

    return_list.sort(key=lambda x : (x['close_portion'], x['fall_portion']), reverse = True)
    for return_item in return_list[0:50]:
        logging.getLogger("chance").info("%s", format_log("top_return", return_item))

    return_pd = pd.DataFrame(return_list)
    #print return_pd
    filename = "./return_" + str(day) + "_" + str(location) + ".csv"
    return_pd.to_csv(filename, index=False)
예제 #15
0
    def core(self, location, day, param_sid):
        stock_df = pd.read_sql_query(
            "select * from t_stock where location = " + str(location) +
            " and type = 1 and status = 'Y'",
            self.conn,
            index_col='id')
        #print stock_df.index, stock_df.columns
        sid_list = [param_sid] if param_sid > 0 else stock_df.index

        for sid in sid_list:
            dyn_info = self.update_dyn(day, sid)
            if dyn_info is None:
                print format_log("stock_paused", {
                    'sid': sid,
                    'location': location,
                    'day': day
                })
                continue

            sql = SqlUtil.create_insert_sql("t_stock_dyn", dyn_info)
            try:
                db_conn = SqlUtil.get_db(self.db_config)
                db_conn.query_sql(sql, True)
            except Exception as e:
                print "err=insert_dyn sid=" + str(sid) + " day=" + str(
                    day) + " ex=" + str(e)
                continue

            print format_log("add_stock_dyn", dyn_info)
        print format_log("finish_dyn", {'location': location, 'day': day})
예제 #16
0
    def fill_order(self, fill_event):
        sid = self.code2sid(fill_event['code'])
        if sid == 0:
            self.logger.error("err=invalid_code code=%s", fill_event['code'])
            return 0

        order_info = self.order_stock[sid]
        if order_info['state'] == PortfolioManager.STATE_CLOSED:
            self.logger.info("%s", format_log("ignore_closed_order", order_info))
            return 0

        # TODO: 暂时不支持追加买入/卖出订单, 或者做多平仓后做空
        if order_info['state'] == PortfolioManager.STATE_WAIT_OPEN and order_info['op'] == fill_event['op']:
            order_info['state'] = PortfolioManager.STATE_OPENED
            order_info['quantity'] = fill_event['quantity']
            order_info['open_price'] = fill_event['price']
            order_info['open_cost'] = fill_event['cost']
        elif order_info['state'] == PortfolioManager.STATE_WAIT_CLOSE and order_info['op'] != fill_event['op']:
            order_info['quantity'] = order_info['quantity'] - fill_event['quantity']
            order_info['close_price'] = fill_event['price']
            order_info['close_cost'] = fill_event['cost']
            if order_info['quantity'] <= 0:
                order_info['state'] = PortfolioManager.STATE_CLOSED
                order_info['profit'] = order_info['close_cost'] - order_info['open_cost']
                order_info['profit_portion'] = (order_info['close_cost'] - order_info['open_cost']) / order_info['open_cost'] * 100
                self.logger.info("%s", format_log("order_profit", order_info))

        trade_info = {'code': fill_event['code'], 'op': fill_event['op'], 'quantity': fill_event['quantity'], 'order_id': fill_event['order_id'], 'price': fill_event['price'], 'cost': fill_event['cost']}
        trade_info['sid'] = sid
        trade_info['order_time'] = fill_event['time']

        if sid not in self.traded_map:
            self.traded_map[sid] = list()
        self.traded_map[sid].append(trade_info)

        self.update_stat("FILL", fill_event)
        self.logger.info("%s", format_log("fill_order", trade_info))

        self.update_holdings_from_fill(fill_event)
        return sid
예제 #17
0
def get_stock_daily(stock_info):
    #scode = stock_info[1]
    scode = stock_info
    url = "http://qt.gtimg.cn/r=" + str(random.random()) + "q=" + scode
    #print scode, url

    try:
        response = requests.get(url, timeout=10)
        content = response.text
    except Exception as e:
        print "err=get_stock_daily scode=" + scode + " error_msg=" + str(e)
        return 

    if content:
        lines = content.strip("\n").split(";")
        conn = None

        if 'REDIS' in config_info :
            conn = redis.StrictRedis(config_info['REDIS']['host'], int(config_info['REDIS']['port']))

        for line in lines:
            if 0 == len(line):
                continue

            item = parse_stock_daily(line)
            if item is None:
                continue

            # 存到缓存中
            if conn:
                key = "daily-" + str(item['sid']) + "-" + str(day)
                conn.set(key, json.dumps(item), 86400)
                # 当日开盘上涨 且 当前价格高于昨日收盘价格
                if item['vary_price'] > 0.0 and  item['close_price'] > item['open_price']:
                    conn.sadd("daily-riseset-" + str(day), item['sid'])

            print format_log("fetch_daily", item)
예제 #18
0
    def realtime_trend(self, item):
        sid = int(item['sid'])
        day = item['day']

        daily_key = "daily-" + str(sid) + "-" + str(day)
        daily_cache_value = self.redis_conn.get(daily_key);
        if daily_cache_value is None:
            self.logger.error("err=fetch_daily sid=%d day=%d", sid, day)
            return

        daily_item = json.loads(daily_cache_value)	
        rt_key = "rt-" + str(sid) + "-" + str(day)
        item_count = self.redis_conn.llen(rt_key)
        if item_count < 3:
            return

        if item_count % 5 == 0:
            # 暂定每5分钟调用分析一次, 后续根据时间段调整
            item_list = self.redis_conn.lrange(rt_key, 0, -1)
            minute_items = []
            for item_json in item_list:
                minute_items.append(json.loads(item_json))

            now_time = minute_items[-1]['time']
            instance = MinuteTrend(sid)
            (trend_stage, trend_info) = instance.core(daily_item, minute_items)
            self.logger.debug("%s", format_log("minute_trend", trend_stage))
            self.logger.debug("%s", format_log("trend_parse", trend_info))

            trend_detail = self.refresh_trend(sid, day, minute_items, trend_info)
            if 'trend' in trend_detail:
                self.logger.info("%s sid=%d day=%d item_count=%d time=%d", format_log("trend_detail", trend_detail), sid, day, item_count, now_time)

            if trend_stage['chance'] and trend_stage['chance']['op'] != MinuteTrend.OP_WAIT:
                trend_stage['trend_detail'] = trend_detail
                self.redis_conn.rpush("chance-queue", json.dumps(trend_stage))
                self.logger.info("%s", format_log("realtime_chance", trend_stage))
예제 #19
0
    def open_position(self, location, day, cur_timenumber, sid, item):
        # 获取该股票所有的操作机会
        same_count = 1
        contray_count = 0

        key = "chance-" + str(sid) + "-" + str(day)
        # 该机会不一定是最新的1个
        chance_item_list = self.redis_conn.lrange(key, 0, -1)
        for chance_item_data in chance_item_list:
            chance_item = json.loads(chance_item_data)
            # 忽略自己
            if chance_item['time'] == item['time']:
                continue

            if chance_item['chance']['op'] == item['chance']['op']:
                same_count += 1
            else:
                contray_count += 1

        # 直接买入
        #print same_count, contray_count
        if contray_count == 0 or same_count > contray_count:
            order_event = {'sid': sid, 'day': day, 'code': item['code'], 'time': item['time']}
            stop_price = item['chance']['stop_price']

            if item['chance']['op'] == MinuteTrend.OP_LONG:
                open_price = item['chance']['price_range'][1]
                stop_price = min(stop_price, open_price * (1 - self.chance_config[location]['stop_portion'] / 100))
            else:
                open_price = item['chance']['price_range'][0]
                stop_price = max(stop_price, open_price * (1 + self.chance_config[location]['stop_portion'] / 100))

            # <=1000时建仓, 取区间的中间价格作为建仓价
            if item['time'] <= 1000:
                open_price = (item['chance']['price_range'][0] + item['chance']['price_range'][1]) / 2

            order_event['open_price'] = open_price
            order_event['stop_price'] = stop_price
            order_event['op'] = item['chance']['op']

            # 调用PortfioManager进行建仓
            open_result = self.portfolio.open(sid, order_event)
            self.logger.info("%s open_result=%s", format_log("open_position", order_event), str(open_result))

            # 建仓的机会push到单独的队列中
            self.redis_conn.rpush("order-chance", json.dumps(item))
            return True

        return False
예제 #20
0
    def close(self, sid, close_item):
        opened_map = self.get_portfolio(PortfolioManager.STATE_OPENED)
        if sid not in opened_map:
            self.logger.info("op=stock_not_open sid=%d", sid)
            return False
        elif close_item['op'] == opened_map[sid]['op']:
            self.logger.info("op=close_same_op sid=%d code=%s open_op=%d count=%d close_op=%d ", sid, opened_map[sid]['code'], opened_map[sid]['op'], opened_map[sid]['count'], close_item['op'])
            return False
        elif not self.check_allow("CLOSE", close_item):
            self.logger.info("%s %s", format_log("close_not_allowed", close_item), json.dumps(self.port_statinfo))
            return False

        # TODO: 推送下单消息, 默认为市价卖出, 设置close_price时极为触及市价卖出, 后续支持order_type指定订单类型(市价/限价)
        order_event = {'sid': sid, 'order_type': 'MKT', 'day': close_item['day'], 'code': close_item['code'], 'op': close_item['op'], 'quantity': opened_map[sid]['quantity']}
        if 'price' in close_item:
            order_event['price'] = close_item['price']

        # TODO: 设置订单类型为触及市价
        self.redis_conn.rpush("order-queue", json.dumps(order_event))

        # 更新订单状态
        opened_map[sid]['state'] = PortfolioManager.STATE_WAIT_CLOSE
        order_event['time'] = close_item['time']
        # 更新持仓管理的统计
        self.update_stat("CLOSE", close_item)
        self.logger.info("%s", format_log("close_order", order_event))

        # TODO: 目前手动构造成交订单
        sign = 1 if close_item['op'] == MinuteTrend.OP_SHORT else -1
        close_cost = sign * order_event['price'] * order_event['quantity']

        fill_event = {'code': order_event['code'], 'op': order_event['op'], 'quantity': opened_map[sid]['quantity'], 'price': order_event['price'], 'cost': close_cost, 'time': close_item['time']}
        fill_event['order_id'] = random.randint(100, 500)
        self.fill_order(fill_event)

        return True
예제 #21
0
    def filter(self, item):
        sid = item['sid']
        day = item['daily_item']['day']

        chance_info = item['chance']
        daily_item = item['daily_item']
        day_vary_portion = (daily_item['close_price'] - daily_item['open_price']) / daily_item['open_price'] * 100

        # 日内涨幅 >= 6%且操作时间在10:20之后, 不建议追高
        if chance_info['op'] == MinuteTrend.OP_LONG and item['time'] >= 1020 and abs(day_vary_portion) >= 6.00:
            return

        # TODO: 从redis中获取大盘趋势
        key = "chance-" + str(sid) + "-" + str(day)
        self.redis_conn.rpush(key, json.dumps(item))

        # 全局list, 倒序排列
        self.redis_conn.lpush("chance-" + str(day), json.dumps(item))
        self.logger.debug("%s", format_log("chance_item", item))

        '''
예제 #22
0
    def day_trend(self, item):
        trend = op = 0
        day_vary_portion = (item['close_price'] -
                            item['open_price']) / item['open_price'] * 100
        max_vary = item['high_price'] - item['close_price']
        min_vary = item['close_price'] - item['low_price']

        # 开盘即涨停
        if item['close_price'] == item[
                'open_price'] and item['vary_portion'] >= 9.6:
            trend = 3
        # 涨跌幅在1%以内, 认为是震荡
        elif abs(day_vary_portion) <= 1:
            trend = 2
            op = 2
        elif item['vary_price'] > 0.0:
            if max_vary == 0.0 or max_vary < min_vary:
                trend = 3
            else:
                trend = 1
        else:
            if min_vary == 0.0 or min_vary < max_vary:
                trend = 1
            else:
                trend = 3

        if trend == 1:
            op = 1
        elif trend == 3:
            op = 3

        daily_policy_key = "daily-policy-" + str(item['sid']) + "-" + str(
            item['day'])
        trend_info = {'trend': trend, 'op': op}
        self.redis_conn.hmset(daily_policy_key, trend_info)

        trend_info['sid'] = item['sid']
        trend_info['day'] = item['day']
        self.logger.debug("%s", format_log("daily_day_trend", trend_info))
예제 #23
0
def chance_return(config_info, location, day, item):    
    hqdata = get_hqdata(config_info['DB'], config_info['REDIS'], day, item['sid'])
    print hqdata['daily']
    
    chance_item = item['chance']
    trend_item = item['trend_item']    
    # 卖出时用平仓价格减卖出价格, 需要乘-1
    factor = 1 if 1 == chance_item['op'] else -1
    
    # 严格一点, 应该用time之后的high_price/low_price计算收益和回撤, 买入点以较大价格进入, 卖出以较低价格卖出    
    enter_price = chance_item['price_range'][1] if 1 == chance_item['op'] else chance_item['price_range'][0]
    exit_price = float(hqdata['daily']['close_price'])
    fall_price = float(hqdata['daily']['low_price'])  if 1 == chance_item['op'] else float(hqdata['daily']['high_price'])

    rise_portion = factor * (exit_price - enter_price) / enter_price * 100
    fall_portion = factor * (fall_price - enter_price) / enter_price * 100   
    
    return_info = {"sid": item['sid'], "code": item['code'], "time": item['time'], "op": chance_item['op'], "trend_length": trend_item['length'], "trend_portion": trend_item['vary_portion'], 
            "enter": enter_price, "exit": exit_price, "fall": fall_price, "close_portion": rise_portion, "fall_portion": fall_portion, "vary_portion": hqdata['daily']['vary_portion']}

    # TODO: 输出 dyn数据
    record_list = []
    try:
        sql = "select * from t_stock_dyn where sid = {sid} and day < {day} order by day desc limit 1".format(sid=item['sid'], day=day)
        #print sql
        db_conn = SqlUtil.get_db(config_info['DB'])
        record_list = db_conn.query_sql(sql)
    except Exception as e:
        print e
        logging.getLogger("chance").error("err=get_stock_dyn sid=%d code=%s location=%d day=%d", item['sid'], item['code'], location, day)
    else:
        #print record_list
        if len(record_list) == 1:
            stock_dyn = record_list[0]
            for key in ['ma5_swing', 'ma20_swing', 'ma5_vary_portion', 'ma20_vary_portion', 'ma5_exchange_portion', 'ma20_exchange_portion', 'volume_ratio']:
                return_info[key] = stock_dyn[key]

    logging.getLogger("chance").info("%s", format_log("chance_item", item))
    return return_info
예제 #24
0
    def core(self, item):
        scode = item
        cur_timestamp = int(time.time() * 1000)
        url = "http://hq.sinajs.cn/rn=" +  str(cur_timestamp) + "&list=" + scode
        print url

        try:
            response = urllib2.urlopen(url, timeout=5)
            content = response.read()
        except urllib2.HTTPError as e:
            self.logger.warning("err=get_stock_daily scode=%s code=%s", scode, str(e.code))
            return 
        except urllib2.URLError as e:
            self.logger.warning("err=get_stock_daily scode=%s reason=%s", scode, str(e.reason))
            return

        if content:
            content = safestr(content.decode('gbk'))
            self.logger.debug("desc=daily_content scode=%s content=%s", scode, content)
            lines = content.strip("\r\n").split(";")

            for line in lines:
                if 0 == len(line):
                    continue

                daily_item = self.parse_stock_daily(line)
                if daily_item is None:
                    continue

                # 追加到redis队列中
                json_item = json.dumps(daily_item)
                if self.conn:
                    self.conn.rpush("daily-queue", json_item)
                self.logger.info(format_log("fetch_daily", daily_item))
                #print format_log("fetch_daily", daily_item)

                # 设置dump则把数据dump到日志中, 暂定每5mindump一次, 可配置
                self.dump(int(daily_item['time'][2:4]), json_item, "daily")
예제 #25
0
    def filter(self, item):
        sid = item['sid']
        day = item['daily_item']['day']

        chance_info = item['chance']
        daily_item = item['daily_item']
        day_vary_portion = (
            daily_item['close_price'] -
            daily_item['open_price']) / daily_item['open_price'] * 100

        # 日内涨幅 >= 6%且操作时间在10:20之后, 不建议追高
        if chance_info['op'] == MinuteTrend.OP_LONG and item[
                'time'] >= 1020 and abs(day_vary_portion) >= 6.00:
            return

        # TODO: 从redis中获取大盘趋势
        key = "chance-" + str(sid) + "-" + str(day)
        self.redis_conn.rpush(key, json.dumps(item))

        # 全局list, 倒序排列
        self.redis_conn.lpush("chance-" + str(day), json.dumps(item))
        self.logger.debug("%s", format_log("chance_item", item))
        '''
예제 #26
0
    def core(self, item):
        scode_list = item
        url = "http://qt.gtimg.cn/r=" + str(random.random()) + "q=" + scode_list
        print url

        try:
            response = urllib2.urlopen(url, timeout=1)
            content = response.read()
        except urllib2.HTTPError as e:
            self.logger.warning("err=get_stock_daily scode_list=%s code=%s", scode_list, str(e.code))
            return 
        except urllib2.URLError as e:
            self.logger.warning("err=get_stock_daily scode_list=%s reason=%s", scode_list, str(e.reason))
            return

        if content:
            content = safestr(content.decode('gbk'))
            #self.logger.info("desc=daily_content content=%s", content)
            lines = content.strip("\r\n").split(";")

            for line in lines:
                if 0 == len(line):
                    continue

                daily_item = self.parse_stock_daily(line)
                if daily_item is None:
                    continue

                # 追加到redis队列中
                json_data = json.dumps(daily_item)
                if self.conn:
                    self.conn.rpush("daily-queue", json_data)
                self.logger.info(format_log("fetch_daily", daily_item))
                
                # 设置dump则把数据dump到日志中, 暂定每5mindump一次, 可配置
                self.dump(int(daily_item['time'][2:4]), json_data, "daily")
예제 #27
0
    def core(self, location, day, param_sid):
        stock_df = pd.read_sql_query("select * from t_stock where location = " + str(location) + " and type = 1 and status = 'Y'", self.conn, index_col='id')
        #print stock_df.index, stock_df.columns
        sid_list = [param_sid] if param_sid > 0 else stock_df.index

        for sid in sid_list:
            dyn_info = self.update_dyn(day, sid)
            if dyn_info is None:
                print format_log("stock_paused", {'sid': sid, 'location': location, 'day': day})
                continue

            sql = SqlUtil.create_insert_sql("t_stock_dyn", dyn_info)
            try:
                db_conn = SqlUtil.get_db(self.db_config)
                db_conn.query_sql(sql, True)
            except Exception as e:
                print "err=insert_dyn sid=" + str(sid) + " day=" + str(day) + " ex=" + str(e)
                continue

            print format_log("add_stock_dyn", dyn_info)
        print format_log("finish_dyn", {'location': location, 'day': day})
예제 #28
0
def refresh_stock_histdata(redis_config, db_config, stock_list, today_data_list, day, location = 1, refresh = True):
    db_conn = SqlUtil.get_db(db_config)
    high_field_list = ["hist_high", "year_high", "month6_high", "month3_high"]
    low_field_list = ["hist_low", "year_low", "month6_low", "month3_low"]
    vary_stock_list = dict()

    for sid, stock_info in stock_list.items():
        # 忽略指数
        if sid not in today_data_list or int(stock_info['type']) == 2:
            continue

        #print stock_info
        stock_data = today_data_list[sid]
        #print stock_data
        high_index = 4
        low_index = 4

        close_price = float(stock_data['close_price'])
        out_capital = close_price * float(stock_info['out_capital']);
        capital_limit = 10
        if 3 == location:
            out_capital = out_capital / 10000
            capital_limit = 15
        # capital < 10/15(us)
        if out_capital <= capital_limit:
            continue

        for index, field_name in enumerate(high_field_list):
            if close_price > float(stock_info[field_name]):
                high_index = index
                break

        for index, field_name in enumerate(low_field_list):
            if close_price < float(stock_info[field_name]):
                low_index = index
                break

        # 表明当天价格存在最高价或者最低价
        #print sid, high_index, low_index
        if high_index < 4 or low_index < 4:  
            vary_stock_list[sid] = {'high_index': high_index, 'low_index': low_index}
            sql = "update t_stock set "
            field_list = []
            high_type = low_type = 0

            # 起始日期定为之前3个交易日, 3个交易日内无同类型突破记录  
            range_start_day = get_past_openday(day, 3, location)

            if high_index < 4:
                high_type = high_index + 1

                high_threshold_list = get_stock_price_threshold(db_config, sid, range_start_day, day, high_type, 0)
                if 0 == len(high_threshold_list):
                    add_result = add_stock_price_threshold(db_config, sid, day, close_price, high_type, low_type)
                    print format_log("add_high_price_threshold", {'sid': sid, 'day': day, 'close_price': close_price, 'high_type': high_type, 'result':add_result})
                    if add_result and high_type <= 2: # 年内新高/历史最高才加入股票池
                        pool_result = add_stock_pool(db_config, redis_config, sid, day, 2, {'wave':1})    
                        print format_log("add_stock_pool", {'sid': sid, 'day': day, 'close_price': close_price, 'result':pool_result})

                for field_name in high_field_list[high_index:]:
                    stock_info[field_name] = close_price
                    field_list.append(field_name + "=" + str(stock_info[field_name]))

            if low_index < 4:
                low_type = low_index + 1

                low_threshold_list = get_stock_price_threshold(db_config, sid, range_start_day, day, 0, low_type)
                if 0 == len(low_threshold_list):
                    add_result = add_stock_price_threshold(db_config, sid, day, close_price, high_type, low_type)
                    print format_log("add_low_price_threshold", {'sid': sid, 'day': day, 'close_price': close_price, 'low_type': low_type, 'result':add_result})
                    # TODO: 把下跌突破也加入股票池                       

                for field_name in low_field_list[low_index:]:
                    stock_info[field_name] = close_price
                    field_list.append(field_name + "=" + str(stock_info[field_name]))
           
            sql = sql + ", ".join(field_list) + " where id=" + str(stock_info['id'])
            print sql

            # 股票且设置刷新, 才插入价格突破记录
            if refresh and int(stock_info['type']) == 1:
                try:
                    db_conn.query_sql(sql, True)
                except Exception as e:
                    continue
            
            log_info = {'sid': sid, 'code': stock_info['code'], 'name': stock_info['name'], 'day': day, 
                        'close_price': stock_data['close_price'], 'high_price': stock_data['high_price'], 'low_price': stock_data['low_price'], 
                       'high_index': high_index, 'low_index': low_index}

            print format_log("refresh_stock_info", log_info)

    #TODO: 统一删除变化的stock_info
    if refresh:
        conn = redis.StrictRedis(redis_config['host'], redis_config['port'])
        key_list = [ "stock:info-" + str(sid) for sid in vary_stock_list.keys() ]
        conn.delete(tuple(key_list))

    return vary_stock_list
예제 #29
0
    def rapid_fall(self, item):
        sid = item['sid']
        price_pair_map = self.vary_map[sid]
        if price_pair_map is None:
            self.logger.warning("desc=non_exist_pairmap sid=%d", sid)
            return
        elif len(price_pair_map) <= 3:
            return

        start_time = int(item['items'][0]['time'] / 100)
        last_time = self.time_map[sid]

        #print price_pair_map
        time_list = price_pair_map.keys()
        time_list.sort()

        key = "ts-rf-" + str(item['day'])
        fall_info = None
        fall_map = dict()
        refresh = False

        cache_value = self.redis_conn.hget(key, sid)
        # 已经存在则判断[start_time, last_time]对应的最低价 <= low, 是则更新其时间
        if cache_value:
            fall_map = json.loads(cache_value)

            for fall_start_time, fall_info in fall_map.items():
                now_time = fall_info['now_time']
                diff_sec = self.get_diff_time(start_time, now_time)

                # 超过5min不再认为是连续下跌
                if diff_sec >= 0 and diff_sec <= 60 * 5:
                    (fall_info, refresh) = self.refresh_rapid(sid, fall_info, start_time, False)
                    if refresh:
                        self.redis_conn.hmset(key, {sid: json.dumps(fall_map)})
                        self.logger.info(format_log("ts_refresh_rapid_fall", fall_info))
                        break
            return

        try:
            index = time_list.index(start_time)
        except ValueError:
            self.logger.warning("err=time_not_exist sid=%d start_time=%d", sid, start_time)
        else:
            while index >= 2 and index < len(time_list):
                now_time = time_list[index]
                past_time = time_list[index-2]
                index += 1

                # 对当前时间的最低价 减去 2分钟前的最高价,若跌幅比例超过1.6%,则认为存在快速拉升的可能
                cur_low_price = price_pair_map[now_time][1]
                past_high_price = price_pair_map[past_time][0]
                vary_portion = round((cur_low_price - past_high_price) / past_high_price * 100, 1)

                if (past_high_price >= 3.0 and vary_portion <= -1.6) or (past_high_price < 3.0 and vary_portion <= -2.5):
                    fall_info = {'start_time': past_time, 'now_time': now_time, 'low': cur_low_price, 'high': past_high_price, 'vary_portion': vary_portion}
                    fall_info['duration'] = self.get_diff_time(fall_info['now_time'], fall_info['start_time'])
                    break

            if fall_info:
                if now_time < last_time and index < len(time_list):
                    (fall_info, refresh) = self.refresh_rapid(sid, fall_info, time_list[index], False)

                fall_map[fall_info['start_time']] = fall_info
                self.redis_conn.hmset(key, {sid: json.dumps(fall_map)})

                fall_info['sid'] = sid
                fall_info['day'] = item['day']
                self.logger.info(format_log("ts_new_rapid_fall", fall_info))
예제 #30
0
    def close_position(self, location, day, cur_timenumber, sid, item):
        if sid not in self.stock_map or self.stock_map[sid][
                'state'] >= PortfolioManager.STATE_WAIT_CLOSE:
            self.logger.debug(
                "desc=stock_closed_already location=%d sid=%d day=%d",
                location, sid, day)
            return

        stock_order = self.stock_map[sid]
        daily_item = self.get_stock_currentinfo(sid, day)
        if daily_item is None:
            return

        current_price = daily_item['close_price']
        stock_time = daily_item['time']

        vary_portion = (current_price - stock_order['open_price']
                        ) / stock_order['open_price'] * 100
        if stock_order['op'] == MinuteTrend.OP_SHORT:
            vary_portion = -1 * vary_portion

        need_close = False
        reason = ""

        # 尝试获利平仓
        if vary_portion > 0:
            for close_portion_item in self.chance_config[location][
                    'close_portion_cond']:
                (hour_time, hour_portion) = close_portion_item
                if stock_time >= hour_time and vary_portion >= hour_portion:
                    need_close = True
                    reason = "profit"
                    break

        # 止损平仓: 越过止损位
        if not need_close and (stock_order['op'] == MinuteTrend.OP_LONG and
                               current_price <= stock_order['stop_price']) or (
                                   stock_order['op'] == MinuteTrend.OP_SHORT
                                   and
                                   current_price >= stock_order['stop_price']):
            reason = "stop"
            need_close = True

        # 结合最近30min趋势来分析是否平仓
        if not need_close:
            trend_key = "trend-" + str(sid) + "-" + str(day)
            suggest_op = MinuteTrend.OP_WAIT

            latest_trend_value = self.redis_conn.lindex(trend_key, -1)
            trend_node = json.loads(
                latest_trend_value) if latest_trend_value else None
            if trend_node:
                suggest_op = MinuteTrend.OP_MAP[trend_node['trend'][0]]
            elif item is not None:
                suggest_op = item['trend_detail']['op']
                if item['trend_detail']['changed']:
                    need_close = True
                    reason = "pivot"

            if suggest_op != MinuteTrend.OP_WAIT and suggest_op != stock_order[
                    'op']:
                need_close = True
                reason = "pivot"

        # 超过指定时间平仓
        if not need_close and stock_time >= self.chance_config[location][
                'close_deadline_time']:
            reason = "time"
            need_close = True

        item_json = "" if item is None else json.dumps(item)
        self.logger.info(
            "%s need_close=%s reason=%s location=%d day=%d time=%d current_price=%.2f vary_portion=%.2f item=%s",
            format_log("close_detail", stock_order),
            str(need_close), reason, location, day, stock_time, current_price,
            abs(vary_portion), item_json)

        #TODO: 调用订单平仓, 这里需要注意下单平仓后, 成交之前重复下单
        if need_close:
            close_item = {
                'sid': sid,
                'day': day,
                'code': daily_item['code'],
                'time': cur_timenumber,
                'price': current_price
            }
            close_item['op'] = MinuteTrend.OP_SHORT if stock_order[
                'op'] == MinuteTrend.OP_LONG else MinuteTrend.OP_LONG
            self.portfolio.close(sid, close_item)
예제 #31
0
    def open_position(self, location, day, cur_timenumber, sid, item):
        # 获取该股票所有的操作机会
        same_count = 1
        contray_count = 0

        key = "chance-" + str(sid) + "-" + str(day)
        # 该机会不一定是最新的1个
        chance_item_list = self.redis_conn.lrange(key, 0, -1)
        for chance_item_data in chance_item_list:
            chance_item = json.loads(chance_item_data)
            # 忽略自己
            if chance_item['time'] == item['time']:
                continue

            if chance_item['chance']['op'] == item['chance']['op']:
                same_count += 1
            else:
                contray_count += 1

        # 直接买入
        #print same_count, contray_count
        if contray_count == 0 or same_count > contray_count:
            order_event = {
                'sid': sid,
                'day': day,
                'code': item['code'],
                'time': item['time']
            }
            stop_price = item['chance']['stop_price']

            if item['chance']['op'] == MinuteTrend.OP_LONG:
                open_price = item['chance']['price_range'][1]
                stop_price = min(
                    stop_price,
                    open_price *
                    (1 - self.chance_config[location]['stop_portion'] / 100))
            else:
                open_price = item['chance']['price_range'][0]
                stop_price = max(
                    stop_price,
                    open_price *
                    (1 + self.chance_config[location]['stop_portion'] / 100))

            # <=1000时建仓, 取区间的中间价格作为建仓价
            if item['time'] <= 1000:
                open_price = (item['chance']['price_range'][0] +
                              item['chance']['price_range'][1]) / 2

            order_event['open_price'] = open_price
            order_event['stop_price'] = stop_price
            order_event['op'] = item['chance']['op']

            # 调用PortfioManager进行建仓
            open_result = self.portfolio.open(sid, order_event)
            self.logger.info("%s open_result=%s",
                             format_log("open_position", order_event),
                             str(open_result))

            # 建仓的机会push到单独的队列中
            self.redis_conn.rpush("order-chance", json.dumps(item))
            return True

        return False
예제 #32
0
def chance_return(config_info, location, day, item):
    hqdata = get_hqdata(config_info['DB'], config_info['REDIS'], day,
                        item['sid'])
    print hqdata['daily']

    chance_item = item['chance']
    trend_item = item['trend_item']
    # 卖出时用平仓价格减卖出价格, 需要乘-1
    factor = 1 if 1 == chance_item['op'] else -1

    # 严格一点, 应该用time之后的high_price/low_price计算收益和回撤, 买入点以较大价格进入, 卖出以较低价格卖出
    enter_price = chance_item['price_range'][1] if 1 == chance_item[
        'op'] else chance_item['price_range'][0]
    exit_price = float(hqdata['daily']['close_price'])
    fall_price = float(
        hqdata['daily']['low_price']) if 1 == chance_item['op'] else float(
            hqdata['daily']['high_price'])

    rise_portion = factor * (exit_price - enter_price) / enter_price * 100
    fall_portion = factor * (fall_price - enter_price) / enter_price * 100

    return_info = {
        "sid": item['sid'],
        "code": item['code'],
        "time": item['time'],
        "op": chance_item['op'],
        "trend_length": trend_item['length'],
        "trend_portion": trend_item['vary_portion'],
        "enter": enter_price,
        "exit": exit_price,
        "fall": fall_price,
        "close_portion": rise_portion,
        "fall_portion": fall_portion,
        "vary_portion": hqdata['daily']['vary_portion']
    }

    # TODO: 输出 dyn数据
    record_list = []
    try:
        sql = "select * from t_stock_dyn where sid = {sid} and day < {day} order by day desc limit 1".format(
            sid=item['sid'], day=day)
        #print sql
        db_conn = SqlUtil.get_db(config_info['DB'])
        record_list = db_conn.query_sql(sql)
    except Exception as e:
        print e
        logging.getLogger("chance").error(
            "err=get_stock_dyn sid=%d code=%s location=%d day=%d", item['sid'],
            item['code'], location, day)
    else:
        #print record_list
        if len(record_list) == 1:
            stock_dyn = record_list[0]
            for key in [
                    'ma5_swing', 'ma20_swing', 'ma5_vary_portion',
                    'ma20_vary_portion', 'ma5_exchange_portion',
                    'ma20_exchange_portion', 'volume_ratio'
            ]:
                return_info[key] = stock_dyn[key]

    logging.getLogger("chance").info("%s", format_log("chance_item", item))
    return return_info
예제 #33
0
    portfolio_list = manager.get_portfolio(PortfolioManager.STATE_ALL)
    print portfolio_list

    # 平仓下单
    close_item = dict()
    close_item['sid'] = sid
    close_item['code'] = code
    close_item['day'] = day
    close_item['time'] = 950
    close_item['op'] = MinuteTrend.OP_SHORT
    close_item['close_price'] = 22.00
    close_result = manager.close(sid, close_item)

    # 平仓订单成交
    close_event = dict()
    close_event['order_id'] = 10013
    close_event['code'] = code
    close_event['op'] = MinuteTrend.OP_SHORT
    close_event['quantity'] = 80
    close_event['price'] = 22.00
    close_event['cost'] = close_event['quantity'] * close_event['price']
    close_event['time'] = 1030
    manager.fill_order(close_event)

    for sid, order_info in manager.order_stock.items():
        print format_log("order_info", order_info)
        records = manager.get_trade_records(sid)
        for record in records:
            print format_log("trade_record", record)

예제 #34
0
    def rapid_rise(self, item):
        sid = item['sid']
        start_time = int(item['items'][0]['time'] / 100)

        last_time = self.time_map[sid]
        price_pair_map = self.vary_map[sid]
        if price_pair_map is None:
            self.logger.warning("desc=non_exist_pairmap sid=%d", sid)
            return
        elif len(price_pair_map) <= 3:
            return

        # 取出的key列表后按照时间大小排列
        time_list = price_pair_map.keys()
        time_list.sort()

        rise_map = dict()
        rise_info = None
        key = "ts-rr-" + str(item['day'])
        cache_value = self.redis_conn.hget(key, sid)
        refresh = False

        # 已经存在则判断[start_time, last_time]对应的最高价 >= high, 是则更新其时间
        # 连续拉升结束后, 超过5min后的再次拉升作为一个新的拉升波段, 根据起始时间存储多个拉升波段
        if cache_value:
            rise_map = json.loads(cache_value)

            for rise_start_time, rise_info in rise_map.items():
                now_time = rise_info['now_time']
                diff_sec = self.get_diff_time(start_time, now_time)

                # 5min 以内作为一个新的波段持续
                if diff_sec >= 0 and diff_sec <= 60 * 5:
                    (rise_info,
                     refresh) = self.refresh_rapid(sid, rise_info, start_time,
                                                   True)
                    if refresh:
                        self.redis_conn.hmset(key, {sid: json.dumps(rise_map)})
                        self.logger.info(
                            format_log("ts_refresh_rapid_rise", rise_info))
                        break
            return

        try:
            index = max(time_list.index(start_time), 2)
        except ValueError:
            self.logger.warning("err=time_not_exist sid=%d start_time=%d", sid,
                                start_time)
        else:
            while index >= 2 and index < len(time_list):
                now_time = time_list[index]
                past_time = time_list[index - 2]
                index += 1

                # 对当前时间的最高价 减去 2分钟前的最低价,若涨幅比例超过1.6%,则认为存在快速拉升的可能
                cur_high_price = price_pair_map[now_time][0]
                past_low_price = price_pair_map[past_time][1]
                vary_portion = round(
                    (cur_high_price - past_low_price) / past_low_price * 100,
                    2)

                if (past_low_price >= 3.0
                        and vary_portion >= 1.6) or (past_low_price < 3.0
                                                     and vary_portion >= 2.5):
                    rise_info = {
                        'start_time': past_time,
                        'now_time': now_time,
                        'low': past_low_price,
                        'high': cur_high_price,
                        'vary_portion': vary_portion
                    }
                    rise_info['duration'] = self.get_diff_time(
                        rise_info['now_time'], rise_info['start_time'])
                    break

            #print rise_info, now_time
            if rise_info:
                if now_time < last_time and index < len(time_list):
                    (rise_info,
                     refresh) = self.refresh_rapid(sid, rise_info,
                                                   time_list[index], True)

                rise_map[rise_info['start_time']] = rise_info
                self.redis_conn.hmset(key, {sid: json.dumps(rise_map)})

                rise_info['sid'] = sid
                rise_info['day'] = item['day']
                self.logger.info(format_log("ts_new_rapid_rise", rise_info))
예제 #35
0
    def core(self, item):
        sid = item[0]
        scode = item[1]
        url = ""
        
        if 1 == self.location:
            key = scode
            url = "http://web.ifzq.gtimg.cn/appstock/app/minute/query?_var=min_data_{CODE}&code={CODE}&r=" + str(random.random())
        elif 3 == self.location:
            stock_info = self.datamap['stock_list'][sid]
            if 1 == int(stock_info['type']):
                ecode_str = "OQ" if 4 == int(stock_info['ecode']) else "N"
                key = scode + "." + ecode_str
            else:
                key = scode
            url = "http://web.ifzq.gtimg.cn/appstock/app/UsMinute/query?_var=min_data_{CODE}&code={CODE}&r=" + str(random.random())

        try:
            request_url = url.format(CODE=key)
            content = ""
            try:
                response = requests.get(request_url, timeout=5)
                content = response.text
            except Exception as e:
                self.logger.exception("err=get_stock_realtime sid=%d scode=%s", sid, scode)
                return None

            hq_json = None
            if content.find("=") != -1:
                part = content.split("=")
                if len(part) >= 2 and part[1]:
                    hq_json = json.loads(part[1])
            else: #部分股票返回时没有=及前面部分, 直接是个json
                hq_json = json.loads(content)

            if hq_json is None or hq_json["code"] == -1:
                self.logger.error("err=invalid_realtime_content sid=%d scode=%s url=%s content=%s", sid, scode, url, content)
                return

            data_json = hq_json['data'][key]['data']
            #print data_json

            #qt包含市场指数和明细, mx为最近2min的逐笔成交明细, price为分价数据
            date_str = data_json['date'].strip()
            #返回的日期为当前日期,非美国时区的日期
            data_day = int(date_str) if len(date_str) > 0 else self.day

            hq_item = list()
            last_time = 0

            if sid in self.time_map:
                last_time = self.time_map[sid]

            new_time = last_time
            for line in data_json['data']:
                fields = line.split(" ")
                try:
                    if len(fields) < 3 or len(fields[0]) == 0:
                        continue

                    # 直接用小时+分组成的时间, 格式为HHMM
                    time = int(fields[0].lstrip("0"))
                    if time <= last_time:
                        continue

                    data_item = dict()
                    data_item['time'] = time
                    data_item['price'] = float(fields[1].replace(",", ""))
                    # 美股拉取的成交量为累计成交量
                    data_item['volume'] = int(fields[2])

                    if data_item['volume'] <= 0:
                        continue
                    hq_item.append(data_item)
                    new_time = max(time, new_time)
                except Exception as e:
                    self.logger.exception("err=parse_stock_realtime sid=%d scode=%s line=%s", sid, scode, line)
                    continue
        except Exception as ex:
            self.logger.exception("err=get_stock_realtime sid=%d scode=%s url=%s", sid, scode, url)
            return

        # 表示当天所有的成交量都为0, 当天停牌
        if len(hq_item) == 0:
            return

        # 更新last_time
        print scode, key, request_url, len(hq_item)
        self.time_map[sid] = new_time

        json_item = json.dumps({'sid': sid, 'day': data_day, 'items': hq_item})
        self.conn.rpush("realtime-queue", json_item)
        self.logger.info(format_log("fetch_realtime", {'sid': sid, 'scode': scode, 'time': hq_item[len(hq_item) - 1]['time'], 'price': hq_item[len(hq_item) - 1]['price']}))
        
        # 设置dump则把数据dump到日志中, 暂定每5min dump一次, 可配置
        self.dump(new_time, json_item, "realtime")
예제 #36
0
    def rapid_fall(self, item):
        sid = item['sid']
        price_pair_map = self.vary_map[sid]
        if price_pair_map is None:
            self.logger.warning("desc=non_exist_pairmap sid=%d", sid)
            return
        elif len(price_pair_map) <= 3:
            return

        start_time = int(item['items'][0]['time'] / 100)
        last_time = self.time_map[sid]

        #print price_pair_map
        time_list = price_pair_map.keys()
        time_list.sort()

        key = "ts-rf-" + str(item['day'])
        fall_info = None
        fall_map = dict()
        refresh = False

        cache_value = self.redis_conn.hget(key, sid)
        # 已经存在则判断[start_time, last_time]对应的最低价 <= low, 是则更新其时间
        if cache_value:
            fall_map = json.loads(cache_value)

            for fall_start_time, fall_info in fall_map.items():
                now_time = fall_info['now_time']
                diff_sec = self.get_diff_time(start_time, now_time)

                # 超过5min不再认为是连续下跌
                if diff_sec >= 0 and diff_sec <= 60 * 5:
                    (fall_info,
                     refresh) = self.refresh_rapid(sid, fall_info, start_time,
                                                   False)
                    if refresh:
                        self.redis_conn.hmset(key, {sid: json.dumps(fall_map)})
                        self.logger.info(
                            format_log("ts_refresh_rapid_fall", fall_info))
                        break
            return

        try:
            index = time_list.index(start_time)
        except ValueError:
            self.logger.warning("err=time_not_exist sid=%d start_time=%d", sid,
                                start_time)
        else:
            while index >= 2 and index < len(time_list):
                now_time = time_list[index]
                past_time = time_list[index - 2]
                index += 1

                # 对当前时间的最低价 减去 2分钟前的最高价,若跌幅比例超过1.6%,则认为存在快速拉升的可能
                cur_low_price = price_pair_map[now_time][1]
                past_high_price = price_pair_map[past_time][0]
                vary_portion = round(
                    (cur_low_price - past_high_price) / past_high_price * 100,
                    1)

                if (past_high_price >= 3.0 and vary_portion <= -1.6) or (
                        past_high_price < 3.0 and vary_portion <= -2.5):
                    fall_info = {
                        'start_time': past_time,
                        'now_time': now_time,
                        'low': cur_low_price,
                        'high': past_high_price,
                        'vary_portion': vary_portion
                    }
                    fall_info['duration'] = self.get_diff_time(
                        fall_info['now_time'], fall_info['start_time'])
                    break

            if fall_info:
                if now_time < last_time and index < len(time_list):
                    (fall_info,
                     refresh) = self.refresh_rapid(sid, fall_info,
                                                   time_list[index], False)

                fall_map[fall_info['start_time']] = fall_info
                self.redis_conn.hmset(key, {sid: json.dumps(fall_map)})

                fall_info['sid'] = sid
                fall_info['day'] = item['day']
                self.logger.info(format_log("ts_new_rapid_fall", fall_info))
예제 #37
0
 def serialize(self, item):
     key = "daily-" + str(item['sid']) + "-" + str(item['day'])
     result = self.redis_conn.set(key, json.dumps(item), 86400)
     self.logger.debug("%s", format_log("daily_item", item))
예제 #38
0
    def open(self, sid, open_item):
        wait_open_map = self.get_portfolio(PortfolioManager.STATE_WAIT_OPEN)
        if sid in wait_open_map:
            self.logger.info("op=stock_wait_open sid=%d code=%s", sid,
                             wait_open_map[sid]['code'])
            return False

        # 暂时不允许已建仓的股票再建仓
        opened_map = self.get_portfolio(PortfolioManager.STATE_OPENED)
        if sid in opened_map and open_item['op'] == opened_map[sid]['op']:
            self.logger.info("%s",
                             format_log("stock_order_opened", opened_map[sid]))
            return False

        # 建仓是否允许
        if not self.check_allow("OPEN", open_item):
            self.logger.info("%s %s", format_log("open_not_allowed",
                                                 open_item),
                             json.dumps(self.port_statinfo))
            return False

        # TODO: cash仅在成交后才会扣减, 实际通过IB下单时, 会出现成交前多只股票都可以下单
        rest_money = self.holdings['cash']
        quantity = self.cal_quantity(sid, open_item['op'], rest_money,
                                     open_item['open_price'])
        if quantity <= 0:
            self.logger.info(
                "op=no_enough_money sid=%d code=%s op=%d rest_money=%d open_price=%.2f",
                sid, open_item['code'], open_item['op'], rest_money,
                open_item['open_price'])
            return False

        # TODO: 推送下单消息, 设置建仓价格 + 止损价格, 设置下单类型为限价单
        order_info = {
            'sid': sid,
            'day': open_item['day'],
            'code': open_item['code'],
            'op': open_item['op'],
            'quantity': quantity,
            'stop_price': open_item['stop_price']
        }
        order_event = dict(order_info)
        order_event['order_type'] = "LMT"
        order_event['price'] = open_item['open_price']
        push_result = self.redis_conn.rpush("order-queue",
                                            json.dumps(order_event))

        # 更新剩余的现金
        sign = 1 if open_item['op'] == MinuteTrend.OP_LONG else -1
        cost = sign * quantity * open_item['open_price']

        order_event['time'] = open_item['time']
        self.logger.info("%s cost=%d", format_log("open_order", order_event),
                         cost)

        order_info['open_price'] = open_item['open_price']
        order_info['state'] = self.STATE_WAIT_OPEN
        self.order_stock[sid] = order_info

        # 更新持仓管理的统计
        self.update_stat("OPEN", open_item)

        # TODO: 目前先手动构造fill_event来成交
        fill_event = {
            'code': order_event['code'],
            'op': order_event['op'],
            'quantity': quantity,
            'price': order_event['price'],
            'cost': cost,
            'time': open_item['time']
        }
        fill_event['order_id'] = random.randint(100, 500)
        self.fill_order(fill_event)

        return True
예제 #39
0
    def evaluate(self, day, policy):
        stock_info = self.get_stock_info(self.sid)
        print stock_info

        cur_day = str(day)
        current_time = datetime.datetime(int(cur_day[0:4]), int(cur_day[4:6]),
                                         int(cur_day[6:8]))
        start_day = '{0:%Y%m%d}'.format(current_time +
                                        datetime.timedelta(days=-60))

        # 获取最近60天内的交易数据
        history_data = self.get_histdata_range(self.sid, start_day, cur_day)
        if len(history_data) <= 20:
            return None

        today_data = history_data[0]
        print today_data

        check_result = self.check(stock_info, history_data, today_data)
        if check_result < 0:
            print format_log(
                "check_failed", {
                    'sid': self.sid,
                    'name': stock_info['name'],
                    'day': day,
                    'result': check_result,
                    'close_price': today_data['close_price']
                })
            return None

        today_open_price = float(today_data['open_price'])
        today_close_price = float(today_data['close_price'])

        # 股票趋势
        trend_info = self.get_trend(history_data[0:10], 5)
        print trend_info

        # 判断股票是否符合指定的分析策略
        judge_info = self.judge(trend_info, stock_info, history_data, policy)
        print judge_info
        if judge_info is False:
            print format_log(
                "judge_failed", {
                    'sid': self.sid,
                    'name': stock_info['name'],
                    'day': day,
                    'trend': trend_info['trend'],
                    'wave': trend_info['wave'],
                    'close_price': today_data['close_price']
                })
            return None

        # TODO: 评估股票的综合得分
        score = self.rank(trend_info, stock_info, history_data, policy)

        pool_info = dict()
        pool_info['trend'] = trend_info['trend']
        pool_info['wave'] = trend_info['wave']
        pool_info.update({
            'low_price': judge_info[0],
            'high_price': judge_info[1]
        })
        pool_info['current_price'] = today_data['close_price']
        pool_info['sid'] = self.sid
        pool_info['day'] = cur_day
        pool_info['score'] = score

        # 把股票加入股票池中
        add = self.add_stock_pool(self.sid, day, pool_info)
        print "op=add_pool_record, add=" + str(add)

        return pool_info
예제 #40
0
    def core(self, item):
        sid = item[0]
        scode = item[1]
        if sid in self.ignore_set:
            return

        if sid in self.pos_map:
            (pno, last_id) = self.pos_map[sid]
        else:
            pno = last_id = 0

        url = "http://stock.gtimg.cn/data/index.php?appn=detail&action=data&c=" + scode + "&p=" + str(pno)
        #print scode, url

        try:
            response = urllib2.urlopen(url, timeout=5)
            content = response.read()
        except urllib2.HTTPError as e:
            self.logger.warning("err=get_stock_transaction sid=%d scode=%s pno=%d code=%s", sid, scode, pno, str(e.code))
            return None
        except urllib2.URLError as e:
            self.logger.warning("err=get_stock_transaction sid=%d scode=%s pno=%d reason=%s", sid, scode, pno, str(e.reason))
            return None

        # 拉取内容为空, 表明股票当天停牌, TODO: 加入公共的停牌列表中
        if 0 == len(content.strip()):
            self.ignore_set.add(sid)
            return

        lines = content.split('"')
        if len(lines) < 2:
            self.logger.warning("err=invalid_resp sid=%d scode=%s content=%s", sid, scode, content)
            return
        #print lines

        elements = lines[1].split("|")
        new_id = last_id
        transaction_list = []

        for element in elements:
            field_list = element.split("/")
            #print field_list
            transaction = dict()

            id = int(field_list[0])
            if id <= last_id :
                continue

            transaction['time'] = field_list[1].replace(":", "")
            transaction['price'] = float(field_list[2])
            transaction['vary_price'] = float(field_list[3])
            transaction['volume'] = int(field_list[4])
            transaction['amount'] = int(field_list[5])
            # 类型为B/S/M, 分别代表买盘/卖盘/中性盘
            transaction['type'] = field_list[6]

            new_id = max(id, new_id)
            transaction_list.append(transaction)

        transaction_count = len(transaction_list)
        #print format_log("fetch_transaction", {'sid': sid, 'scode': scode, 'p': pno, 'last_id': last_id, 'new_id': new_id, 'detail_count': transaction_count})
        self.logger.info(format_log("fetch_transaction", {'sid': sid, 'scode': scode, 'p': pno, 'last_id': last_id, 'new_id': new_id, 'detail_count': transaction_count}))

        if transaction_count > 0:
            # 每个时间段达到70笔成交记录时, p需要加1
            if transaction_count == 70:
                pno += 1

            #更新pno和last_id的值
            self.pos_map[sid] = (pno, new_id)

            self.conn.rpush("ts-queue", json.dumps({'sid': sid, 'day': self.day, 'items': transaction_list}))
        else: # 没有新记录表明拉取完了
            self.ignore_set.add(sid)
            return
예제 #41
0
    def rapid_rise(self, item):
        sid = item['sid']
        start_time = int(item['items'][0]['time'] / 100)

        last_time = self.time_map[sid]
        price_pair_map = self.vary_map[sid]
        if price_pair_map is None:
            self.logger.warning("desc=non_exist_pairmap sid=%d", sid)
            return 
        elif len(price_pair_map) <= 3:
            return

        # 取出的key列表后按照时间大小排列
        time_list = price_pair_map.keys()
        time_list.sort()

        rise_map = dict()
        rise_info = None
        key = "ts-rr-" + str(item['day'])
        cache_value = self.redis_conn.hget(key, sid)
        refresh = False

        # 已经存在则判断[start_time, last_time]对应的最高价 >= high, 是则更新其时间
        # 连续拉升结束后, 超过5min后的再次拉升作为一个新的拉升波段, 根据起始时间存储多个拉升波段
        if cache_value:
            rise_map = json.loads(cache_value)

            for rise_start_time, rise_info in rise_map.items():
                now_time = rise_info['now_time']
                diff_sec = self.get_diff_time(start_time, now_time)

                # 5min 以内作为一个新的波段持续
                if diff_sec >= 0 and diff_sec <= 60 * 5:
                    (rise_info, refresh) = self.refresh_rapid(sid, rise_info, start_time, True)
                    if refresh:
                        self.redis_conn.hmset(key, {sid: json.dumps(rise_map)})
                        self.logger.info(format_log("ts_refresh_rapid_rise", rise_info))
                        break
            return

        try:
            index = max(time_list.index(start_time), 2)
        except ValueError:
            self.logger.warning("err=time_not_exist sid=%d start_time=%d", sid, start_time)
        else:
            while index >= 2 and index < len(time_list):
                now_time = time_list[index]
                past_time = time_list[index-2]
                index += 1

                # 对当前时间的最高价 减去 2分钟前的最低价,若涨幅比例超过1.6%,则认为存在快速拉升的可能
                cur_high_price = price_pair_map[now_time][0]
                past_low_price = price_pair_map[past_time][1]
                vary_portion = round((cur_high_price - past_low_price) / past_low_price * 100, 2)

                if (past_low_price >= 3.0 and vary_portion >= 1.6) or (past_low_price < 3.0 and vary_portion >= 2.5):
                    rise_info = {'start_time': past_time, 'now_time': now_time, 'low': past_low_price, 'high': cur_high_price, 'vary_portion': vary_portion}
                    rise_info['duration'] = self.get_diff_time(rise_info['now_time'], rise_info['start_time'])
                    break

            #print rise_info, now_time
            if rise_info:
                if now_time < last_time and index < len(time_list):
                    (rise_info, refresh) = self.refresh_rapid(sid, rise_info, time_list[index], True)

                rise_map[rise_info['start_time']] = rise_info
                self.redis_conn.hmset(key, {sid: json.dumps(rise_map)})

                rise_info['sid'] = sid
                rise_info['day'] = item['day']
                self.logger.info(format_log("ts_new_rapid_rise", rise_info))
예제 #42
0
    def parse_stock_daily(self, line):
        line = line.strip("\r\n")
        parts = line.split("=")
        #print line, parts
        stock_code = parts[0].replace("var hq_str_gb_", "").upper()
        content = parts[1].strip('"')
        #print content

        fields = content.split(",")
        #print fields
        if len(fields) < 20:
            line_str = safestr(line)
            self.logger.error(format_log("daily_lack_fields", {'line': line_str, 'content': content}))
            return None

        # 当日停牌则不能存入
        open_price = float(fields[5])
        close_price = float(fields[1])
        if open_price == 0.0 or close_price == 0.0:
            return None

        item = dict()

        try:
            item['name'] = safestr(fields[0])
            item['code'] = stock_code
            item['sid'] = int(self.datamap['code2id'][stock_code])
            item['day'] = self.day
            item['last_close_price'] = float(fields[26])
            item['open_price'] = open_price
            item['high_price'] = float(fields[6])
            item['low_price'] = float(fields[7])
            item['close_price'] = close_price

            # 当前时刻, 格式为HHMMSS, 这里新浪返回的时间是错的, 改成自己计算, 可能出现不实时的情况
            #time_parts = fields[3].split(" ")
            #item['time'] = time_parts[1].replace(":", "")
            item['time'] = str(get_timenumber(3))

            item['vary_price'] = float(fields[4])
            item['vary_portion'] = float(fields[2])
            # 成交量单位为股
            item['volume'] = int(fields[10])
            item['predict_volume'] = get_predict_volume(item['volume'], item['time'], self.location)
            # 成交额转化为万元
            item['amount'] = 0
            # 总股本
            item['out_capital'] = float(fields[19])
            # 总市值
            item['cap'] = float(fields[12])
            # 计算换手率
            if item['out_capital'] > 0:
                item['exchange_portion'] = item['volume'] / item['out_capital'] * 100
            item['swing'] = (item['high_price'] - item['low_price']) / item['last_close_price'] * 100
        except Exception as e:
            traceback.print_exc() 
            self.logger.exception("err=parse_daily_ex code=%s line=%s", stock_code, line)
            return None

        if item['out_capital'] > 0:
            capital = item['out_capital'] / 10000
            self.logger.debug("op=update_sql sql={update t_stock set capital=%.2f, out_capital=%.2f where id = %d;}", capital, capital, item['sid'])
        return item
예제 #43
0
    def core(self, item):
        sid = item[0]
        scode = item[1]
        url = "http://data.gtimg.cn/flashdata/hushen/minute/" + scode + ".js?maxage=10&" + str(
            random.random())
        #print scode, url

        try:
            response = urllib2.urlopen(url, timeout=1)
            content = response.read()
        except urllib2.HTTPError as e:
            self.logger.warning(
                "err=get_stock_realtime sid=%d scode=%s code=%s", sid, scode,
                str(e.code))
            return None
        except urllib2.URLError as e:
            self.logger.warning(
                "err=get_stock_realtime sid=%d scode=%s reason=%s", sid, scode,
                str(e.reason))
            return None

        content = content.strip(' ;"\n').replace("\\n\\", "")
        lines = content.split("\n")
        #print lines

        date_info = lines[1].split(":")
        data_day = int("20" + date_info[1])
        hq_item = list()
        last_time = 0

        if sid in self.time_map:
            last_time = self.time_map[sid]

        new_time = last_time
        for line in lines[2:]:
            fields = line.split(" ")
            # 直接用小时+分组成的时间, 格式为HHMM
            time = int(fields[0])

            if time <= last_time:
                continue

            data_item = dict()
            data_item['time'] = time
            data_item['price'] = float(fields[1])
            data_item['volume'] = int(fields[2])

            if data_item['volume'] <= 0:
                continue

            hq_item.append(data_item)
            new_time = max(time, new_time)

        # 表示当天所有的成交量都为0, 当天停牌
        if len(hq_item) == 0:
            return

        # 更新last_time
        self.time_map[sid] = new_time

        self.conn.rpush(
            "realtime-queue",
            json.dumps({
                'sid': sid,
                'day': data_day,
                'items': hq_item
            }))
        #print format_log("fetch_realtime", {'sid': sid, 'scode': scode, 'time': hq_item[len(hq_item) - 1]['time'], 'price': hq_item[len(hq_item) - 1]['price']})
        self.logger.info(
            format_log(
                "fetch_realtime", {
                    'sid': sid,
                    'scode': scode,
                    'time': hq_item[len(hq_item) - 1]['time'],
                    'price': hq_item[len(hq_item) - 1]['price']
                }))
예제 #44
0
    portfolio_list = manager.get_portfolio(PortfolioManager.STATE_ALL)
    print portfolio_list

    # 平仓下单
    close_item = dict()
    close_item['sid'] = sid
    close_item['code'] = code
    close_item['day'] = day
    close_item['time'] = 950
    close_item['op'] = MinuteTrend.OP_SHORT
    close_item['close_price'] = 22.00
    close_result = manager.close(sid, close_item)

    # 平仓订单成交
    close_event = dict()
    close_event['order_id'] = 10013
    close_event['code'] = code
    close_event['op'] = MinuteTrend.OP_SHORT
    close_event['quantity'] = 80
    close_event['price'] = 22.00
    close_event['cost'] = close_event['quantity'] * close_event['price']
    close_event['time'] = 1030
    manager.fill_order(close_event)

    for sid, order_info in manager.order_stock.items():
        print format_log("order_info", order_info)
        records = manager.get_trade_records(sid)
        for record in records:
            print format_log("trade_record", record)
예제 #45
0
    def close_position(self, location, day, cur_timenumber, sid, item):
        if sid not in self.stock_map or self.stock_map[sid]['state'] >= PortfolioManager.STATE_WAIT_CLOSE:
            self.logger.debug("desc=stock_closed_already location=%d sid=%d day=%d", location, sid, day);
            return

        stock_order = self.stock_map[sid]
        daily_item = self.get_stock_currentinfo(sid, day)
        if daily_item is None:
            return

        current_price = daily_item['close_price']
        stock_time = daily_item['time']

        vary_portion = (current_price - stock_order['open_price']) / stock_order['open_price'] * 100
        if stock_order['op'] == MinuteTrend.OP_SHORT:
            vary_portion = -1 * vary_portion

        need_close = False
        reason = ""

        # 尝试获利平仓
        if vary_portion > 0:
            for close_portion_item in self.chance_config[location]['close_portion_cond']:
                (hour_time, hour_portion) = close_portion_item
                if stock_time >= hour_time and vary_portion >= hour_portion:
                    need_close = True
                    reason = "profit"
                    break

        # 止损平仓: 越过止损位
        if not need_close and (stock_order['op'] == MinuteTrend.OP_LONG and current_price <= stock_order['stop_price']) or (stock_order['op'] == MinuteTrend.OP_SHORT and current_price >= stock_order['stop_price']):
            reason = "stop"
            need_close = True

        # 结合最近30min趋势来分析是否平仓
        if not need_close:
            trend_key = "trend-" + str(sid) + "-" + str(day)
            suggest_op = MinuteTrend.OP_WAIT

            latest_trend_value = self.redis_conn.lindex(trend_key, -1)
            trend_node = json.loads(latest_trend_value) if latest_trend_value else None
            if trend_node:
               suggest_op = MinuteTrend.OP_MAP[trend_node['trend'][0]]
            elif item is not None:
                suggest_op = item['trend_detail']['op']
                if item['trend_detail']['changed']:
                    need_close = True
                    reason = "pivot"

            if suggest_op != MinuteTrend.OP_WAIT and suggest_op != stock_order['op']:
                need_close = True
                reason = "pivot"

        # 超过指定时间平仓
        if not need_close and stock_time >= self.chance_config[location]['close_deadline_time']:
            reason = "time"
            need_close = True

        item_json = "" if item is None else json.dumps(item)
        self.logger.info("%s need_close=%s reason=%s location=%d day=%d time=%d current_price=%.2f vary_portion=%.2f item=%s",
                    format_log("close_detail", stock_order), str(need_close), reason, location, day, stock_time, current_price, abs(vary_portion), item_json)

        #TODO: 调用订单平仓, 这里需要注意下单平仓后, 成交之前重复下单
        if need_close:
            close_item = {'sid': sid, 'day': day, 'code': daily_item['code'], 'time': cur_timenumber, 'price': current_price}
            close_item['op'] = MinuteTrend.OP_SHORT if stock_order['op'] == MinuteTrend.OP_LONG else MinuteTrend.OP_LONG
            self.portfolio.close(sid, close_item)