예제 #1
0
    def load_account(self) -> AccountStats:
        records = AccountStats.query_data(
            filters=[AccountStats.trader_name == self.trader_name],
            order=AccountStats.timestamp.desc(),
            limit=1,
            return_type='domain')
        if not records:
            return self.account
        latest_record: AccountStats = records[0]

        # create new orm object from latest record
        account_dict = account_stats_schema.dump(latest_record)
        del account_dict['id']
        del account_dict['positions']
        account = AccountStats()
        fill_domain_from_dict(account, account_dict)

        positions: List[Position] = []
        for position_domain in latest_record.positions:
            position_dict = position_schema.dump(position_domain)
            self.logger.debug('current position:{}'.format(position_dict))
            del position_dict['id']
            del position_dict['account_stats']
            position = Position()
            fill_domain_from_dict(position, position_dict)
            positions.append(position)

        account.positions = positions

        return account
예제 #2
0
    def persist_account(self, timestamp):
        """
        save the account to db,we do this after closing time every day

        :param timestamp:
        :type timestamp:
        """
        the_id = '{}_{}'.format(self.trader_name,
                                to_time_str(timestamp, TIME_FORMAT_ISO8601))
        positions = []
        for position in self.latest_account['positions']:
            position_domain = Position()
            fill_domain_from_dict(position_domain, position, None)

            position_domain.id = '{}_{}_{}'.format(
                self.trader_name, position['entity_id'],
                to_time_str(timestamp, TIME_FORMAT_ISO8601))
            position_domain.timestamp = to_pd_timestamp(timestamp)
            position_domain.account_stats_id = the_id

            positions.append(position_domain)

        account_domain = AccountStats(
            id=the_id,
            entity_id=f'trader_zvt_{self.trader_name}',
            trader_name=self.trader_name,
            cash=self.latest_account['cash'],
            positions=positions,
            all_value=self.latest_account['all_value'],
            value=self.latest_account['value'],
            timestamp=to_pd_timestamp(self.latest_account['timestamp']))

        self.logger.info('persist_account:{}'.format(
            account_stats_schema.dump(account_domain)))

        self.session.add(account_domain)
        self.session.commit()
