def calcDayDescriptor(self, stock_code: str, timepoint: str): try: period = self.params['N'] except Exception as e: raise ("计算平均持股成本缺少回溯期数N") timepoint = datetime.strptime(timepoint, '%Y%m%d') priceInfo = fundamentalApi.quotes( asset_code=stock_code, starttime=None, endtime=timepoint - timedelta(days=1), period=period, fields=['Open', 'High', 'Low', 'Close', 'Tor_f'], freq='d', adj='hfq') if ((priceInfo['Tor_f'] > 0).sum() < period * 2 / 3 or priceInfo.__len__() == 0): cgoValue = np.nan else: yesterdayClose = priceInfo['Close'].values[0] priceInfo['Tor_f'] = priceInfo['Tor_f'].clip( upper=1) #换手率超过100%的视为100% priceInfo.sort_index(ascending=False, inplace=True) priceInfo['remianPart'] = ( 1 - priceInfo['Tor_f']).shift(1).fillna(1).cumprod() priceInfo['relativeWeight'] = priceInfo['remianPart'] * priceInfo[ 'Tor_f'] priceInfo['priceWeight'] = priceInfo['relativeWeight'] / priceInfo[ 'relativeWeight'].sum() priceInfo['avgPrice'] = (priceInfo['High'] + priceInfo['Low'] + priceInfo['Open'] + priceInfo['Close']) / 4 referencePrice = (priceInfo['priceWeight'] * priceInfo['avgPrice']).sum() cgoValue = yesterdayClose / referencePrice - 1 return cgoValue
def calcDayDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") try: t = self.params['t'] #计算量价背离所用回溯期数 if (type(t) is not int or t < 1): raise Exception("回溯期t必须为大于1的整数!") except Exception as e: print("参数异常!") raise (e) priceInfo = fundamentalApi.quotes( asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=t, fields=['Open', 'High', 'Low', 'Close', 'Volume'], freq='d', adj='hfq') try: depart = 0.25 * ( np.corrcoef(priceInfo['Open'], priceInfo['Volume']) + np.corrcoef(priceInfo['High'], priceInfo['Volume']) + np.corrcoef(priceInfo['Low'], priceInfo['Volume']) + np.corrcoef(priceInfo['Close'], priceInfo['Volume']))[0, 1] except: depart = np.nan return depart
def calcDayDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") try: n1, n2 = self.params['wait'], self.params['cum'] #间隔期,总长度 if (type(n1) is not int or type(n2) is not int): raise Exception("间隔期wait、区间长度cum必须为非负整数!") if (n2 < 1): raise Exception("区间长度cum至少为1!") except Exception as e: print("参数异常!") raise (e) priceInfo = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=n1 + n2, fields=['Open', 'Close'], freq='d', adj='hfq') try: momentum = priceInfo['Close'].values[n1] / priceInfo[ 'Open'].values[n1 + n2 - 1] - 1 except: momentum = np.nan return momentum
def __settleOrder(self,stockcode:str,dealprice:float,holdAmt:int,topModelPeriod:int): #被fire调用时执行 #以股票交易价格作为已结模型的关闭仓位价格、新开模型的开仓价格,每只股票关闭时,尝试对OrderBook表、OrderBookDetail表登记实现损益 #(这玩意不影响交易流程,但运行的时机和实际交易时机一致) #1.标记平仓行实际收益率real_ret #1.1 select出OrderBookDetail中所有待平仓行(所有已执行(即标记了开仓价及开仓后仓位),但未平仓,且指令源模型已到平仓时间的指令) #1.2 逐行循环,找出复权因子 #1.3 计算实现平仓复权价与实现开仓复权价之间的收益率 #1.4 记录收益率 CalendarCount = fApi.trade_cal('SSE', self.MODEL_BEGIN_DATE, datetime.now().strftime('%Y%m%d'), '1').__len__() markCloseSql = 'select a.Id,a.AdjDt,a.stockcode,a.open_price from OrderBookDetail a ' \ 'left join MultiFactorOptimizor b on a.Id=b.Id ' \ 'where a.post_position is not null and a.real_ret is null' \ 'and {CalendarCount}%b.ForcastPeriod=0 '.format(CalendarCount=CalendarCount) closeList = pd.read_sql_query(markCloseSql,fApi.conn) for k,v in closeList.iterrows(): id,adjdt,stkcode,open_price = v openAdjFactor = fApi.quotes(stkcode,None,datetime.strptime(adjdt,'%Y%m%d'),1,'Adjfactor','d',None)['Adjfactor'].values[0] nowAdjFactor = fApi.quotes(stkcode,None,datetime.now()-timedelta(days=1),1,'Adjfactor','d',None)['Adjfactor'].values[0] openAdjPrice = open_price * openAdjFactor closeAdjPrice = dealprice * nowAdjFactor realRet = closeAdjPrice/openAdjPrice -1 sql = 'replace into OrderBookDetail' \ '(id, adjdt, stockcode, fore_position, adj_amount_plan, post_position_plan, post_position, open_price, exp_ret, real_ret)' \ 'select a.id, a.adjdt, a.stockcode, a.fore_position, a.adj_amount_plan, a.post_position_plan' \ ', a.post_position, {dealprice}, a.exp_ret, {realRet}' \ 'from OrdeBookDetail a' \ 'where a.stockcode=\'{stockcode}\' and a.Id={Id} and a.AdjDt=\'{adjdt}\''.format(dealprice=dealprice,realRet=realRet,stockcode=stockcode,Id=id,adjdt=adjdt) fApi.conn.execute(sql) #2.标记开仓行的实际开仓价格和仓位(当前阶段,将现实仓位与计划仓位差异全归于顶层模型) #找出已下达但未执行(即未标记开仓价格和开仓后仓位)的指令,写入开仓价及开仓后仓位 markOpenSql = 'replace into OrderBookDetail' \ '(id, adjdt, stockcode, fore_position, adj_amount_plan, post_position_plan, post_position, open_price, exp_ret, real_ret)' \ 'select a.id, a.adjdt, a.stockcode, a.fore_position, a.adj_amount_plan, a.post_position_plan' \ ', case when c.ForcastPeriod={topModelPeriod} then {holdAmt} else a.post_position_plan end' \ ', {dealprice}, a.exp_ret, a.real_ret' \ 'from OrdeBookDetail a' \ 'left join OrderBook b on a.Id=b.Id and a.AdjDt=b.AdjDt' \ 'left join MultiFactorOptimozor c on a.Id=c.Id'\ 'where a.stockcode=\'{stockcode}\' and b.IsClosed=0 and a.post_position is null'.format(topModelPeriod=topModelPeriod,holdAmt=holdAmt,dealprice=dealprice,stockcode=stockcode) fApi.conn.execute(markOpenSql) fApi.conn.commit()
def calcWeekDescriptor(self, stock_code: str, timepoint: str): try: period = self.params['period'] indexCode = self.params['index'] except Exception as e: raise ("计算股票beta缺少回溯期数period或参照指数index") timepoint = datetime.strptime(timepoint, '%Y%m%d') stockReturn = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint - timedelta(days=1), period=period, fields=['Close'], freq='d', adj='hfq')['Close'].pct_change() indexReturn = fundamentalApi.quotes( asset_code=indexCode, starttime=None, endtime=timepoint - timedelta(days=1), period=period, fields=['Close'], freq='d', adj=None, asset_type='index')['Close'].pct_change() mergeReturn = pd.DataFrame({ 'stock': stockReturn, 'index': indexReturn }) try: model = sm.OLS(mergeReturn['stock'].values.reshape(-1, 1), sm.add_constant(mergeReturn['index'].values.reshape( -1, 1)), missing='drop') rst = model.fit() Beta = rst.params[1] except Exception as e: Beta = np.nan return Beta
def calcWeekDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") size_df = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=1, fields='TotalMv', freq='d', adj=None) if (size_df.__len__() == 0): return np.nan else: return size_df.values[0][1]
def makeOptimizeOrder(self): #从表MultiFactorOptimizor中获取今日激活运行的优化器,从基层到 #高层(短周期)逐个执行; #每执行完一层改变一层当前对象base,并将新target插入OrderBook表; #执行前检查base周期是否可整除当前模型周期;若不可整除则跳过该层模型。 #每日第二个执行 rst = self.getActiveModelStackFromDB() num_of_models = rst.__len__() if(num_of_models==0): #本日非调仓日,无需操作 print('本日无模型调仓!') return else: print('本日以下'+num_of_models.__str__()+'个模型调仓:') [print('{id}号模型,预测期{p}日'.format(id=str(id),p=str(forcastperiod))) for id,model,optmizor,forcastperiod in rst] stk_price = fApi.quotes(asset_code=None,starttime=None,endtime=datetime.now(), period=1, fields='Close', freq='day', adj=None) #todo 这里是以收盘价格近似次日开盘价格,若当日为除权登记日(即这两个价格会有较大差异时)时不可直接近似,需要对stk_price做除权处理 stk_price.set_index('StockCode',inplace=True) cashrow = pd.DataFrame({'Close':1},index=['cash']) stk_price = stk_price.append(cashrow) stk_price = stk_price['Close'] for id,model,optmizor,forcastperiod in rst: model:MultiFactor stk_return = model.forcastStkReturn()['e_ret'] #multifactormodel.forcastStkCov需另写 OptimizeOutcome = portfolioOptimizor(stk_return = stk_return ,stk_price = stk_price ,stk_cov=model.forcastStkCov() ,stk_hold=self.base ,feeRate=0.003 ,method=optmizor ,gamma=0.1) target = OptimizeOutcome['after_adj_hold']['hold_quantity'].to_dict() #获取调整后持仓目标 exp_ret = OptimizeOutcome['expRet'] self.base = target #优化后,把新目标写入self.base、数据库OrderBook表、OrderBookDetail表 sql = 'insert into OrderBook(Id,AdjDt,exp_ret,IsClosed) values(?,?,?,?)' fApi.conn.execute(sql,(id,datetime.now().strftime('%Y%m%d'),exp_ret,0)) #再将目标插入OrderBookDetail表(内含现金行) for idx,row in OptimizeOutcome['after_adj_hold'].iterrows(): stockcode = idx fore_position = row['remain_quantity'] adj_amount_plan = row['trade_shares'] post_position_plan = row['hold_quantity'] stk_exp_ret = row['stk_return'] stk_sql = 'insert into OrderBookDetail(Id,AdjDt,stockcode,fore_position,adj_amount_plan,post_position_plan,exp_ret)' \ 'values (?,?,?,?,?,?,?)' fApi.conn.execute(stk_sql,(id,datetime.now().strftime('%Y%m%d'),stockcode,fore_position,adj_amount_plan,post_position_plan, stk_exp_ret)) fApi.conn.commit()
def calcDayDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") priceInfo = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=1, fields=['PB'], freq='d', adj=None) try: BTOP = 1 / priceInfo['PB'].values[0] except Exception as e: BTOP = np.nan return BTOP
def calcDayDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") try: n1 = self.params['n1'] #间隔期 n2 = self.params['n2'] #总长度 if (n2 < 1): raise ("n2至少为1!") except Exception as e: print("参数异常!") raise (e) priceInfo = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=1, fields=['Open', 'Close'], freq='d', adj=None)
def calcDayDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") try: N = self.params['N'] except Exception as e: print("params中必须输入计算非流动性回溯期N!") raise (e) priceInfo = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=N, fields=['Open', 'Close', 'Amount'], freq='d', adj=None) priceInfo['dIlliq'] = abs((priceInfo['Close'] / priceInfo['Open'] - 1)) / priceInfo['Amount'] * pow(10, 8) Illiquidity = priceInfo['dIlliq'].mean() return Illiquidity
def calcDayDescriptor(self, stock_code: str, timepoint: str): timepoint_dt = datetime.strptime(timepoint, "%Y%m%d") try: t = self.params['t'] #计算量价背离所用回溯期数 if (type(t) is not int or t < 1): raise Exception("回溯期t必须为大于1的整数!") except Exception as e: print("参数异常!") raise (e) priceInfo = fundamentalApi.quotes(asset_code=stock_code, starttime=None, endtime=timepoint_dt - timedelta(days=1), period=t, fields=['Volume'], freq='d', adj='hfq') try: abnormalVol = priceInfo['Volume'][0] / np.mean(priceInfo['Volume']) except: abnormalVol = np.nan return abnormalVol