Exemplo n.º 1
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)
    paid = FloatField(default=0)
    total_money = FloatField(default=0)
    total_capital = FloatField(default=0)
    total_profit = FloatField(default=0)
    _is_admin = BooleanField(default=False)

    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
Exemplo n.º 2
0
class Account(Document):
    """ 抢单用户的账号 """
    class Meta:
        idf = IDFormatter('{user}_{exchange}_{login_name}')

    user = StringField(required=True)
    exchange = StringField(required=True)
    login_name = StringField(required=True)
    login_password = StringField(required=True)
    money_password = StringField()
    bank_password = StringField()

    collections = ListField(StringField, default=[])
    position = ListField(EmbeddedField(MyPosition))
    orders = ListField(EmbeddedField(MyOrder))
    order_status = ListField(EmbeddedField(MyStatus))
    money = FloatField()
    capital = FloatField()
    profit = FloatField()
    earned = FloatField()  # 交易收入
    lost = FloatField()  # 交易亏损

    @property
    def mobile(self):
        user = User.cached(60).query_one({'_id': self.user})
        return user.mobile

    @property
    def username(self):
        user = User.cached(60).query_one({'_id': self.user})
        return user.username
Exemplo n.º 3
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()
Exemplo n.º 4
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)
Exemplo n.º 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()  # 交易亏损
Exemplo n.º 6
0
class DailyTrading(Document):
    """ 当日账号汇总 """
    class Meta:
        idf = IDFormatter('{account}_{date}')
        idx1 = Index(['account', 'date'], unique=True)

    date = DateTimeField(required=True)
    account = StringField(required=True)
    position = ListField(EmbeddedField(MyPosition))
    orders = ListField(EmbeddedField(MyOrder))
    order_status = ListField(EmbeddedField(MyStatus))
    profit = FloatField()
    money = FloatField()
    capital = FloatField()
    earned = FloatField()  # 交易收入
    lost = FloatField()  # 交易亏损
Exemplo n.º 7
0
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)
Exemplo n.º 8
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%'
Exemplo n.º 9
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)
Exemplo n.º 10
0
class Summary(Document):
    """ 各种组合的汇总信息

    总资金/总持仓/总浮盈
    """
    class Meta:
        idf = IDFormatter('{exchange}_{user}_{collection}')
        idx1 = Index('exchange')
        idx2 = Index('user')
        idx3 = Index('collection')

    exchange = StringField(default='', required=True)
    user = StringField(default='', required=True)
    collection = StringField(default='', required=True)

    position = ListField(EmbeddedField(MyPosition))
    orders = ListField(EmbeddedField(MyOrder))
    order_status = ListField(EmbeddedField(MyStatus))
    money = FloatField()
    capital = FloatField()
    profit = FloatField()
    earned = FloatField()  # 交易收入
    lost = FloatField()  # 交易亏损
