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
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()
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")