def Divide(otv1, otv2): otv1 = otv1.asMatrix() otv2 = otv2.asMatrix() try: common_index = np.intersect1d(otv1.index, otv2.index) otv1 = otv1.reindex(common_index) otv2 = otv2.reindex(common_index) col_num1 = len(otv1.columns) col_num2 = len(otv2.columns) if col_num1 == 1 and otv1.columns[0] == gsUtils.getGodGid( ) and col_num2 != 1: tmp = np.tile(otv1, col_num2) output = tmp / otv2 elif col_num1 != 1 and col_num2 == 1 and otv2.columns[ 0] == gsUtils.getGodGid(): tmp = np.tile(otv2, col_num1) output = otv1 / tmp else: output = otv1 / tmp except: output = otv1 / otv2 try: output = output.dropna(axis=[0, 1], how='all') except: pass return output
def convex_optimizer(context,mode,position_limit,forecast_return,original_portfolio,target_risk,target_return,X,covariance_matrix,delta,constraint): ''' optimize fund weight target on different constraints, objective, based on target type and mode, fund return target, fund weight, group weight, etc. Parameters ---------- mode: dictionary target optimization type({type: mode}) 0: minimum risk. 1: minimum risk subject to target return. 2: maximum return subject to target risk. original_portfolio: OOTV input original waiting for optimization forecast_return: Dataframe, OTV, asset return for all symbols. index=date, O: asset names, V: asset return. target_return: double Target return for portfolio respected to benchmark. target_risk: double Portfolio risk tolerance whose objective is maximum return. cov_matrix: OOTV covariance matrix from risk model if holdings are stocks. X: pandas panel factor exposure delta: OOTV specific risk, diagonal matrix constraint: dictionaries tuples dictionary: OOTV, OTVV Returns ------- df_result: DataFrame Optimized value of weight. Index: target date. Columns: assets names. ''' # create logger logger = logging.getLogger() handler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s') handler.setFormatter(formatter) if not logger.handlers: logger.addHandler(handler) logger.setLevel(logging.DEBUG) # convert gft table to pandas dataframe if isinstance(original_portfolio, gftIO.GftTable): original_portfolio = original_portfolio.asColumnTab() if isinstance(forecast_return, gftIO.GftTable): forecast_return = forecast_return.asMatrix() if isinstance(covariance_matrix, gftIO.GftTable): covariance_matrix = covariance_matrix.asColumnTab().copy() # extra action in case the index is set as date in the function asColumnTab try: covariance_matrix.set_index('date', inplace=True) except: pass if isinstance(delta, gftIO.GftTable): delta = delta.asMatrix() all_factors_gid = covariance_matrix['factorid1'].unique() df_industries_asset_weight = original_portfolio.dropna( axis=0, subset=['industry', 'symbol'], how='any') datetime_index = pd.DatetimeIndex(df_industries_asset_weight['date'].unique()) target_date = datetime_index[0] # get unique symbols from the portfolio unique_symbol = df_industries_asset_weight['symbol'].unique() # create dataframe for output df_opts_weight = pd.DataFrame(data=np.nan, columns=unique_symbol, index=datetime_index) df_opts_status = pd.DataFrame(data=np.nan, columns=gsUtils.getGodGid(), index=datetime_index) for target_date in datetime_index: logger.debug('target date: %s', target_date) # only select those intersection assets between unique symbol and symbols in delta on target date. try: target_assets = delta.loc[target_date].index.intersection(unique_symbol) except KeyError as e: logger.debug(e.args) # fill the weight with previous value if error. df_opts_weight.fillna(method='pad', inplace=True) df_opts_status.loc[target_date] = gsConst.Const.Infeasible continue # select the number of position limit ranked symbols by requested mode. if mode == gsConst.Const.MinimumRiskUnderReturn: target_assets = forecast_return.loc[:target_date, target_assets].fillna('pad').std().sort_values(ascending=False)[:position_limit].index else: target_assets = forecast_return.loc[target_date,target_assets].sort_values(ascending=False)[:position_limit].index noa = len(target_assets) logger.debug('target assets: %s', target_assets.shape) # use the mean return prior target date as the predicted return temperarily # will use the forecasted return as ultimate goal rets_mean = forecast_return.loc[target_date, target_assets] # get delta on the target date, which is a diagonal matrix diag = delta.loc[target_date, target_assets] delta_on_date = pd.DataFrame(np.diag(diag), index=diag.index, columns=diag.index).fillna(0) # get covariance matrix, re-index from the list of all factors' gid cov_matrix = covariance_matrix.loc[target_date] cov_matrix = cov_matrix.pivot(index='factorid1', columns='factorid2', values='value') cov_matrix = cov_matrix.reindex(all_factors_gid, all_factors_gid, fill_value=np.nan) # big X is sigma in the quadratic equation, size = 35 * number of assets big_X = X.loc[target_date] big_X = big_X.loc[target_assets] big_X = big_X.reindex(columns=all_factors_gid) big_X.fillna(0,inplace=True) # setup the Factor model portfolio optimization parameter # w is the solution x variable w = cvx.Variable(noa) f = big_X.T.values*w # gamma parameter, multiplier of risk gamma = cvx.Parameter(sign='positive') # Lmax is maximum leverage Lmax = cvx.Parameter() ret = w.T * rets_mean.values # create quadratic form of risk risk = cvx.quad_form(f, cov_matrix.values) + cvx.quad_form(w, delta_on_date.values) # setup value constraint: """ # asset constraint: Asset ts_asset_group_loading diagonal matrix:(OOTV, Matrix M1(n * n)), 59 * 59. asset value range, value1, value2, 58 * 2. select 58 * 58 from diagonal matrix, order by idx_leve1_value. product: multiply_matrix.T.values * w, [58x58] * [58x1] # industry constraint: industry ts_asset_group_loading sparse matrix:(OOTV, Matrix M1(n * m)), 58 * 26. industry value range, value1, value2, 26 * 2. select 58 * 26 from sparse matrix, order by group_constraint index value. product: multiply_matrix.T.values * w, [26x58] * [58x1]=[26x1] # factor constraint: factor ts_asset_group_loading exposure matrix:(OOTV, Matrix M1(n * m)), 35 * 3436. factor exposure value range, value1, value2, 35 * 2. select 35 * 58 from factor exposure matrix. product: multiply_matrix.T.values * w, [35x58] * [58x1]=[35x1] """ constraint_value = [] for cst in constraint: if cst is None: continue # in order to align the production. df_boundary = cst['ts_group_loading_range'].asColumnTab().copy() df_boundary = df_boundary.loc[(df_boundary['date'] == target_date)] df_boundary.drop('date', axis=1, inplace=True) df_boundary.set_index('target', inplace=True) df_boundary_idx = df_boundary.index multiply_matrix = cst['ts_asset_group_loading'].copy().\ loc[target_date].loc[target_assets, df_boundary_idx].fillna(0) create_constraint(multiply_matrix.T.values*w, df_boundary, constraint_value) # leverage level and risk adjusted parameter Lmax.value = 1 gamma.value = 1 eq_constraint = [cvx.sum_entries(w) == 1, cvx.norm(w, 1) <= Lmax] if mode == gsConst.Const.MinimumRisk: # maximize negative product of gamma and risk prob_factor = cvx.Problem(cvx.Maximize(-gamma*risk), eq_constraint + constraint_value) if mode == gsConst.Const.MinimumRiskUnderReturn: # minimum risk subject to target return, Markowitz Mean_Variance Portfolio prob_factor = cvx.Problem(cvx.Maximize(-gamma*risk), [ret >= target_return]+eq_constraint+constraint_value) if mode == gsConst.Const.MaximumReturnUnderRisk: # Portfolio optimization with a leverage limit and a bound on risk prob_factor = cvx.Problem(cvx.Maximize(ret), [risk <= target_risk]+eq_constraint+constraint_value) prob_factor.solve(verbose=False) logger.debug(prob_factor.status) if prob_factor.status == 'infeasible': df_opts_status.loc[target_date] = gsConst.Const.Infeasible else: df_opts_weight.loc[target_date, target_assets] = np.array(w.value.astype(np.float64)).T df_opts_status.loc[target_date] = gsConst.Const.Feasible return {'weight':df_opts_weight.dropna(axis=0, how='all'), 'status':df_opts_status}
def SIMPLE_SIMULATE_DAILY_TRADE_CHN_STK(beginDate, endDate, initialHolding, df_targetPortfolioWgt, df_markToMarketPrice, df_totalReturnFactor, df_executePrice, df_execPriceReturn, df_tradeVolume, dict_tradingParam, dict_additionalTs): beginDate = beginDate[0] endDate = endDate[0] df_targetPortfolioWgt = df_targetPortfolioWgt.asMatrix() df_markToMarketPrice = df_markToMarketPrice.asMatrix() df_totalReturnFactor = df_totalReturnFactor.asMatrix() df_executePrice = df_executePrice.asMatrix() df_execPriceReturn = df_execPriceReturn.asMatrix() df_tradeVolume = df_tradeVolume.asMatrix() cashSymbol = gsUtils.getCashGid() godGid = gsUtils.getGodGid() allDates = df_markToMarketPrice.index if len(allDates[(allDates >= beginDate) & (allDates <= endDate)]) < 1: raise ValueError('no trading date falls between begindate and enddate') endDate = allDates[allDates <= endDate][-1] if beginDate > endDate: raise ValueError('beginDate should be less than endDate') initHldIsCash = True if isinstance(initialHolding, gftIO.GftTable): df_initialHolding = initialHolding.asMatrix() if df_initialHolding.shape[0] < 1: raise ValueError('no init holding is provided') initHldIsCash = False df_initialHolding = df_initialHolding.ix[-1:] beginDate = gsUtils.alignDate(df_initialHolding.index[0], allDates, method='ffill') if pd.isnull(beginDate): raise ValueError('do not have close price for the date of initHld') df_initialHolding.index = [beginDate] else: beginDate = gsUtils.alignDate(beginDate, allDates, method='bfill') if pd.isnull(beginDate): raise ValueError( 'beginDate should be less than the last trading date') if (df_targetPortfolioWgt < 0).any(axis=1).any(): raise ValueError( 'Do not support stock short selling and cash borrowing') if (round(df_targetPortfolioWgt.sum(1), 4) > 1).any(): raise ValueError('Total weight is greater than 1.') df_targetPortfolioWgt = df_targetPortfolioWgt.dropna(axis=[0, 1], how='all') sigDates = df_targetPortfolioWgt.index rebDates = sigDates if len(sigDates) > 0: execDelayPeriods = gsUtils.getParm(dict_tradingParam, "execDelayPeriods", 0) if execDelayPeriods > 0: idxs = np.array(gsUtils.alignDate(sigDates, allDates, method='bfill', returnidx=True), dtype=float) idxs = idxs + execDelayPeriods idxs[idxs > len(allDates)] = NA idxs[idxs == np.append(idxs[1:], NA)] = NA idxs_nonnan_flag = np.logical_not(np.isnan(idxs)) if sum(idxs_nonnan_flag) < 1: raise ValueError("no trade date after the execute delay shift") df_targetPortfolioWgt = df_targetPortfolioWgt.ix[idxs_nonnan_flag] rebDates = allDates[np.array(idxs[idxs_nonnan_flag], dtype=int)] df_targetPortfolioWgt.index = rebDates if len(rebDates) > 0: if initHldIsCash: if gsUtils.getParm(dict_tradingParam, "shiftBeginDateToSignal", False): beginDate = rebDates[rebDates >= beginDate][0] if pd.isnull(beginDate): raise ValueError('beginDate is null after shift') tradeDates = allDates[(allDates >= beginDate) & (allDates <= endDate)] beginDate = tradeDates[0] endDate = tradeDates[-1] if beginDate > endDate: raise ValueError( "Begin date is larger than end date after the date processing!") rebDates = rebDates[(rebDates >= beginDate) & (rebDates <= endDate)] df_targetPortfolioWgt = df_targetPortfolioWgt.ix[rebDates] allSymbols = np.unique(df_markToMarketPrice.columns) portfolioSymbols = np.unique( np.setdiff1d(df_targetPortfolioWgt.columns, cashSymbol)) holdingSymbols = np.array([]) if not (initHldIsCash): holdingSymbols = np.unique( np.setdiff1d(df_initialHolding.columns, cashSymbol)) if len(np.setdiff1d(holdingSymbols, allSymbols)) > 0: raise ValueError("Initial Portfolio has non A-share stocks!") if len(np.setdiff1d(portfolioSymbols, allSymbols)) > 0: raise ValueError("Target Portfolio has non A-share stocks!") allSymbols = np.unique( np.setdiff1d( np.intersect1d(allSymbols, np.append(holdingSymbols, portfolioSymbols)), cashSymbol)) priceDates = allDates[(allDates >= beginDate - datetime.timedelta(days=20)) & (allDates <= endDate)] df_markToMarketPrice = df_markToMarketPrice.reindex(priceDates, allSymbols, fill_value=NA) df_totalReturnFactor = df_totalReturnFactor.reindex( priceDates, allSymbols, fill_value=1.).fillna(1.) df_executePrice = df_executePrice.reindex(priceDates, allSymbols, fill_value=NA) df_execPriceReturn = df_execPriceReturn.reindex(priceDates, allSymbols, fill_value=NA) df_tradeVolume = df_tradeVolume.reindex(priceDates, allSymbols, fill_value=0.) if initHldIsCash: df_initialHolding = pd.DataFrame(initialHolding, index=[beginDate], columns=[cashSymbol]) df_initialHolding = df_initialHolding.reindex( columns=np.append(allSymbols, cashSymbol)).fillna(0.) df_initialHoldingCash = df_initialHolding.ix[:, cashSymbol] df_initialHolding = df_initialHolding.ix[:, allSymbols] initHldValue = float( (df_initialHolding * df_markToMarketPrice.ix[df_initialHolding.index] ).sum(axis=1)) + df_initialHoldingCash.ix[0, 0] df_targetPortfolioWgt = df_targetPortfolioWgt.reindex( rebDates, allSymbols, fill_value=0.).fillna(0.) df_buyVolume = df_tradeVolume.copy().fillna(0) df_sellVolume = df_buyVolume.copy() if gsUtils.getParm(dict_tradingParam, "canTradeOnSuspend", 0) > 0: df_buyVolume[df_buyVolume < 1] = np.inf df_sellVolume[df_sellVolume < 1] = np.inf riseLimitThres = gsUtils.getParm(dict_tradingParam, "riseLimitThres", 0) if riseLimitThres > 0: riseLimit = df_execPriceReturn > riseLimitThres df_buyVolume[riseLimit] = 0 df_sellVolume[riseLimit & (df_sellVolume > 0)] = np.inf fallLimitThres = gsUtils.getParm(dict_tradingParam, "fallLimitThres", 0) if fallLimitThres < 0: fallLimit = df_execPriceReturn < fallLimitThres df_buyVolume[fallLimit & (df_buyVolume > 0)] = np.inf df_sellVolume[fallLimit] = 0 volumeLimitPct = gsUtils.getParm(dict_tradingParam, "volumeLimitPct", 0) if volumeLimitPct > 0: df_buyVolume = df_buyVolume * volumeLimitPct df_sellVolume = df_sellVolume * volumeLimitPct else: df_buyVolume[df_buyVolume > 0] = np.inf df_sellVolume[df_sellVolume > 0] = np.inf lotSize = gsUtils.getParm(dict_tradingParam, "lotSize", 0) df_buyVolume = gsUtils.roundToLot(df_buyVolume, lotSize) df_sellVolume = gsUtils.roundToLot(df_sellVolume, lotSize) buyCommission = gsUtils.getParm(dict_tradingParam, "buyCommission", 0) sellCommission = gsUtils.getParm(dict_tradingParam, "sellCommission", 0) df_holdings = pd.DataFrame(0., index=tradeDates, columns=allSymbols) df_weights = df_holdings.copy() df_execution = df_holdings.copy() df_holdingCash = pd.DataFrame(0., index=tradeDates, columns=cashSymbol) df_portfolioValue = pd.DataFrame(0., index=tradeDates, columns=godGid) df_cumRets = df_portfolioValue.copy() df_singlePeriodRets = df_portfolioValue.copy() df_turnoverPct = df_portfolioValue.copy() d = tradeDates[0] df_holdings.ix[d] = df_initialHolding.ix[d] df_holdingCash.ix[d] = df_initialHoldingCash.ix[0, 0] if len(rebDates) < 1: nextd = tradeDates[-1] ls_adjustedHoldings = fillHolding(d, nextd, tradeDates, df_holdings, df_holdingCash, df_totalReturnFactor) df_holdings = ls_adjustedHoldings['holdings'] df_holdingCash = ls_adjustedHoldings['holdingCash'] else: nextd = rebDates[0] ls_adjustedHoldings = fillHolding(d, nextd, tradeDates, df_holdings, df_holdingCash, df_totalReturnFactor) df_holdings = ls_adjustedHoldings['holdings'] df_holdingCash = ls_adjustedHoldings['holdingCash'] for i in range(len(rebDates)): d = rebDates[i] s_currentHoldingValue = df_holdings.ix[d] * df_executePrice.ix[d] totalValue = s_currentHoldingValue.sum() + df_holdingCash.ix[d, 0] s_currentHoldingWgt = s_currentHoldingValue / totalValue s_targetHoldingWgt = df_targetPortfolioWgt.ix[d] targetHoldingCashWgt = 1.0 - s_targetHoldingWgt.sum() s_orderWgt = s_targetHoldingWgt - s_currentHoldingWgt s_sellOrderWgt = s_orderWgt.copy() s_sellOrderWgt[s_sellOrderWgt > 0.] = 0. s_buyOrderWgt = s_orderWgt.copy() s_buyOrderWgt[s_buyOrderWgt < 0.] = 0. cashAvail = df_holdingCash.ix[d, 0] if (s_sellOrderWgt < 0).any(): s_sellOrder = gsUtils.roundToLot( s_sellOrderWgt / s_currentHoldingWgt.where(s_currentHoldingWgt > 0, 1.0) * df_holdings.ix[d], lotSize) s_sellOrder = s_sellOrder.where(s_targetHoldingWgt > 0, -df_holdings.ix[d]) s_sellExecution = s_sellOrder.copy() s_sellExecution = -pd.concat( [s_sellExecution.fillna(0).abs(), df_sellVolume.ix[d]], axis=1).min(axis=1) cashAvail = cashAvail + (s_sellExecution.abs( ) * df_executePrice.ix[d]).sum() * (1 - sellCommission) df_execution.ix[d] += s_sellExecution df_holdings.ix[d] += s_sellExecution if (s_buyOrderWgt > 0).any(): canBuyWgt = cashAvail / totalValue - targetHoldingCashWgt if canBuyWgt > 0: s_buyOrder = gsUtils.roundToLot( (min(canBuyWgt / s_buyOrderWgt.sum(), 1.0) * s_buyOrderWgt * totalValue / (1 + buyCommission) / df_executePrice.ix[d]).fillna(0), lotSize) s_buyExecution = s_buyOrder.copy() s_buyExecution = pd.concat( [s_buyExecution.fillna(0), df_buyVolume.ix[d]], axis=1).min(axis=1) cashAvail = cashAvail - (s_buyExecution.abs( ) * df_executePrice.ix[d]).sum() * (1 + buyCommission) df_execution.ix[d] += s_buyExecution df_holdings.ix[d] += s_buyExecution df_holdingCash.ix[d] = cashAvail df_turnoverPct.ix[d] < -(df_execution.ix[d].abs() * df_executePrice.ix[d]).sum() / totalValue if i < (len(rebDates) - 1): nextd = rebDates[i + 1] else: nextd = tradeDates[-1] ls_adjustedHoldings = fillHolding(d, nextd, tradeDates, df_holdings, df_holdingCash, df_totalReturnFactor) df_holdings = ls_adjustedHoldings['holdings'] df_holdingCash = ls_adjustedHoldings['holdingCash'] df_portfolioValue.ix[:, 0] = (df_holdings * df_markToMarketPrice.ix[tradeDates]).sum( axis=1) + df_holdingCash.ix[:, 0] df_weights = (df_holdings * df_markToMarketPrice.ix[tradeDates]).div( df_portfolioValue.ix[:, 0], axis=0) df_cumRets = df_portfolioValue / initHldValue - 1 df_singlePeriodRets = df_portfolioValue / df_portfolioValue.shift(1) - 1 df_singlePeriodRets.ix[0, 0] = df_portfolioValue.ix[0, 0] / initHldValue - 1 result = {} result[gsConst.Const.Holding] = pd.concat( [df_holdings.replace(0, NA), df_holdingCash], axis=1) result[gsConst.Const.PortfolioValue] = df_portfolioValue result[gsConst.Const.Weights] = df_weights.replace(0, NA) result[gsConst.Const.SinglePeriodReturn] = df_singlePeriodRets result[gsConst.Const.CumulativeReturn] = df_cumRets result[gsConst.Const.Turnover] = df_turnoverPct print(df_cumRets.ix[-1]) return result
def SIMPLE_SIMULATE_DAILY_TRADE_CHN_STK(dt_begin, dt_end, initial_holding_position, \ df_w_target_portfolio_weight, df_w_market_price, \ df_w_total_return_factor, df_w_execute_price, \ df_w_execute_price_return, df_w_trade_volume, \ dict_trading_param, additionalTs): """股票模拟交易 input: dt_begin, 开始交易日期,在rebalance之前,repeat initial holding, 在rebalance之后,从begin date开始交易。 dt_end, 结束日期 initial_holding_position, 输入持仓,可能为一个dataframe, 也有可能是一个数量cash df_w_target_portfolio_weight, 目标持仓权重,每行总和应等于1 df_w_market_price, 股票价格 df_w_total_return_factor, 复权因子计算 df_w_execute_price, 交易股票执行价格 df_w_execute_price_return, 每只股票交易日的回报 df_w_trade_volume, 每只股票交易日的交易量 dict_trading_param, 交易参数 additionalTs,月平衡策略参数 output: result, 字典 result['HOLDING'], 所有股票持仓和现金数据 OTV all dates result['PORTFOLIO_VALUE'], 组合总价值 TV monthly result['SINGLE_PERIOD_RETURN'], 组合每日回报 TV all dates result['WEIGHTS'],组合中各项权重 OTV all dates result['CUMULATIVE_RETURN'],累积收益 TV all dates result['TURNOVER'],换手率 TV monthly """ df_w_target_portfolio_weight = df_w_target_portfolio_weight.asMatrix() df_w_market_price = df_w_market_price.asMatrix() df_w_total_return_factor = df_w_total_return_factor.asMatrix() df_w_execute_price = df_w_execute_price.asMatrix() df_w_execute_price_return = df_w_execute_price_return.asMatrix() df_w_trade_volume = df_w_trade_volume.asMatrix() ls_all_dates = df_w_market_price.index.intersection(df_w_total_return_factor.index.intersection(df_w_execute_price.index.intersection(df_w_execute_price_return.index.intersection(df_w_trade_volume.index)))) ls_cashGid = gsUtils.getCashGid() if len(df_w_target_portfolio_weight) > 0: df_w_target_portfolio_weight = df_w_target_portfolio_weight.ix[ls_all_dates] df_w_target_portfolio_weight = df_w_target_portfolio_weight.dropna(how='all') ls_rebalance_dates = df_w_target_portfolio_weight.index.tolist() if len(ls_rebalance_dates) > 0 and gsUtils.getParm(dict_trading_param, 'execDelayPeriods', 0) > 0: ls_rebalance_dates = ls_rebalance_dates.shift(gsUtils.getParm(dict_trading_param, 'execDelayPeriods', 0)) ls_rebalance_dates = gsUtils.alignDate(ls_all_dates, ls_rebalance_dates) df_w_target_portfolio_weight = df_w_target_portfolio_weight.ix[ls_rebalance_dates] ls_holding_symbols = [] if isinstance(initial_holding_position, pd.DataFrame): dt_begin = initial_holding_position.index[-1] ls_holding_symbols = sorted(list(set([i for i in initial_holding_position.columns if i not in ls_cashGid]))) else: if len(df_w_target_portfolio_weight) > 0 and gsUtils.getParm(dict_trading_param, 'shiftBeginDateToSignal', 0) > 0: dt_begin = max(dt_begin, df_w_target_portfolio_weight.index[0]) ls_trade_dates = [date for date in ls_all_dates if date >= dt_begin and date <= dt_end] dt_begin = ls_trade_dates[0] dt_end = ls_trade_dates[-1] ls_rebalance_dates = [date for date in ls_rebalance_dates if date>=dt_begin and date<=dt_end] if (dt_begin > dt_end): raise ValueError('input error! Begin date must be less than end date!') ls_all_symbols = sorted(list(set.intersection(set(df_w_market_price.columns),set(df_w_execute_price.columns),set(df_w_execute_price_return.columns),set(df_w_trade_volume.columns)))) df_w_tmp_target_portfolio_weight = df_w_target_portfolio_weight.dropna(how='all',axis=1) df_w_tmp_target_portfolio_weight = df_w_tmp_target_portfolio_weight.loc[:,(df_w_tmp_target_portfolio_weight!=0).any(axis=0)] ls_portfolio_symbols = [s for s in df_w_tmp_target_portfolio_weight.columns if s not in ls_cashGid] if len([s for s in ls_holding_symbols if s not in ls_all_symbols]) > 0: raise ValueError('input error! Initial Portfolio has non A-share stocks!') if len([s for s in ls_portfolio_symbols if s not in ls_all_symbols]) > 0: raise ValueError('input error! Target Portfolio has non A-share stocks!') # todo: process holding symbols ls_all_symbols = sorted([s for s in set.intersection(set(ls_all_symbols),set(ls_portfolio_symbols)) if s not in ls_cashGid]) ls_price_dates = [d for d in ls_all_dates if d >= (dt_begin-pd.Timedelta('20 days')) and d <= dt_end] df_w_market_price = df_w_market_price.loc[ls_price_dates][ls_all_symbols] df_w_total_return_factor = add_missing_columns(df_w_total_return_factor, ls_all_symbols, 1).loc[ls_price_dates, ls_all_symbols] df_w_execute_price = df_w_execute_price.loc[ls_price_dates][ls_all_symbols] df_w_execute_price_return = df_w_execute_price_return.loc[ls_price_dates][ls_all_symbols] df_w_trade_volume = df_w_trade_volume.loc[ls_price_dates][ls_all_symbols] df_w_initial_holding = initial_holding_position if not isinstance(initial_holding_position, pd.DataFrame): df_w_initial_holding = pd.DataFrame(initial_holding_position, index=[dt_begin], columns=[ls_cashGid]) # todo: if initial holding is dataframe, fill df_w_initial_holding = add_missing_columns(df_w_initial_holding, ls_all_symbols) df_w_initial_holding_cash = df_w_initial_holding.loc[df_w_initial_holding.index, ls_cashGid] # todo, pop godgid df_w_initial_holding =df_w_initial_holding.drop(ls_cashGid, axis=1) num_initial_holding_positionValue = float((df_w_initial_holding*df_w_market_price.ix[df_w_initial_holding.index]).sum(1)+df_w_initial_holding_cash.values[-1]) ls_all_symbols.append(ls_cashGid) df_w_target_portfolio_weight_fill = add_missing_columns(df_w_target_portfolio_weight, ls_all_symbols) df_w_target_portfolio_weight = df_w_target_portfolio_weight_fill.ix[ls_rebalance_dates].fillna(0) ls_all_symbols.pop(-1) if (df_w_target_portfolio_weight < 0).any().any(): raise ValueError('input error! Do not support stock short selling and cash borrowing.') df_w_initial_holding_cash_weight = df_w_target_portfolio_weight[ls_cashGid] df_w_target_portfolio_weight = df_w_target_portfolio_weight.drop(ls_cashGid, axis=1) df_w_initial_holding_cash_weight = 1. - df_w_target_portfolio_weight.sum(axis=1) df_w_buy_volume = df_w_trade_volume.copy().fillna(0) df_w_sell_volumn = df_w_trade_volume.copy().fillna(0) num_lot_size = gsUtils.getParm(dict_trading_param, 'lotSize', 0) df_w_buy_volume = round_to_lot(df_w_buy_volume, num_lot_size) df_w_sell_volumn = round_to_lot(df_w_sell_volumn, num_lot_size) try: can_trade_on_suspend = gsUtils.getParm(dict_trading_param, 'canTradeOnSuspend', 0) if not can_trade_on_suspend is None: pass except KeyError: print("no trade on suspend information") else: if can_trade_on_suspend > 0: df_w_buy_volume[df_w_buy_volume < 1] = np.inf df_w_sell_volumn[df_w_sell_volumn < 1] = np.inf try: rise_limit_thres = gsUtils.getParm(dict_trading_param, 'riseLimitThres', 0) if not rise_limit_thres is None: pass except KeyError: print("no rise limit threshold information") else: if rise_limit_thres > 0: rise_limit = df_w_execute_price_return > rise_limit_thres df_w_buy_volume[rise_limit] = 0 df_w_sell_volumn[rise_limit & (df_w_sell_volumn > 0)] = np.inf try: fall_limit_thres = gsUtils.getParm(dict_trading_param, 'fallLimitThres', 0) if not fall_limit_thres is None: pass except KeyError: print("no fall limit threshold information") else: if fall_limit_thres < 0: fall_limit = df_w_execute_price_return < fall_limit_thres df_w_buy_volume[fall_limit & (df_w_buy_volume > 0)] = np.inf df_w_sell_volumn[fall_limit] = 0 try: volume_limit_pct = gsUtils.getParm(dict_trading_param, 'volumeLimitPct', 0) if volume_limit_pct is None: pass except KeyError: print("no fall limit threshold information") else: if volume_limit_pct > 0: df_w_buy_volume = df_w_buy_volume * volume_limit_pct df_w_sell_volumn = df_w_sell_volumn * volume_limit_pct else: df_w_buy_volume[df_w_buy_volume > 0] = np.inf df_w_sell_volumn[df_w_sell_volumn > 0] = np.inf num_buy_commission = gsUtils.getParm(dict_trading_param, 'buyCommission', 0) num_sell_commission = gsUtils.getParm(dict_trading_param, 'sellCommission', 0) df_w_holding = pd.DataFrame(0., index=ls_trade_dates, columns=ls_all_symbols) df_w_weights = df_w_holding.copy() df_w_execution = df_w_holding.copy() df_w_holding_cash = pd.DataFrame(0.,index=ls_trade_dates,columns=[ls_cashGid]) ls_getGodGid = gsUtils.getGodGid() df_portfolio_value = pd.DataFrame(0., index=ls_trade_dates, columns=[ls_getGodGid]) df_w_single_period_ret = df_portfolio_value.copy() df_w_turnover_percent = df_portfolio_value.copy() ## trading d = ls_trade_dates[0] df_w_holding.ix[d] = df_w_initial_holding.loc[d].tolist() df_w_holding_cash.ix[d] = df_w_initial_holding_cash.values[-1] if len(ls_rebalance_dates) < 1: nextd = ls_trade_dates[-1] df_w_holding, df_w_holding_cash = fill_holding(d, nextd, ls_trade_dates, \ df_w_holding, df_w_holding_cash, \ df_w_total_return_factor) else: nextd = ls_rebalance_dates[0] df_w_holding, df_w_holding_cash=fill_holding(d, nextd, ls_trade_dates, \ df_w_holding, df_w_holding_cash, \ df_w_total_return_factor) for i in range(len(ls_rebalance_dates)): d = ls_rebalance_dates[i] df_w_current_holding_value = df_w_holding.ix[d]*(df_w_execute_price.ix[d].fillna(0)) # one line num_totalValue = df_w_current_holding_value.sum() + df_w_holding_cash.ix[d].values[-1] df_w_current_holding_weight = df_w_current_holding_value / num_totalValue df_w_currrent_holding_cash_weight = 1. - df_w_current_holding_weight.sum() df_w_target_holding_weight = df_w_target_portfolio_weight.ix[d] num_target_holding_cash_weight = 1. - df_w_target_holding_weight.sum() df_w_order_weight = df_w_target_holding_weight - df_w_current_holding_weight df_w_sell_order_weight = df_w_order_weight.copy() df_w_sell_order_weight[df_w_order_weight >= 0.0] = 0.0 df_w_buy_order_weight = df_w_order_weight.copy() df_w_buy_order_weight[df_w_order_weight <= 0.0] = 0.0 num_cash_available = df_w_holding_cash.ix[d].values[-1] # sell if (df_w_sell_order_weight.dropna() < 0.0).any(): df_w_current_holding_weight_for_sell = df_w_current_holding_weight.copy().fillna(0) df_w_current_holding_weight_for_sell[df_w_current_holding_weight_for_sell <= 0.0] = 1.0 tmp_1 = df_w_sell_order_weight / df_w_current_holding_weight_for_sell * df_w_holding.ix[d] df_w_sell_order = round_to_lot(tmp_1, num_lot_size) # share # sell all if target holding weight is equal to 0 df_w_sell_order[df_w_target_holding_weight <= 0.0] = -df_w_holding.ix[d] df_w_sell_execution = df_w_sell_order.copy() df_w_sell_execution = -pd.concat([df_w_sell_execution.fillna(0).abs(), \ df_w_sell_volumn.ix[d]], axis=1).min(axis=1) num_cash_available = num_cash_available + (df_w_sell_execution.abs() * \ df_w_execute_price.ix[d]).sum() * (1. - num_sell_commission) df_w_execution.ix[d] = df_w_execution.ix[d] + df_w_sell_execution df_w_holding.ix[d] = df_w_holding.ix[d] + df_w_sell_execution # buy if (df_w_buy_order_weight > 0.0).any(): num_can_buy_weight = num_cash_available / num_totalValue - num_target_holding_cash_weight if num_can_buy_weight > 0: num_pct = min(num_can_buy_weight / df_w_buy_order_weight.sum(), 1) tmp_2 = (num_pct * df_w_buy_order_weight * num_totalValue / (1.+num_buy_commission) / df_w_execute_price.ix[d]).fillna(0) df_w_buy_order = round_to_lot(tmp_2, num_lot_size) df_w_buy_order = df_w_buy_order.fillna(0) df_w_buy_execution = df_w_buy_order.copy() # redundant df_w_buy_execution = pd.concat([df_w_buy_execution.fillna(0), \ df_w_buy_volume.ix[d]],axis=1).min(axis=1) num_cash_available = num_cash_available - (abs(df_w_buy_execution) * \ df_w_execute_price.ix[d]).sum() * \ (1.+num_buy_commission) df_w_execution.ix[d] = df_w_execution.ix[d] + df_w_buy_execution df_w_holding.ix[d] = df_w_holding.ix[d] + df_w_buy_execution df_w_holding_cash.ix[d] = num_cash_available df_w_turnover_percent.ix[d] = (abs(df_w_execution.ix[d])*df_w_execute_price.ix[d]).sum() / num_totalValue if i < (len(ls_rebalance_dates) - 1): nextd = ls_rebalance_dates[i+1] df_w_holding, df_w_holding_cash = fill_holding(d, nextd, ls_trade_dates, \ df_w_holding, df_w_holding_cash, \ df_w_total_return_factor) # loop to the next day. nextd = ls_trade_dates[-1] df_w_holding, df_w_holding_cash = fill_holding(d, nextd, ls_trade_dates, \ df_w_holding, df_w_holding_cash, \ df_w_total_return_factor) df_w_portfolio_stat = pd.DataFrame(columns=[ls_getGodGid]) df_w_portfolio_stat['value'] = (df_w_holding*df_w_market_price.ix[ls_trade_dates]).sum(axis=1) df_w_portfolio_stat['cash'] = df_w_holding_cash df_portfolio_value = df_w_portfolio_stat.sum(axis=1) df_w_weights = (df_w_holding*df_w_market_price.ix[ls_trade_dates]).div(df_portfolio_value,axis=0) df_w_single_period_ret = df_portfolio_value / df_portfolio_value.shift(1) - 1.0 df_w_single_period_ret[0] = df_portfolio_value[0] / num_initial_holding_positionValue - 1.0 # df_w_single_period_ret.name = ls_getGodGid s_cum_rets = df_portfolio_value / num_initial_holding_positionValue s_cum_rets[-1] = df_portfolio_value[-1] / num_initial_holding_positionValue print(s_cum_rets[-1]) result = {} result['HOLDING'] = pd.concat([df_w_holding, df_w_holding_cash], axis=1) result['PORTFOLIO_VALUE'] = pd.DataFrame(df_portfolio_value, columns=[ls_getGodGid]) # result['PORTFOLIO_VALUE'] = result['PORTFOLIO_VALUE'].reset_index() result['SINGLE_PERIOD_RETURN'] = pd.DataFrame(df_w_single_period_ret, columns=[ls_getGodGid]) # result['SINGLE_PERIOD_RETURN'] = result['SINGLE_PERIOD_RETURN'] result['WEIGHTS'] = df_w_weights result['CUMULATIVE_RETURN'] = pd.DataFrame(s_cum_rets, columns=[ls_getGodGid]) result['TURNOVER'] = pd.DataFrame(df_w_turnover_percent, columns=[ls_getGodGid]) return result
if dict_trading_param['volumeLimitPct'] > 0: df_w_buy_volume = df_w_buy_volume * dict_trading_param['volumeLimitPct'] df_w_sell_volumn = df_w_sell_volumn * dict_trading_param['volumeLimitPct'] else: df_w_buy_volume[df_w_buy_volume > 0] = np.inf df_w_sell_volumn[df_w_sell_volumn > 0] = np.inf num_buy_commission = get_param(dict_trading_param, 'buyCommission', 0) num_sell_commission = get_param(dict_trading_param, 'sellCommission', 0) df_w_holding = pd.DataFrame(0., index=ls_trade_dates, columns=ls_all_symbols) df_w_weights = df_w_holding.copy() df_w_execution = df_w_holding.copy() df_w_holding_cash = pd.DataFrame(0.,index=ls_trade_dates,columns=[ls_cashGid]) ls_getGodGid = gsUtils.getGodGid() df_portfolio_value = pd.DataFrame(0., index=ls_trade_dates, columns=[ls_getGodGid]) df_w_single_period_return = df_portfolio_value.copy() df_w_turnover_percent = df_portfolio_value.copy() ## trading d = ls_trade_dates[0] df_w_holding.ix[d] = df_w_initial_holding.loc[d].tolist() df_w_holding_cash.ix[d] = df_w_initial_holdingCash.values[-1][-1] if len(ls_rebalance_dates) < 1: nextd = ls_trade_dates[-1] df_w_holding, df_w_holding_cash = fill_holding(d, nextd, ls_trade_dates, \ df_w_holding, df_w_holding_cash, \