Exemplo n.º 11
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)
Exemplo n.º 12
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
Exemplo n.º 13
0
class Position(Document):
    """ 用户持仓信息 """
    class Meta:
        idx1 = Index(['user', 'exchange', 'symbol'], unique=True)

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

    user = StringField(required=True)
    quantity = FloatField(required=True, default=0)

    @classmethod
    def num_exchanges(cls, user):
        """ 用户持有多少个交易所的持仓 """
        return len(
            set(p.exchange
                for p in cls.query({'user': user}, {'exchange': 1})))

    @classmethod
    def num_collections(cls, user):
        """ 用户持有多少不同的藏品 """
        return len(
            list(cls.query({
                'user': user,
                'quantity': {
                    '$gt': 0
                }
            }, {'_id': 1})))

    @classmethod
    def num_sold(cls, user):
        """ 用户已卖出过多少个藏品 """
        return len(
            set('{exchange}_{symbol}'.format(**t)
                for t in Transaction.find({
                    'user': user,
                    'type_': 'sell'
                }, {
                    'exchange': 1,
                    'symbol': 1
                })))

    @classmethod
    def user_position(cls, user):
        """ 目前持仓概况, cached """
        cachekey = 'pcache_{}'.format(user)
        if not hasattr(cls, cachekey):
            setattr(cls, cachekey, {})

        now = time.time()
        cache = getattr(cls, cachekey)
        if 'position' not in cache or \
                cache.get('time', now) < now - 5:
            cache['position'] = cls._user_position(user)
            cache['time'] = time.time()
        return copy.deepcopy(cache['position'])

    @classmethod
    def _user_position(cls, user):
        """ 目前持仓概况 """
        collections = {}
        for p in cls.query({'user': user}):
            pair = (p.exchange, p.symbol)
            collections.setdefault(pair, 0)
            collections[pair] += p.quantity
        buy_info = {}
        for t in Transaction.query({
                'user': user,
                'type_': 'buy'
        },
                                   sort=[('operated_at', 1)]):
            pair = (t.exchange, t.symbol)
            if pair in collections:
                buy_info.setdefault(pair, [0, 0, None])
                buy_info[pair][0] += t.quantity
                buy_info[pair][1] += t.quantity * t.price
                if not buy_info[pair][2]:
                    buy_info[pair][2] = t.operated_at
        sell_info = {}
        for t in Transaction.query({
                'user': user,
                'type_': 'sell'
        },
                                   sort=[('operated_at', -1)]):
            pair = (t.exchange, t.symbol)
            if pair in collections:
                sell_info.setdefault(pair, [0, 0, None])
                sell_info[pair][0] += t.quantity
                sell_info[pair][1] += t.quantity * t.price
                if not sell_info[pair][2]:
                    sell_info[pair][2] = t.operated_at
        position = []
        for pair, quantity_amount in buy_info.items():
            exchange, symbol = pair
            quantity, amount, first_buy_at = quantity_amount
            if quantity:
                avg_buy_price = amount / quantity
                realized_profit = 0
                last_sell_at = datetime.utcnow()
                if pair in sell_info:
                    quantity2, amount2, last_sell_at = sell_info[pair]
                    quantity -= quantity2
                    realized_profit = amount2 - avg_buy_price * quantity2

                if quantity != collections[pair]:
                    cls.update_one(
                        {
                            'exchange': pair[0],
                            'symbol': pair[1],
                            'user': user
                        }, {'$set': {
                            'quantity': quantity
                        }})
                latest_price = Quote.latest_price(exchange, symbol)
                increase = Quote.increase(exchange, symbol)
                if not latest_price:
                    latest_price = avg_buy_price
                unrealized_profit = (latest_price - avg_buy_price) * quantity
                past_days = max(1, (last_sell_at - first_buy_at).days)
                annual_profit = (365 / past_days) * \
                    (unrealized_profit + realized_profit)
                position.append({
                    'exchange':
                    exchange,
                    'symbol':
                    symbol,
                    'name':
                    Collection.get_name(exchange, symbol),
                    'avg_buy_price':
                    avg_buy_price,
                    'quantity':
                    quantity,
                    'latest_price':
                    latest_price,
                    'increase':
                    increase,
                    'total_increase':
                    latest_price / avg_buy_price -
                    1 if avg_buy_price > 0 else 100,
                    'realized_profit':
                    realized_profit,
                    'unrealized_profit':
                    unrealized_profit,
                    'annual_profit':
                    annual_profit,
                })
        return sorted(position, key=lambda x: x['exchange'])

    @classmethod
    def average_increase(cls, user):
        """ 平均涨幅 """
        position = [p for p in cls.user_position(user) if p['quantity'] > 0]
        if len(position):
            return sum(p['total_increase'] for p in position) / len(position)

    @classmethod
    def market_value(cls, user):
        """ 持仓总市值 """
        position = cls.user_position(user)
        return sum(p['latest_price'] * p['quantity'] for p in position)

    @classmethod
    def unrealized_profit(cls, user):
        """ 未实现收益(浮盈) """
        return sum(p['unrealized_profit'] for p in cls.user_position(user))

    @classmethod
    def realized_profit(cls, user):
        """ 已实现收益 """
        return sum(p['realized_profit'] for p in cls.user_position(user))

    @classmethod
    def annual_profit(cls, user):
        """ 年化收益 """
        return sum(p['annual_profit'] for p in cls.user_position(user))

    @classmethod
    def do_op(cls, t, reverse=False):
        c = cls.query_one({
            'user': t.user,
            'exchange': t.exchange,
            'symbol': t.symbol
        })
        if not c:
            if reverse:
                return False
            elif t.type_ == 'buy':
                c = cls({
                    'user': t.user,
                    'exchange': t.exchange,
                    'symbol': t.symbol,
                    'quantity': t.quantity
                })
            else:
                return False
        elif t.type_ == 'buy':
            c.quantity += t.quantity
        elif t.type_ == 'sell':
            c.quantity -= t.quantity

        if c.quantity >= 0:
            c.upsert()
            return True
        else:
            return False