예제 #3
0
    def order(self,
              entity_id,
              current_price,
              current_timestamp,
              order_amount=0,
              order_pct=1.0,
              order_price=0,
              order_type=ORDER_TYPE_LONG,
              order_money=0):
        """
        下单

        Parameters
        ----------
        entity_id : str
            交易标的id

        current_price : float
            当前价格

        current_timestamp: timestamp
            下单的时间

        order_amount : int
            数量

        order_pct : float
            使用可用现金(仓位)的百分比,0.0-1.0

        order_price : float
            用于限价交易

        order_type : {ORDER_TYPE_LONG,ORDER_TYPE_SHORT,ORDER_TYPE_CLOSE_LONG,ORDER_TYPE_CLOSE_SHORT}
            交易类型

        Returns

        """

        # 市价交易,就是买卖是"当时"并"一定"能成交的
        # 简单起见,目前只支持这种方式
        if order_price == 0:
            current_position = self.get_current_position(entity_id=entity_id)

            if not current_position:
                trading_t = self.entity_schema.get_trading_t()
                current_position = Position(trader_name=self.trader_name,
                                            entity_id=entity_id,
                                            long_amount=0,
                                            available_long=0,
                                            average_long_price=0,
                                            short_amount=0,
                                            available_short=0,
                                            average_short_price=0,
                                            profit=0,
                                            value=0,
                                            trading_t=trading_t)
                # add it to latest account
                self.account.positions.append(current_position)

            # 按钱交易
            if order_money > 0:
                # 开多
                if order_type == ORDER_TYPE_LONG:
                    if current_position.short_amount > 0:
                        raise InvalidOrderError(
                            "close the short position before open long")

                    if order_money > self.account.cash:
                        if self.rich_mode:
                            self.input_money()
                        else:
                            raise NotEnoughMoneyError()

                    cost = current_price * (1 + self.slippage + self.buy_cost)
                    # 买的数量
                    order_amount = order_money // cost

                    if order_amount < 1:
                        self.logger.error(
                            f'invalid order_money:{order_money}, cost:{cost}, order_amount:{order_amount}'
                        )
                        return

                    self.update_position(current_position, order_amount,
                                         current_price, order_type,
                                         current_timestamp)
                # 开空
                elif order_type == ORDER_TYPE_SHORT:
                    if current_position.long_amount > 0:
                        raise InvalidOrderError(
                            "close the long position before open short")

                    if order_money > self.account.cash:
                        if self.rich_mode:
                            self.input_money()
                        else:
                            raise NotEnoughMoneyError()

                    cost = current_price * (1 + self.slippage + self.buy_cost)

                    order_amount = order_money // cost

                    if order_amount < 1:
                        self.logger.error(
                            f'invalid order_money:{order_money}, cost:{cost}, order_amount:{order_amount}'
                        )
                        return
                    self.update_position(current_position, order_amount,
                                         current_price, order_type,
                                         current_timestamp)
                else:
                    raise InvalidOrderParamError(
                        'close long/short not support order_money')

            # 按数量交易
            elif order_amount > 0:
                # 开多
                if order_type == ORDER_TYPE_LONG:
                    if current_position.short_amount > 0:
                        raise InvalidOrderError(
                            "close the short position before open long")

                    self.update_position(current_position, order_amount,
                                         current_price, order_type,
                                         current_timestamp)
                # 开空
                elif order_type == ORDER_TYPE_SHORT:
                    if current_position.long_amount > 0:
                        raise InvalidOrderError(
                            "close the long position before open short")

                    self.update_position(current_position, order_amount,
                                         current_price, order_type,
                                         current_timestamp)
                # 平多
                elif order_type == ORDER_TYPE_CLOSE_LONG:
                    if current_position.available_long >= order_amount:
                        self.update_position(current_position, order_amount,
                                             current_price, order_type,
                                             current_timestamp)
                    else:
                        raise NotEnoughPositionError()
                # 平空
                elif order_type == ORDER_TYPE_CLOSE_SHORT:
                    if current_position.available_short >= order_amount:
                        self.update_position(current_position, order_amount,
                                             current_price, order_type,
                                             current_timestamp)
                    else:
                        raise Exception("not enough position")

            # 按仓位比例交易
            elif 0 < order_pct <= 1:
                # 开多
                if order_type == ORDER_TYPE_LONG:
                    if current_position.short_amount > 0:
                        raise InvalidOrderError(
                            "close the short position before open long")

                    cost = current_price * (1 + self.slippage + self.buy_cost)
                    want_pay = self.account.cash * order_pct
                    # 买的数量
                    order_amount = want_pay // cost

                    if order_amount < 1:
                        if self.rich_mode:
                            self.input_money()
                            order_amount = max(
                                (self.account.cash * order_pct) // cost, 1)
                        else:
                            raise NotEnoughMoneyError()
                    self.update_position(current_position, order_amount,
                                         current_price, order_type,
                                         current_timestamp)
                # 开空
                elif order_type == ORDER_TYPE_SHORT:
                    if current_position.long_amount > 0:
                        raise InvalidOrderError(
                            "close the long position before open short")

                    cost = current_price * (1 + self.slippage + self.buy_cost)
                    want_pay = self.account.cash * order_pct

                    order_amount = want_pay // cost

                    if order_amount < 1:
                        if self.rich_mode:
                            self.input_money()
                            order_amount = max(
                                (self.account.cash * order_pct) // cost, 1)
                        else:
                            raise NotEnoughMoneyError()

                    self.update_position(current_position, order_amount,
                                         current_price, order_type,
                                         current_timestamp)

                # 平多
                elif order_type == ORDER_TYPE_CLOSE_LONG:
                    if current_position.available_long > 0:
                        if order_pct == 1.0:
                            order_amount = current_position.available_long
                        else:
                            order_amount = math.floor(
                                current_position.available_long * order_pct)

                        if order_amount != 0:
                            self.update_position(current_position,
                                                 order_amount, current_price,
                                                 order_type, current_timestamp)
                        else:
                            self.logger.warning(
                                f'{entity_id} available_long:{current_position.available_long} order_pct:{order_pct} order_amount:{order_amount}'
                            )
                    else:
                        raise NotEnoughPositionError()
                # 平空
                elif order_type == ORDER_TYPE_CLOSE_SHORT:
                    if current_position.available_short > 0:
                        if order_pct == 1.0:
                            order_amount = current_position.available_short
                        else:
                            order_amount = math.floor(
                                current_position.available_short * order_pct)

                        if order_amount != 0:
                            self.update_position(current_position,
                                                 order_amount, current_price,
                                                 order_type, current_timestamp)
                        else:
                            self.logger.warning(
                                f'{entity_id} available_long:{current_position.available_long} order_pct:{order_pct} order_amount:{order_amount}'
                            )
                    else:
                        raise Exception("not enough position")