def dump_orders(orders, s, die=True): if len(orders) > 0: # print "\n", s logger.info( "order|%10s|%2s|%8s|%10s|%10s|%8s|%10s|%8s|%7s|%10s|%10s|%10s|%10s|%10s" % ("order_id", 'op', 'fund_id', 'place_date', 'place_time', 'amount', 'share', 'fee', 'nav', 'nav_date', 'ack_date', 'ack_amount', 'ack_share', 'share_id')) for o in orders: # (order_id, fund_id, fund_code, op, place_date, place_time, amount, share, fee, nav, nav_date, ack_date, ack_amount, ack_share, div_mode) logger.info( "order|%s|%2d|%d|%s|%10s|%8.6f|%10.6f|%6.6f|%7.4f|%s|%s|%10.6f|%10.6f|%10s" % (o['order_id'], o['op'], o['fund_id'], o['place_date'].strftime("%Y-%m-%d"), o['place_time'], o['amount'], o['share'], o['fee'], o['nav'], o['nav_date'].strftime("%Y-%m-%d"), o['ack_date'].strftime("%Y-%m-%d"), o['ack_amount'], o['ack_share'], o['share_id'])) if die: dd('----end orders----')
def dump_event(x, e, die=False): dt, op, order_id, fund_id, argv = e if op == 0: s = "nav:%.4f, nav_date:%s" % (argv['nav'], argv['nav_date'].strftime("%Y-%m-%d")) elif op == 1: s = "at:%s %s, share:%.4f, nav:%.4f, fee:%.2f" % ( argv['place_date'].strftime("%Y-%m-%d"), argv['place_time'], argv['share'], argv['nav'], argv['fee']) elif op == 11: s = "ack_date:%s, share:%.4f " % ( argv['ack_date'].strftime("%Y-%m-%d"), argv['share']) elif op == 2: s = "at:%s %s, share:%.4f, nav:%.4f, fee:%.2f" % ( argv['place_date'].strftime("%Y-%m-%d"), argv['place_time'], argv['share'], argv['nav'], argv['fee']) elif op == 12: s = "ack_date:%s, share:%.4f " % ( argv['ack_date'].strftime("%Y-%m-%d"), argv['share']) elif op == 8: s = '' elif op == 15: s = '' elif op == 16: s = '' elif op == 17: s = '' elif op == 18: s = '' elif op == 100: s = '' elif op == 101: s = '' logger.info( "%c xev|%20s|%3d|%10s|%10s|%s" % (x, dt.strftime("%Y-%m-%d %H:%M:%S"), op, order_id, fund_id, s)) if die: dd('----end events----')
def process(self, ev): result = [] # 事件类型:0:净值更新;1:申购;2:赎回;3:分红;8:调仓;11:申购确认;12:赎回到账;15:分红登记;16:分红除息;17:分红派息;18:基金分拆; # 100:例行事件生成;101:记录当前持仓; dt, op, order_id, fund_id, argv = ev # # 更新模拟时钟 # if dt >= self.ts: self.ts = dt else: #dd("SNH: ev out of date", self.ts, ev) pass if op == 0: # # 净值更新 # if fund_id in self.df_share.index.levels[0]: # if dt.strftime("%Y-%m-%d") == '2014-11-11' and fund_id == 30000237: # pdb.set_trace() # df = self.df_share.query('fund_id in [%d]' % fund_id) df = self.df_share.loc[self.df_share.index.get_level_values(0) == fund_id] self.df_share.loc[fund_id, 'yield'] = (df['share'] + df['share_buying'] ) * (argv['nav'] - df['nav']) self.df_share.loc[fund_id, 'nav'] = argv['nav'] self.df_share.loc[fund_id, 'nav_date'] = argv['nav_date'] else: dd("SNH: fund_id not in df_share.index", fund_id, self.df_share) elif op == 1: # # 申购 # self.cash -= argv['share'] * argv['nav'] + argv['fee'] buy_date = argv['nav_date'] df_tmp = self.make_share(fund_id, order_id, argv['share'], buy_date, argv['nav'], argv['nav_date'], argv['ack_date'], argv['div_mode']) self.df_share = self.df_share.append(df_tmp) # 记录购买费率 self.dt_today_fee_buy[fund_id] = self.dt_today_fee_buy.setdefault( fund_id, 0) + (-argv['fee']) # # 生成申购确认事件 # if argv['ack_date'].date() == dt.date(): hours = 19 else: hours = 2 ev2 = (argv['ack_date'] + timedelta(hours=hours), 11, order_id, fund_id, argv) result.append(ev2) elif op == 11: # # 申购确认 # if argv['order_id'] in self.df_share.index.get_level_values(1): self.df_share.loc[(fund_id, order_id), 'share'] = self.df_share.loc[(fund_id, order_id), 'share_buying'] self.df_share.loc[(fund_id, order_id), 'share_buying'] = 0 elif op == 2: # # 赎回 # left = self.df_share.loc[(fund_id, argv['share_id']), 'share'] - argv['share'] self.df_share.loc[(fund_id, argv['share_id']), 'share'] = left if left > 0.00000099 else 0 # 生成赎回上下文 row = (fund_id, argv['order_id'], argv['share_id'], argv['share'], argv['nav'], argv['nav_date'], argv['ack_date'], argv['fee']) tmp = pd.DataFrame([row], columns=[ 'fund_id', 'redeem_id', 'share_id', 'share', 'nav', 'nav_date', 'ack_date', 'fee' ]) self.df_redeem = self.df_redeem.append( tmp.set_index(['fund_id', 'redeem_id'])) # 记录赎回费率 self.dt_today_fee_redeem[ fund_id] = self.dt_today_fee_redeem.setdefault( fund_id, 0) + (-argv['fee']) # # 生成赎回确认事件 # if argv['ack_date'].date() == dt.date(): hours = 19 else: hours = 2 ev2 = (argv['ack_date'] + timedelta(hours=hours), 12, order_id, fund_id, argv) result.append(ev2) elif op == 12: # # 赎回确认 # # pdb.set_trace() self.cash += argv['share'] * argv['nav'] - argv['fee'] self.df_redeem.drop((fund_id, argv['order_id']), inplace=True) elif op == 8: # # 调仓处理 # # df_share: 是当前状态;argv['pos']: 是目标状态 # # 调仓逻辑里面,首先取消掉所有未提交的订单,然后在根据当前状态 # 重新生成订单 # self.remove_flying_op() orders = self.adjust(dt, argv['pos']) # # 将订单插入事件队列 # for order in orders: argv = order if order['op'] == 1: ev2 = (order['nav_date'] + timedelta(hours=18), 1, order['order_id'], order['fund_id'], argv) else: ev2 = (order['nav_date'] + timedelta(hours=18, minutes=30), 2, order['order_id'], order['fund_id'], argv) result.append(ev2) self.orders.extend(orders) elif op == 15: # # 基金分红:权益登记 # # [XXX] 这里啰嗦几句基金分红的处理:基金的分红方式有两种, # 现金分红和红利再投。 # # 对于红利再投的方式,再投份额的折算发生在除息日,按照除息 # 日的净值,但该部分份额不可赎回,直到派息日方可赎回。 # # 对于现金分红方式,除息日扣除相应的净值,派息日打款 # # df = self.df_share.loc[[fund_id]] df['share_bonusing'] = df['share'] + df['share_buying'] # if dt.strftime("%Y-%m-%d") == '2013-05-16': # pdb.set_trace() # # 生成分红订单,记录分红操作 # share_dividend = df.loc[df['div_mode'] == 1, 'share_bonusing'].sum() share_cash = df['share_bonusing'].sum() - share_dividend orders, dividend_id, cash_id = [], "0", "0" if share_dividend > 0.000000001: order = self.make_bonus_order(dt, fund_id, share_dividend, argv, 1) orders.append(order) dividend_id = order['order_id'] if share_cash > 0.000000001: order = self.make_bonus_order(dt, fund_id, share_cash, argv, 0) orders.append(order) cash_id = order['order_id'] if orders: dump_orders(orders, "bonus %s" % dt.strftime("%Y-%m-%d"), False) self.orders.extend(orders) self.df_share.loc[[fund_id]] = df # dd(orders, argv, self.df_share.loc[[fund_id]], dt, share_dividend, share_cash, df) # # 调度除息事件 # share = df['share_bonusing'].sum() if share > 0.000000001: argv2 = { 'share': share, 'bonus_ratio': argv['bonus_ratio'], 'bonus_nav': argv['bonus_nav'], 'bonus_nav_date': argv['bonus_nav_date'], 'payment_date': argv['payment_date'], 'order_dividend': dividend_id, 'order_cash': cash_id, } ev2 = (argv['dividend_date'] + timedelta(hours=16, minutes=30), 16, 0, fund_id, argv2) result.append(ev2) elif op == 16: # # 基金分红:除息 # # pdb.set_trace() df = self.df_share.loc[[fund_id]] df['amount_bonusing'] = df['share_bonusing'] * argv['bonus_ratio'] ev2 = (argv['payment_date'] + timedelta(hours=16, minutes=45), 17, 0, fund_id, argv) result.append(ev2) # 记录分红业绩 self.dt_today_bonus[fund_id] = self.dt_today_bonus.setdefault( fund_id, 0) + df['amount_bonusing'].sum() # # 处理红利再投的份额转换 # mask = (df['div_mode'] == 1) share = df.loc[mask, 'amount_bonusing'].sum() / argv['bonus_nav'] # # 清除红利再投上下文 # df.loc[mask, 'share_bonusing'] = 0 df.loc[mask, 'amount_bonusing'] = 0 self.df_share.loc[[fund_id]] = df # # 生成再投份额 # df_tmp = self.make_share(fund_id, argv['order_dividend'], share, pd.to_datetime(dt.date()), argv['bonus_nav'], argv['bonus_nav_date'], argv['payment_date'], 1) self.df_share = self.df_share.append(df_tmp) # tmp = { # 'share': 0.0, # 'yield': 0.0, # 'share_buying': share, # 'nav': argv['bonus_nav'], # 'nav_date': argv['bonus_nav_date'], # 'buy_date': pd.to_datetime(dt.date()), # 'ack_date': argv['payment_date'], # 'share_bonusing': 0.0, # 'amount_bonusing': 0.0, # 'div_mode': 1, # } # self.df_share.loc[(fund_id, argv['order_dividend']), :] = tmp elif op == 17: # pdb.set_trace() # # 基金分红:派息 # if fund_id in self.df_share.index.get_level_values(0): df = self.df_share.loc[[fund_id]] # # 现金分红 # mask = (df['div_mode'] == 0) if mask.any(): self.cash_bounused = df.loc[mask, 'amount_bonusing'].sum() # # 清除现金分红上下文 # df.loc[mask, 'share_bonusing'] = 0 df.loc[mask, 'amount_bonusing'] = 0 # # 红利再投, 确认红利再投份额 # if argv['order_dividend'] in df.index.get_level_values(1): df.loc[(fund_id, argv['order_dividend']), 'share'] = df.loc[(fund_id, argv['order_dividend']), 'share_buying'] df.loc[(fund_id, argv['order_dividend']), 'share_buying'] = 0 self.df_share.loc[[fund_id]] = df elif op == 18: # # 基金分拆 # df = self.df_share.loc[[fund_id]] df['share'] *= argv['ra_split_proportion'] df['share_buying'] *= argv['ra_split_proportion'] # # 理论上此时的净值为昨天的净值, 因为分拆是在调整今天净值之前处理的. # # 要保证后面日收益计算的正确, 这个地方需要对净值进行折算. # df['nav'] /= argv['ra_split_proportion'] self.df_share.loc[[fund_id]] = df elif op == 100: # # 当日持仓相关的例行事件 # result.extend(self.share_routine(dt, argv)) elif op == 101: # if dt.strftime("%Y-%m-%d") in ['2014-02-10']: # pdb.set_trace() day = pd.to_datetime(dt.date()) # # 记录持仓 # if self.debug and self.df_share.sum().sum() > 0.000099: self.dt_holding[dt] = self.df_share.copy() # # 记录净值 # amount = ( (self.df_share['share'] + self.df_share['share_buying']) * self.df_share['nav'] + self.df_share['amount_bonusing']).sum() amount += (self.df_redeem['share'] * self.df_redeem['nav'] - self.df_redeem['fee']).sum() + self.cash self.nav[day] = amount # # 记录业绩归因 # sr_yield = self.df_share['yield'].groupby(level=0).sum() sr_yield = sr_yield.loc[sr_yield.abs() > 0.00000099] sr_fee_buy = pd.Series(self.dt_today_fee_buy) sr_fee_redeem = pd.Series(self.dt_today_fee_redeem) sr_bonus = pd.Series(self.dt_today_bonus) sr_contrib = pd.concat({ 0: sr_yield, 1: sr_fee_buy, 2: sr_fee_redeem, 3: sr_bonus }) sr_contrib.index.names = ['ra_return_type', 'ra_fund_id'] self.contrib[day] = sr_contrib #print "%s: %.6f / %.6f" % (day.strftime("%Y-%m-%d"), self.cash + 0.000000001, amount) # # 删除无用持仓 # mask = (self.df_share['share'] + self.df_share['share_buying'] + self.df_share['amount_bonusing']) < 0.000000001 if mask.any(): self.df_share.drop(self.df_share[mask].index, inplace=True) # self.idebug(dt) return result