Exemplo n.º 14
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
Exemplo n.º 15
0
class MyMoney(EmbeddedDocument):
    """ 资金 """
    usable = FloatField(required=True)
    withdrawable = FloatField(required=True)
Exemplo n.º 16
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
Exemplo n.º 17
0
class ProfitLog(Document):
    """ 用户的收益日志 """
    class Meta:
        idx1 = Index(['user', 'date'], unique=True)

    user = StringField(required=True)
    date = DateTimeField(required=True)
    profit = FloatField()

    @classmethod
    def profits(cls, user):
        """ 获得收益日志 """
        return [{
            'date': pl.date,
            'profit': pl.profit
        } for pl in cls.query({'user': user}, sort=[('date', 1)])]

    @classmethod
    def ensure_all_profits(cls):
        for u in User.query():
            cls.ensure_profits(u._id)

    @classmethod
    def ensure_profits(cls, user):
        """ 确保生成收益日志 """
        log.info('为用户{}生成收益日志'.format(user))
        ts = list(reversed(Transaction.user_recent_transactions(user)))
        if ts:
            today = datetime.utcnow() + timedelta(hours=8)
            today = today.replace(hour=0, minute=0, second=0, microsecond=0)
            di = 0
            date = ts[di].operated_at
            positions = {}
            realized_profits = defaultdict(float)
            while date <= today:
                profit = 0
                # include new transactions
                while di < len(ts) and ts[di].operated_at <= date:
                    t = ts[di]
                    op = 1 if t.type_ == 'buy' else -1
                    pair = (t.exchange, t.symbol)
                    if pair not in positions:
                        positions[pair] = (t.price, t.quantity * op)
                    else:
                        pv = positions[pair]
                        amount = pv[0] * pv[1] + t.price * t.quantity * op
                        quantity = pv[1] + t.quantity * op
                        if quantity == 0:
                            realized_profits[pair] += pv[1] * (t.price - pv[0])
                            del positions[pair]
                        else:
                            positions[pair] = (amount / quantity, quantity)
                    di += 1

                # calculate profit
                for pair in positions:
                    q = Quote.query_one(
                        {
                            'exchange': pair[0],
                            'symbol': pair[1],
                            'quote_type': '1d',
                            'quote_at': {
                                '$lte': date
                            }
                        },
                        sort=[('quote_at', -1)])
                    if q:
                        pv = positions[pair]
                        profit += (q.close - pv[0]) * pv[1]

                profit += sum(realized_profits.values())

                # update profit
                cls.update_one({
                    'date': date,
                    'user': user
                }, {'$set': {
                    'profit': profit
                }},
                               upsert=True)
                date += timedelta(days=1)