예제 #1
0
class MyPosition(EmbeddedDocument):
    """ 持仓 """
    name = StringField(required=True)
    symbol = StringField(required=True)
    average_price = FloatField(required=True)
    quantity = IntField(required=True)
    price = FloatField(required=True)
    sellable = IntField()
    profit = FloatField()
예제 #2
0
class MyStatus(EmbeddedDocument):
    """ 挂单情况 """

    order = StringField(required=True)
    order_at = StringField(required=True)
    type_ = StringField(required=True)
    name = StringField(required=True)
    symbol = StringField(required=True)
    price = FloatField(required=True)
    quantity = IntField(required=True)
    pending_quantity = IntField(required=True)
    status = StringField(required=True)
예제 #3
0
파일: wechat.py 프로젝트: maocis/ybk
class WechatAccessToken(Document):
    """ 微信ACCESS_TOKEN """
    class Meta:
        idf = IDFormatter('{access_token}')
        idx1 = Index('expires_at', expireAfterSeconds=7200)

    access_token = StringField(required=True)
    expires_in = IntField()
    expires_at = DateTimeField(required=True)
    updated_at = DateTimeField(modified=True)

    @classmethod
    def get_access_token(cls):
        instance = cls.query_one()
        if instance:
            return instance['access_token']
        else:
            appid = ybk.config.conf.get('wechat_appid')
            appsecret = ybk.config.conf.get('wechat_appsecret')
            grant_type = 'client_credential'
            url = 'https://api.weixin.qq.com/cgi-bin/token'
            params = {
                'grant_type': grant_type,
                'appid': appid,
                'secret': appsecret,
            }
            j = requests.get(url, params=params, timeout=(3, 7)).json()
            expires_at = datetime.utcnow() + timedelta(seconds=j['expires_in'])
            cls({
                'access_token': j['access_token'],
                'expires_in': j['expires_in'],
                'expires_at': expires_at
            }).save()
            return j['access_token']
예제 #4
0
class Investor(Document):
    """ 投资人 """
    class Meta:
        idf = IDFormatter('{id_number}')
        idx1 = Index(['user', 'order'], unique=True)

    user = StringField(required=True)  # 用户
    order = IntField(required=True)  # 顺序
    name = StringField(required=True)
    id_type = StringField(required=True)  # 身份证/..
    id_number = StringField(required=True)  # 号码
    id_front = BinaryField()  # 照片
    id_back = BinaryField()
    mobile = StringField(required=True)
    province = StringField(required=True)
    city = StringField(required=True)
    address = StringField(required=True)
    bank_accounts = ListField(EmbeddedField(BankAccount))

    @classmethod
    def get_user_order(cls, user):
        i = cls.query_one({'user': user}, sort=[('order', -1)], limit=1)
        if not i:
            return 1
        else:
            return i.order + 1

    @classmethod
    def user_investors(cls, user):
        return list(cls.query({'user': user}, sort=[('order', 1)]))
예제 #5
0
class Exchange(Document):
    """ 交易所 """
    class Meta:
        idf = IDFormatter('{name}')

    name = StringField(required=True)
    num_users = IntField()
    num_accounts = IntField()
    position = ListField(EmbeddedField(MyPosition))
    orders = ListField(EmbeddedField(MyOrder))
    order_status = ListField(EmbeddedField(MyStatus))
    money = FloatField()
    capital = FloatField()
    profit = FloatField()
    earned = FloatField()  # 交易收入
    lost = FloatField()  # 交易亏损
예제 #6
0
파일: position.py 프로젝트: maocis/ybk
class Transaction(Document):
    """ 用户交易信息 """
    class Meta:
        idx1 = Index(['user', 'type_', 'exchange', 'symbol'])
        idx2 = Index(['user', 'operated_at'])

    exchange = StringField(required=True)  # 交易所ID(简称)
    symbol = StringField(required=True)  # 交易代码

    user = StringField(required=True)
    type_ = StringField(required=True)  # buy/sell
    price = FloatField(required=True)
    quantity = IntField(required=True)
    operated_at = DateTimeField(created=True)

    @classmethod
    def user_total_transactions(cls, user):
        """ 用户总操作次数 """
        return cls.count({'user': user})

    @classmethod
    def user_recent_transactions(cls, user, offset=0, limit=None):
        """ 用户最近的操作 """
        qs = cls.query({'user': user}, sort=[('operated_at', -1)])
        if offset:
            qs.skip(offset)
        if limit:
            qs.limit(limit)
        return list(qs)
예제 #7
0
class MyPosition(EmbeddedDocument):
    """ 持仓汇总 """

    name = StringField(required=True)
    symbol = StringField(required=True)
    average_price = FloatField(required=True)
    quantity = IntField(required=True)
    price = FloatField(required=True)
    sellable = IntField()
    profit = FloatField()

    @property
    def increase(self):
        if self.price > 0:
            return '{:4.2f}%'.format(
                (self.price / self.average_price - 1) * 100)
        else:
            return '0%'
