def loadAssetTypeSpecialCaseFromFile(file): """ [String] file => [Dictionary] ID -> [Dictionary] security info """ stringToTuple = compose(tuple, partial(map, lambda s: s.strip()), lambda s: s.split(',')) updatePosition = lambda position: mergeDict( position , { 'Portfolio': str(int(position['Portfolio'])) \ if isinstance(position['Portfolio'], float) \ else position['Portfolio'] , 'AssetType': stringToTuple(position['AssetType']) } ) return \ compose( dict , partial(map, lambda p: (p['ID'], p)) , partial(map, updatePosition) , getRawPositions , fileToLines , partial(join, getDataDirectory()) )(file)
def getGenevaPositions(portfolio, date, mode): """ [String] portfolio, [String] date (yyyymmdd), [String] mode => [Iterator] Investment positions of the portfolio on that date """ """ [String] file (Geneva investment positions report, Excel format) => [Iterator] positions """ readGenevaInvestmentPositionFile = compose( partial( map, lambda p: mergeDict( p, {'Remarks1': 'Geneva investment positions report'})), lambda lines: getPositions(lines)[1], fileToLines, lambda file: lognContinue( 'readGenevaInvestmentPositionFile(): {0}'.format(file), file)) """ [String] portfolio, [String] date (yyyymmdd), [String] mode => [String] file """ getGenevaInvestmentPositionFile = lambda portfolio, date, mode: \ join( getInputDirectory(mode) , portfolio + '_Investment_Positions_' + date + '.xlsx' ) return \ compose( readGenevaInvestmentPositionFile , getGenevaInvestmentPositionFile )(portfolio, date, mode)
def getAllPositionsBlp(date, mode): """ [String] date (yyyymmdd), [String] mode => [Iterator] positions of all portfolios on the date from Bloomberg """ getBlpPositionFile = lambda date, mode: \ join(getInputDirectory(mode), 'risk_m2_mav_' + date + '.xlsx') # [Iterable] lines => [List] line that contains the date findDateLine = partial( firstOf, lambda line: len(line) > 1 and line[1].startswith('Risk-Mon Steven')) # [String] The string containing date => [String] date (yyyymmdd) # it looks like: Risk Report LQA Master as of 20200429 getDateFromString = lambda s: s.split()[-1] getDateFromLines = compose( getDateFromString, lambda line: lognRaise('Failed to find date line') if line == None else line[1], findDateLine) floatToString = lambda x: str(int(x)) if isinstance(x, float) else x updatePosition = lambda date, position: \ mergeDict( position , { 'AsOfDate': date , 'Remarks1': 'Bloomberg MAV Risk-Mon Steven' , 'Account Code': floatToString(position['Account Code']) } ) getPositions = lambda date, lines: \ compose( partial(map, partial(updatePosition, date)) , partial(filterfalse, lambda p: p['Account Code'] == '') , getRawPositions , lambda lines: dropwhile(lambda line: line[0] != 'Name', lines) )(lines) return \ compose( lambda t: getPositions(t[0], t[1]) , lambda lines: (getDateFromLines(lines), lines) , fileToLines , lambda file: lognContinue('getAllPositionsBlp(): {0}'.format(file), file) , getBlpPositionFile )(date, mode)
def loadLiquiditySpecialCaseFromFile(file): """ [String] file => [Dictionary] id -> [Dictionary] liquidity data """ toDate = lambda x: \ fromExcelOrdinal(x) if isinstance(x, float) else \ datetime.strptime(x, '%m/%d/%Y') updatePosition = lambda position: mergeDict( position, {'CALC_MATURITY': toDate(position['CALC_MATURITY'])}) return \ compose( dict , partial(map, lambda p: (p['ID'], p)) , partial(map, updatePosition) , getRawPositionsFromFile )(file)
def getGenevaLqaPositions(positions): """ [Iterable] positions => [Iterable] positions Read Geneva consolidated tax lot positions, then do the following: 1) take out those not suitable for liquidity test (cash, FX forward, etc.); 2) Add Id, IdType and Position fields for LQA processing. """ # [Dictonary] p => [Dictionary] enriched position with id and idType addIdnType = compose( lambda t: mergeDict(t[2], {'Id': t[0], 'IdType': t[1]}) , lambda p: (*getIdnType(p), p) ) return compose( partial(map, addIdnType) , partial(filterfalse, noNeedLiquidityGeneva) , lambda positions: lognContinue('getGenevaLqaPositions(): start', positions) )(positions)
def getBlpLqaPositions(positions): """ [Iterable] positions => ( [Iterable] CLO positions , [Iterable] nonCLO positions ) Read Bloomberg raw positions, then do the following: 1) take out those not suitable to do liquidity test (cash, FX forward, etc.); 2) take out DIF fund positions, since they will come from Geneva; 2) split into clo and nonCLO positions. Return (CLO positions, nonCLO positions) """ removeUnwantedPositions = compose( partial( filterfalse , lambda p: p['Asset Type'] in [ 'Cash', 'Foreign Exchange Forward' , 'Repo Liability', 'Money Market'] \ or p['Name'] in ['.FSFUND HK', 'CLFLDIF HK'] # open ended funds ) , partial(filterfalse, lambda p: p['Position'] == '' or p['Position'] <= 0) ) # [Dictionary] position => [Dictioanry] position with id and idtype updatePositionId = compose( lambda t: mergeDict(t[2], {'Id': t[0], 'IdType': t[1]}) , lambda position: (*getIdnType(position), position) ) isCLOPortfolio = lambda p: p['Account Code'] in \ ['12229', '12734', '12366', '12630', '12549', '12550', '13007'] """ [Iterable] positions => ( [Iterable] CLO positions , [Iterable] non CLO positions ) Split the positions into All, CLO and non-CLO group """ splitCLO = lambda positions: \ reduce( lambda acc, el: ( chain(acc[0], [el]) , acc[1] ) if isCLOPortfolio(el) else \ ( acc[0] , chain(acc[1], [el]) ) , positions , ([], []) ) return \ compose( splitCLO , partial(map, updatePositionId) , removeUnwantedPositions , lambda positions: lognContinue('getBlpLqaPositions(): start', positions) )(positions)
Example: [ {'Id': '1 HK', 'Position': 100, ...} , {'Id': '1 HK', 'Position': 200, ...} ] => {'Id': '1 HK', 'Position': 300, ...} NOTE: consolidated position is hard to tell whether it is a blp position or a geneva position. Therefore we 'Position' field to store the total quantity. """ consolidateGroup = lambda group: \ mergeDict(group[0].copy(), {'Position': sum(map(getQuantity, group))}) """[Iterable] positions => [Iterable] consolidated positions""" consolidate = compose( partial(map, consolidateGroup) , lambda d: d.values() , partial(groupbyToolz, lambda p: p['Id']) ) def lognContinue(msg, x): logger.debug(msg) return x
def updateSecurityId(p): if len(p['SECURITIES'].split()[0]) == 12: # it's ISIN return mergeDict(p, {'SECURITIES': p['SECURITIES'].split()[0]}) else: return p