예제 #8
0
class Order(EmbeddedDocument):
    """ 成交订单汇总 """
    type_ = StringField(required=True)
    name = StringField(required=True)
    symbol = StringField(required=True)
    price = FloatField(required=True)  # 成本价
    current_price = FloatField(required=True)  # 成交价
    quantity = IntField(required=True)
    commision = FloatField(required=True)
    profit = FloatField(required=True)
예제 #9
0
class Collection(Document):
    class Meta:
        idf = IDFormatter('{exchange}_{symbol}')

    exchange = StringField(required=True)
    symbol = StringField(required=True)
    name = StringField(required=True)
    trade_day = IntField(required=True)  # 现在是开始交易的第几个交易日?
    buy_price = FloatField(default=0, required=True)  # 平均买入价
    quantity = IntField(default=0, required=True)  # 总持有数量
    accounts = ListField(StringField)  # 持有这个品种的账号
    users = ListField(StringField)  # 持有这个品种的用户
    updated_at = DateTimeField(modified=True)

    @property
    def num_users(self):
        return len(self.users)

    @property
    def num_accounts(self):
        return len(self.accounts)
예제 #10
0
class User(Document):
    """ 抢单用户 """
    class Meta:
        idf = IDFormatter('{mobile}')
        idx1 = Index('mobile', unique=True)
        idx2 = Index('username')

    mobile = StringField(required=True)
    username = StringField(required=True)
    password = StringField(required=True)
    owning = FloatField(default=0)  # 欠款
    paid = FloatField(default=0)  # 已结清
    num_exchanges = IntField()
    num_accounts = IntField()
    profit = FloatField()
    money = FloatField()
    capital = FloatField()
    earned = FloatField()  # 交易收入
    lost = FloatField()  # 交易亏损
    position = ListField(EmbeddedField(MyPosition))
    orders = ListField(EmbeddedField(MyOrder))
    order_status = ListField(EmbeddedField(MyStatus))

    _is_admin = BooleanField()

    def get_id(self):
        return self._id

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def is_authenticated(self):
        return True

    def is_admin(self):
        return self._is_admin
예제 #11
0
class Quote(Document):
    """ 交易行情信息 """
    class Meta:
        idf = IDFormatter('{exchange}_{symbol}_{quote_type}_{quote_at}')
        idx1 = Index(['exchange', 'symbol', 'quote_type', 'quote_at'],
                     unique=True)
        idx2 = Index(['exchange', 'quote_at'])

    exchange = StringField(required=True)  # 交易所ID(简称)
    symbol = StringField(required=True)  # 交易代码

    # 1m/5m/15m/30m/1h/4h/1d/1w/1m
    quote_type = StringField(required=True)
    quote_at = DateTimeField(required=True)  # 交易时间

    lclose = FloatField()  # 上次收盘价
    open_ = FloatField(required=True)  # 周期开盘价
    high = FloatField(required=True)  # 周期最高价
    low = FloatField(required=True)  # 周期最低价
    close = FloatField(required=True)  # 周期收盘价
    mean = FloatField()  # 周期均价
    volume = IntField(required=True)  # 周期成交量
    amount = FloatField(required=True)  # 周期成交额

    @classmethod
    def latest_price(cls, exchange, symbol):
        """ 获得品种的最新成交价

        从日线数据中取就可以了, 实时交易价格也会保存在日线中
        """
        today = datetime.utcnow() + timedelta(hours=8)
        today = today.replace(hour=0, minute=0, second=0, microsecond=0)
        q = cls.cached(300).query_one(
            {
                'exchange': exchange,
                'symbol': symbol,
                'quote_type': '1d',
                'quote_at': {
                    '$lte': today
                }
            }, {'close': 1},
            sort=[('quote_at', -1)])
        if q:
            return q.close

    @classmethod
    def increase(cls, exchange, symbol):
        """ 获得品种的今日涨幅 """
        now = datetime.utcnow() + timedelta(hours=8)
        if now.hour < 9 and now.minute < 30:
            now -= timedelta(days=1)
        date = now.replace(hour=0, minute=0, second=0, microsecond=0)
        q = cls.cached(300).query_one(
            {
                'exchange': exchange,
                'symbol': symbol,
                'quote_type': '1d',
                'quote_at': date
            }, {
                'close': 1,
                'lclose': 1
            })
        if q and q.lclose:
            return q.close / q.lclose - 1
예제 #12
0
class User(Document):
    """ 用户 """
    class Meta:
        idf = IDFormatter('{mobile}')
        idx1 = Index('mobile', unique=True)
        idx2 = Index('_is_admin')
        idx3 = Index('_is_active')
        idx4 = Index('username', unique=True)

    mobile = StringField(required=True)
    username = StringField()
    password = StringField()  # bcrypt hashed
    invited_by = StringField()  # 邀请人id
    ymoney = IntField(default=1000)  # Y币
    reserved_ymoney = IntField(default=0)  # 预扣Y币
    created_at = DateTimeField(created=True)
    last_login_at = DateTimeField(modified=True)
    auto_accounting = BooleanField(default=False)

    _is_active = BooleanField(default=False)  # 通过验证
    _is_admin = BooleanField(default=False)

    def add_to_admin(self):
        self._is_admin = True
        self.upsert()
        return True

    def activate(self):
        self._is_active = True
        self.upsert()

    def is_admin(self):
        return self._is_admin

    def is_active(self):
        return self._is_active

    def is_authenticated(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return self.mobile

    @classmethod
    def check_available(cls, mobile=None, username=None):
        u1 = cls.query_one({'mobile': mobile})
        u2 = cls.query_one({'username': username})
        if u1 or u2:
            return False
        else:
            return True

    @classmethod
    def create_user(cls, mobile, username, password, invited_by):
        if not cls.find_one({'_id': invited_by}):
            raise ValueError('请输入正确的邀请人手机号码')
        if not cls.check_available(mobile, username):
            raise ValueError('手机号码或用户名已被使用')
        u = cls({
            'mobile': mobile,
            'username': username,
            'password': cls.create_password(password),
            'invited_by': invited_by,
        })
        u.save()
        return u

    @classmethod
    def create_password(cls, password):
        return bcrypt.hashpw(password.encode('utf-8'),
                             bcrypt.gensalt()).decode('utf-8')

    def change_password(self, password):
        self.password = self.create_password(password)
        self.upsert()

    @classmethod
    def check_login(cls, mobile, password):
        u = cls.query_one({'mobile': mobile})
        password = password.encode('utf-8')
        if u:
            hashed = u.password.encode('utf-8')
            if bcrypt.hashpw(password, hashed) == hashed:
                return u
        return None
예제 #13
0
class Collection(Document):

    """ 收藏品: Stamp/Coin/Card """
    class Meta:
        idf = IDFormatter('{exchange}_{symbol}')
        idx1 = Index(['exchange', 'symbol'], unique=True)
        idx2 = Index('from_url')

    from_url = StringField()        # 来自哪个公告
    exchange = StringField(required=True)        # 交易所ID(简称)
    symbol = StringField(required=True)          # 交易代码
    name = StringField()                        # 交易名
    type_ = StringField(default="邮票")           # "邮票"/"钱币"/"卡片"
    status = StringField(default="申购中")          # "申购中"/"已上市"

    issuer = StringField()          # 发行机构
    texture = StringField()         # 材质
    price_forsale = FloatField()    # 挂牌参考价
    quantity_all = IntField()       # 挂牌总数量
    quantity_forsale = IntField()   # 限售总数

    offer_fee = FloatField(default=0.001)        # 申购手续费
    offer_quantity = IntField()     # 供申购数量 *
    offer_price = FloatField()      # 申购价格 *
    offer_accmax = IntField()       # 单账户最大中签数 *
    offer_overbuy = BooleanField()  # 是否可超额申购 *
    offer_cash_ratio = FloatField()  # 资金配售比例 *

    change_min = FloatField()       # 最小价格变动单位
    change_limit_1 = FloatField()   # 首日涨跌幅
    change_limit = FloatField()     # 正常日涨跌幅
    pickup_min = IntField()         # 最小提货量
    trade_limit = FloatField()      # 单笔最大下单量

    offers_at = DateTimeField()     # 申购日 *
    draws_at = DateTimeField()      # 抽签日 *
    trades_at = DateTimeField()     # 上市交易日 *

    invest_mv = FloatField()       # 申购市值(Market Value)
    invest_cash = FloatField()     # 申购资金 *
    invest_cash_return_ratio = FloatField()   # 资金中签率, 和上面那个二选一 *

    updated_at = DateTimeField(modified=True)

    @classmethod
    def get_name(cls, exchange, symbol):
        if not hasattr(cls, 'cache'):
            setattr(cls, 'cache', {})
        pair = (exchange, symbol)
        if not cls.cache or cls.cache.get('time', time.time()) < 3600:
            cls.cache = {(c.exchange, c.symbol): c.name
                         for c in cls.query({},
                                            {'exchange': 1,
                                             'symbol': 1,
                                             'name': 1,
                                             '_id': 0})}
            cls.cache['time'] = time.time()
        return cls.cache.get(pair)

    @classmethod
    def search(cls, name_or_abbr, limit=100):
        # warm cache
        cls.get_name('', '')
        na = name_or_abbr.upper()
        pairs = []
        exchanges = set()

        def add_name(name, pair):
            initials = py.get_initials(name, '')
            sna = set(na)
            sname = set(name)
            sinit = set(initials)

            if name.startswith(na) or initials.startswith(na):
                distance = min(max(len(sna - sname), len(sname - sna)),
                               max(len(sna - sinit), len(sinit - sna)))
                pairs.append((distance, pair))

        def add_exchange(name):
            initials = py.get_initials(name, '')
            sna = set(na)
            sname = set(name)
            sinit = set(initials)

            if name.startswith(na) or initials.startswith(na):
                distance = min(max(len(sna - sname), len(sname - sna)),
                               max(len(sna - sinit), len(sinit - sna)))
                for pair in cls.cache.keys():
                    if pair[0] == name:
                        pairs.append((distance, pair))

        for pair in cls.cache.keys():
            if pair == 'time':
                continue
            exchanges.add(pair[0])
            name = cls.get_name(*pair)
            add_name(name, pair)

        for name in exchanges:
            add_exchange(name)

        return [p[1] for p in sorted(pairs)][:limit]

    @property
    def abbr(self):
        return py.get_initials(self.name, '')

    @property
    def offer_mv(self):
        """ 申购市值配额 """
        if self.offer_quantity:
            return self.offer_quantity * self.offer_price * \
                (1 - self.offer_cash_ratio)

    @property
    def offer_cash(self):
        """ 申购资金配额 """
        try:
            return self.offer_quantity * self.offer_price * \
                self.offer_cash_ratio
        except:
            pass

    @property
    def offer_max_invest(self):
        """ 最大申购资金 """
        if self.offer_overbuy:
            return float('inf')
        else:
            return self.offer_mv + \
                self.offer_fee * self.offer_accmax

    @property
    def invest_cash_real(self):
        """ 申购资金(含估算) """
        if self.invest_cash:
            return self.invest_cash
        elif self.invest_cash_return_ratio and self.offer_cash:
            return self.offer_cash / self.invest_cash_return_ratio

    @property
    def result_ratio_cash(self):
        """ 资金中签率 """
        if self.invest_cash_return_ratio:
            return self.invest_cash_return_ratio
        if self.status == "已上市" and self.invest_cash:
            try:
                return self.offer_cash / self.invest_cash
            except:
                pass

    @property
    def result_ratio_mv(self):
        """ 市值中签率 """
        if self.status == "已上市" and self.invest_mv:
            return self.offer_mv * (1 - self.offer_cash_ratio) \
                / self.invest_mv

    @property
    def offer_min_invest(self):
        """ 必中最低申购资金 """
        if self.status == "已上市":
            if self.invest_cash or self.invest_cash_return_ratio:
                magnitude = math.ceil(math.log10(1 / self.result_ratio_cash))
                return self.offer_price * 10 ** magnitude \
                    * (1 + self.offer_fee)

    @property
    def cashout_at(self):
        """ 出金日期 """
        c = get_conf(self.exchange)
        return self.offset(c['cashout'])

    def offset(self, incr):
        c = get_conf(self.exchange)
        notrade = [int(x) for x in str(c['notrade'] or '').split(',') if x]
        delta = 1 if incr > 0 else -1
        odate = self.offers_at
        while incr != 0:
            incr -= delta
            odate += timedelta(days=delta)
            while odate.weekday() in notrade:
                odate += timedelta(days=delta)
        return odate

    @property
    def total_offer_cash(self):
        return sum(c.offer_cash or 0
                   for c in Collection.cached(300)
                   .query({'exchange': self.exchange,
                           'offers_at': self.offers_at}))

    @property
    def expected_invest_cash(self):
        ex = Exchange.find_exchange(self.exchange)
        if ex.expected_invest_cash and self.total_offer_cash:
            return (self.offer_cash or 0) / self.total_offer_cash \
                * ex.expected_invest_cash

    @property
    def expected_invest_mv(self):
        ex = Exchange.find_exchange(self.exchange)
        if ex.total_market_value:
            return ex.total_market_value

    @property
    def expected_result_mv_ratio(self):
        """ 预期市值中签率 """
        if not self.result_ratio_cash:
            if self.expected_invest_mv and self.offer_mv:
                return self.offer_mv / self.expected_invest_mv

    @property
    def expected_annual_profit(self):
        """ 预期年化收益率 """
        ex = Exchange.find_exchange(self.exchange)
        if ex.expected_invest_cash and ex.median_increase:
            return self.expected_result_cash_ratio * ex.median_increase

    @property
    def expected_result_cash_ratio(self):
        """ 预期资金中签率 """
        if not self.result_ratio_cash:
            ex = Exchange.find_exchange(self.exchange)
            if ex.expected_invest_cash:
                return self.total_offer_cash / ex.expected_invest_cash
        else:
            return self.result_ratio_cash