Source code for RiskQuantLib

#!/usr/bin/python
#coding = utf-8
import argparse

[docs]def initiateBuildFile(): """ initiateBuildFile() is a function to generate content of build.py source file. build.py will be created in every RiskQuantLib project when initiating it. It is the entrance of build and render action. Users can either call 'python build.py' directly or add some parameter from command line. Returns ------- PYB : pythonScriptBuilder """ from RiskQuantLib.Tool.codeTool import pythonScriptBuilder, codeBuilderPython PYB = pythonScriptBuilder() PYB.setTitle() PYB.setImport('os,sys,argparse') PYB.setImport('RiskQuantLib','',True,'autoBuildProject,buildProject') PYB.code = codeBuilderPython(indent=0) PYB.code.addLine(r'path = sys.path[0] if not getattr(sys, "frozen", False) else os.path.dirname(sys.executable)') PYB.code.addLine(r'parser = argparse.ArgumentParser()') PYB.code.addLine(r'parser.add_argument("-a","--auto", help="use auto build model to build project dynamically", action="store_true")') PYB.code.addLine(r'parser.add_argument("-t", "--targetPath", type=str, help="the RiskQuantLib project you want to build")') PYB.code.addLine(r'parser.add_argument("-r", "--renderFromPath", type=str, help="the directory of source code where the template code exists")') PYB.code.addLine(r'parser.add_argument("-c", "--channel", type=str, help="if given a channel name, render action in this channel will not delete the result of render in other channel unless it is overwritten by current render")') PYB.code.addLine(r'parser.add_argument("-d", "--debug", help="use debug mode, break point in Src will start to effect", action="store_true")') PYB.code.addLine(r'parser.add_argument("-f", "--force", help="force to build, cached building information will be neglected, if auto-build mode is on, building information will be deleted for one time before auto-building", action="store_true")') PYB.code.addLine(r'args = parser.parse_args()') PYB.code.addLine(r'targetPath = args.targetPath if args.targetPath else path') PYB.code.addLine(r'renderFromPath = args.renderFromPath if args.renderFromPath else targetPath+os.sep+"Src"') PYB.code.addLine(r'bindType = args.channel if args.channel else "renderedSourceCode"') PYB.code.addLine(r'autoBuildProject(targetPath,renderFromPath,bindType,args.debug,args.force) if args.auto else buildProject(targetPath,renderFromPath,bindType,args.debug,args.force)') return PYB
[docs]def initiateMainFile(): """ initiateMainFile() is a function to generate content of main.py source file. main.py will be created in every RiskQuantLib project when initiating it. It is the entrance of all project. Users should call 'python main.py' to run the project. Returns ------- PYB : pythonScriptBuilder """ from RiskQuantLib.Tool.codeTool import pythonScriptBuilder, codeBuilderPython PYB = pythonScriptBuilder() PYB.setTitle() PYB.setImport('os,sys') PYB.setImport('RiskQuantLib.module','',True,'*') PYB.code = codeBuilderPython(indent=0) PYB.code.addLine(r'path = sys.path[0] if not getattr(sys, "frozen", False) else os.path.dirname(sys.executable)') PYB.code.addLine('print("Write Your Code Here : "+path+os.sep+"main.py")') return PYB
[docs]def initiateConfigFile(): """ initiateConfigFile() is a function to generate content of config.py source file. config.py will be created in every RiskQuantLib project when initiating it. It is the declaration file of all instruments to be used in project and their inheritance relationship, it also contains the attribute the would be used in this project. Returns ------- PYB : pythonScriptBuilder """ from RiskQuantLib.Tool.codeTool import pythonScriptBuilder, codeBuilderPython PYB = pythonScriptBuilder() PYB.setTitle() PYB.code = codeBuilderPython(indent=0) PYB.code.addLine('') PYB.code.addLine(r'#-|instrument: security, company, index, interest') PYB.code.addLine(r'#-|instrument: bond@security, stock@security, derivative@security, fund@security') PYB.code.addLine(r'#-|instrument: future@derivative, option@derivative') PYB.code.addLine('') PYB.code.addLine(r'#-|instrument-DefaultInstrumentType: security@Security, company@Company, index@Index, interest@Interest') PYB.code.addLine(r'#-|instrument-DefaultInstrumentType: bond@Bond, stock@Stock, derivative@Derivative, fund@Fund') PYB.code.addLine(r'#-|instrument-DefaultInstrumentType: future@Future, option@Option') return PYB
[docs]def initiateExecutableBuildShortcutFile(): """ initiateExecutableBuildShortcutFile() is a function to generate executable shortcut for different system. build.bat or build.sh will be created in every RiskQuantLib project when initiating it on Windows or linux. It is the shortcut of run build.py by console. Returns ------- PYB : pythonScriptBuilder """ import sys from RiskQuantLib.Tool.codeTool import pythonScriptBuilder, codeBuilderPython PYB = pythonScriptBuilder() PYB.code = codeBuilderPython(indent=0) if sys.platform in {'win32'}: PYB.code.addLine(r'''cd %~dp0''') PYB.code.addLine(r'''python build.py''') PYB.code.addLine(r'''pause''') elif sys.platform in {'darwin','linux','linux2'}: PYB.code.addLine(r'''DIR=$(cd $(dirname $0) && pwd)''') PYB.code.addLine(r'''cd $DIR''') PYB.code.addLine(r'''python build.py''') PYB.code.addLine(r'''sleep 3''') else: PYB.code.addLine('') return PYB
[docs]def initiateExecutableDebugShortcutFile(): """ initiateExecutableDebugShortcutFile() is a function to generate executable shortcut for different system. debug.bat or debug.sh will be created in every RiskQuantLib project when initiating it on Windows or linux. It is the shortcut of run build.py by console. Returns ------- PYB : pythonScriptBuilder """ import sys from RiskQuantLib.Tool.codeTool import pythonScriptBuilder, codeBuilderPython PYB = pythonScriptBuilder() PYB.code = codeBuilderPython(indent=0) if sys.platform in {'win32'}: PYB.code.addLine(r'''cd %~dp0''') PYB.code.addLine(r'''python build.py -a -d''') PYB.code.addLine(r'''pause''') elif sys.platform in {'darwin','linux','linux2'}: PYB.code.addLine(r'''DIR=$(cd $(dirname $0) && pwd)''') PYB.code.addLine(r'''cd $DIR''') PYB.code.addLine(r'''python build.py -a -d''') PYB.code.addLine(r'''sleep 3''') else: PYB.code.addLine('') return PYB
[docs]def parseBuildPath(targetPath: str, checkExist:bool = False): """ parseBuildPath() is a function to generate the paths of project related file. Parameters ---------- targetPath : str The path of target RiskQuantLib project directory. checkExist : bool If true, this function will check the existence of related path of target project. It will raise exception iin case of absence. Returns ------- rqlPath : str the path of RiskQuantLib directory configFilePath : str The path of config.py used as default build config file. buildCachePath : str the path of building cache """ import os rqlPath = targetPath + os.sep + "RiskQuantLib" configFilePath = targetPath + os.sep + "config.py" buildCachePath = rqlPath + os.sep + "Build" + os.sep + "buildInfo.pkl" if checkExist and (not os.path.isdir(rqlPath) or not os.path.exists(configFilePath)): raise Exception("The target directory should be a RiskQuantLib project, with directory named as RiskQuantLib and config.py in it!") return rqlPath, configFilePath, buildCachePath
[docs]def buildProjectFromConfig(targetPath: str, buildCachePath: str, configFilePath:str, renderFromPath: str, bindType: str = 'renderedSourceCode', debug: bool = False, force: bool = False): """ buildProjectFromConfig() is a function to build project according to config.py declaration. Parameters ---------- targetPath : str The path of target RiskQuantLib project directory. buildCachePath : str The path of building cache. configFilePath : str The path of config.py used as default build config file. renderFromPath : str The path of source code directory bindType : str The channel of binding action. Source code are rendered and injected into project by different channels, The source code injected by channel A will be not influenced by source code injected by channel B, unless the content of tag is overwritten by code in channel B. This is used when you have several builders and you want them to build into the same project. In this case, you should give a bindType for each render action to make sure they do not conflict with each other. debug : bool If false, the break point in Src will not be effective, only break point within instrument class will effect. If true, the class method defined in Src directory will be dynamically bound to instrument node class. Then the program will take .py file under .Src directory as a module and import it, bind the class method into specified class. This mode is useful when your code is still under development. You will not have to change between ./Src/somecode.py and target instrument class .py file to edit any code error. The break point will stop right under ./Src/somecode.py. force : bool If True, the cached building file buildInfo.pkl will be neglected, a new builder object will be created. This is useful when there are some mistakes in buildInfo.pkl, or error happens when caching buildInfo.pkl. In these cases, old buildInfo.pkl exists but can not be used. The traditional way to solve this problem is manually deleting this file and build whole project again. With this parameter specified as True, users can choose to build project no matter buildInfo.pkl exists or not. However, any information in old building will be deleted. If you use guardian projects, there could be problems, you will have to build all guardian projects again. Returns ------- None """ import os, time from RiskQuantLib.Build.builder import configBuilder if os.path.isfile(buildCachePath) and not force: buildObj = configBuilder.loadInfo(buildCachePath) else: buildObj = configBuilder(targetProjectPath=targetPath) buildObj.buildProject(configFilePath=configFilePath) buildObj.renderProject(renderFromPath,bindType,persist=False, debug=debug) print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "- Build Project Finished")
[docs]def newProject(targetPath:str = ''): """ newProject() is a function to create a new RiskQuantLib project. Use terminal command 'newRQL' to use this function. The terminal command 'newRQL' accept a parameter 'targetPathString', which is the path that you want to build RiskQuantLib project. If there is already a RiskQuantLib project in target path, it will be deleted and replaced by a new project. Parameters ---------- targetPath : str A terminal command parameter, specify the path where you want to build a new project. Returns ------- None """ if targetPath == '': parser = argparse.ArgumentParser() parser.add_argument("target", type=str, help="the target directory where you want to create a RiskQuantLib project") args = parser.parse_args() targetPath = args.target import os,shutil,sys RiskQuantLibDirectory = os.path.abspath(__file__).split('RiskQuantLib'+os.sep+'__init__')[0] sourcePath = os.path.abspath(RiskQuantLibDirectory)+os.sep+r'RiskQuantLib' rqlPath, configFilePath, buildCachePath = parseBuildPath(targetPath) if not os.path.exists(rqlPath): # if there is no target path, create one os.makedirs(rqlPath) if os.path.exists(sourcePath): # if there is already a path, clear it shutil.rmtree(rqlPath) shutil.copytree(sourcePath, rqlPath) # create config.py for build PYB = initiateConfigFile() PYB.writeToFile(configFilePath) # create build script PYB = initiateBuildFile() PYB.writeToFile(targetPath + os.sep + 'build.py') # create program start point PYB = initiateMainFile() PYB.writeToFile(targetPath+os.sep+'main.py') # decide file extension depending on operating system shortCutFileType = '.bat' if sys.platform in {'win32'} else '.sh' if sys.platform in {'darwin', 'linux', 'linux2'} else '' # create build.bat or build.sh shortcut PYB = initiateExecutableBuildShortcutFile() PYB.writeToFile(targetPath + os.sep + 'build'+shortCutFileType) # create debug.bat or debug.sh shortcut PYB = initiateExecutableDebugShortcutFile() PYB.writeToFile(targetPath + os.sep + 'debug'+shortCutFileType) # create python source file directory and other useful directory renderFromPath = targetPath+os.sep+'Src' cachePath = targetPath+os.sep+'Cache' dataPath = targetPath+os.sep+'Data' resultPath = targetPath+os.sep+'Result' [os.makedirs(p) if not os.path.exists(p) else None for p in [renderFromPath, cachePath, dataPath, resultPath]] print('RiskQuantLib project created!')
[docs]def packModule(targetPath: str = '', targetName: str = '', keepTop: bool = False): """ packModule() is a function to pack a RiskQuantLib project into '.zip' file. Use terminal command 'pkgRQL' to use this function. The terminal command 'pkgRQL' accept a parameter 'targetPathString', which is the path that you want to package. It doesn't need to have a directory named 'RiskQuantLib' to be packaged. Parameters ---------- targetPath : str A terminal command parameter, specify the path which you want to package. targetName : str A terminal command parameter, specify the name you want to mark the zip file with. keepTop : bool A terminal command parameter, specify if this function will keep the top level directory. Returns ------- None """ if targetPath == '' and targetName == '' and not keepTop: parser = argparse.ArgumentParser() parser.add_argument("target", type=str, help="the file or directory which you want to package into a zip file") parser.add_argument("-n", "--name", type=str, help="the name which you want to name the module by") parser.add_argument("-t", "--top", help="keep top level directory", action="store_true") args = parser.parse_args() targetPath = args.target targetName = args.name if args.name else '' keepTop = args.top import os, shutil, logging if targetName == '': name = os.path.splitext(os.path.basename(targetPath))[0] else: nameList = targetName.split('.') if len(nameList)>1: name = "".join(nameList[0:-1]) else: name = nameList[0] parentDirectory = os.path.dirname(targetPath) logger = logging.getLogger("nameOfTheLogger") ConsoleOutputHandler = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s - %(message)s') ConsoleOutputHandler.setFormatter(formatter) logger.addHandler(ConsoleOutputHandler) logger.setLevel(logging.INFO) zipAS = parentDirectory + os.sep + name try: if os.path.isdir(targetPath) and not keepTop: shutil.make_archive(zipAS, "zip", targetPath, logger=logger) print(f'RiskQuantLib module packaged: {targetPath}') elif os.path.isfile(targetPath) or keepTop: shutil.make_archive(zipAS, "zip", parentDirectory, os.path.basename(targetPath), logger=logger) print(f'RiskQuantLib module packaged: {targetPath}') else: raise FileExistsError(f'Target does not exist: {targetPath}') except Exception as e: failedFile = zipAS + '.zip' os.remove(failedFile) if os.path.isfile(failedFile) else None raise e
[docs]def checkAndCreateLibraryPath(moduleName: tuple = ('Template', 'Model', 'Tool')): """ checkAndCreateLibraryPath() is a function to check whether the Template path exists. Parameters ---------- moduleName : tuple The parameter tells how many sub-categories the library has. By default, it has Template, Model and Tool. Returns ------- sourcePath : str The path of RiskQuantLib template directory. """ import os RiskQuantLibDirectory = os.path.abspath(__file__).split('RiskQuantLib'+os.sep+'__init__')[0] RiskQuantLibDirectoryABS = os.path.abspath(RiskQuantLibDirectory) + os.sep + 'RQL' modulePath = tuple(RiskQuantLibDirectoryABS + os.sep + i for i in moduleName) [None if os.path.exists(s) else os.makedirs(s) for s in modulePath] return moduleName, modulePath, RiskQuantLibDirectoryABS
[docs]def checkModuleCategory(moduleCategory: str = ''): """This function will check the parameter value of moduleCategory is validated or not.""" import os _, _, RiskQuantLibDirectoryABS = checkAndCreateLibraryPath() moduleCategoryName = moduleCategory[0].upper() + moduleCategory[1:] sourcePath = RiskQuantLibDirectoryABS + os.sep + moduleCategoryName if not os.path.exists(sourcePath): existCategory = [i.lower() for i in os.listdir(RiskQuantLibDirectoryABS) if os.path.isdir(RiskQuantLibDirectoryABS+os.sep+i)] raise ValueError('No sub-category of RiskQuantLib library named as: ', moduleCategory, 'Existed categories are: ', existCategory) else: return moduleCategoryName, sourcePath
[docs]def addModule(moduleCategory: str = '', targetPath:str = '', targetName:str = ''): """ addModule() is a function to add a RiskQuantLib module '.zip' file to library. Use terminal command 'addRQL' to use this function. The terminal command 'addRQL' accept two parameters: 'moduleCategory' and 'targetPathString'. 'moduleCategory' is the sub-category of your library, by default, it can be template, model or tool. 'targetPathString' is the RiskQuantLib module '.zip' file path that you want to add to library. It has to be a '.zip' file to be added to library. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to add '.zip' file into. targetPath : str A terminal command parameter, specify the RiskQuantLib module '.zip' file path which you want to add to library. targetName : str The name you want to use to save the .zip file as, it is not necessary to add .zip behind it. Returns ------- None """ if moduleCategory == '' and targetPath == '' and targetName == '': parser = argparse.ArgumentParser() parser.add_argument("category", type=str, help="the sub-category of your library where you want to add module into") parser.add_argument("target", type=str, help="the zipped module file you want to add into RiskQuantLib library") parser.add_argument("-n", "--name", type=str, help="the name which you want to store the .zip file as") args = parser.parse_args() moduleCategory = args.category targetPath = args.target targetName = args.name if args.name else '' import os moduleCategoryName, sourcePath = checkModuleCategory(moduleCategory) if targetName == '': modulePackPath = os.path.splitext(targetPath)[0] name = modulePackPath.split(os.sep)[-1] else: nameList = targetName.split('.') if len(nameList) > 1: name = "".join(nameList[0:-1]) else: name = nameList[0] import shutil shutil.copy(targetPath,sourcePath+os.sep+name+'.zip') os.remove(targetPath) print(f'RiskQuantLib module added: {moduleCategoryName} -> {name}')
[docs]def saveModule(moduleCategory: str = '', targetPath: str = '', targetName: str = ''): """ saveModule() is a function to save a RiskQuantLib module and add it to library. Use terminal command 'saveRQL' to use this function. The terminal command 'saveRQL' accept two parameters: 'moduleCategory' and 'targetPath'. 'moduleCategory' is the sub-category of your library where you want to save file into, by default, it can be template, model or tool. 'targetPath' is the file or directory path that you want to save. There is also an optional parameter 'targetName', which is the name you want to give to this module. After calling this function, a '.zip' file will be created in the target directory, and this file will be stored as a module. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to save '.zip' file into. targetPath : str A terminal command parameter, specify the file or directory path which you want to save as template. targetName : str A terminal command parameter, specify the name you want to save this module as. Returns ------- None """ if moduleCategory == '' and targetPath == '' and targetName == '': parser = argparse.ArgumentParser() parser.add_argument("category", type=str, help="the sub-category of your library where you want to save module into") parser.add_argument("target", type=str, help="the path of file or directory which you want to save into RiskQuantLib library") parser.add_argument("-n", "--name", type=str, help="the name which you want to store the module as") args = parser.parse_args() moduleCategory = args.category targetPath = args.target targetName = args.name if args.name else '' import os checkModuleCategory(moduleCategory) parentDir = os.path.dirname(targetPath) name = os.path.splitext(os.path.basename(targetPath))[0] if targetName == '' else targetName keepTop = False if moduleCategory in {'template', 'Template'} else True packModule(targetPath=targetPath, targetName=name, keepTop=keepTop) addModule(moduleCategory=moduleCategory, targetPath=parentDir+os.sep+name+'.zip', targetName=name)
[docs]def unpackModule(moduleCategory: str = '', moduleName: str = '', targetPath: str = ''): """ unpackModule() is a function to unpack a RiskQuantLib module from library and use it again. Use terminal command 'tplRQL' to use this function. The terminal command 'tplRQL' accept two parameters: 'moduleCategory' and 'moduleName', 'moduleCategory' is the sub-category of your library where you want to unpack module from, by default, it can be template, model or tool. 'moduleName' is the module name you want to unpack from library. 'targetPathString' is the path where you want to unpack RiskQuantLib module into. After calling this function, the content of existing RiskQuantLib module will be unpacked to the location you choose, and you can start a project at the foundation of this un-packed module, or use the functions or models of un-packed module. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to un-pack '.zip' file from. moduleName : str A terminal command parameter, specify the module name you want to unpack from library. targetPath : str A terminal command parameter, specify the path where you want to un-pack module into. Returns ------- None """ if moduleCategory == '' and moduleName == '' and targetPath == '': parser = argparse.ArgumentParser() parser.add_argument("category", type=str, help="the sub-category of your library where you want to un-pack module from") parser.add_argument("module", type=str, help="the name of saved RiskQuantLib module") parser.add_argument("target", type=str, help="the path where you want to unpack the module into") args = parser.parse_args() moduleCategory = args.category moduleName = args.module targetPath = args.target import os moduleCategoryName, sourcePath = checkModuleCategory(moduleCategory) # use index number to locate template if str.isdigit(moduleName): moduleNameDict = {idx:i.replace('.zip', '') for idx, i in enumerate(os.listdir(sourcePath))} templateIndex = int(moduleName) moduleName = moduleNameDict[templateIndex] if templateIndex in moduleNameDict else moduleName # change targetPath targetPath = targetPath + os.sep + 'RiskQuantLib' + os.sep + 'Model' if moduleCategoryName == 'Model' else targetPath + os.sep + 'RiskQuantLib' + os.sep + 'Tool' if moduleCategoryName == 'Tool' else targetPath os.makedirs(targetPath) if not os.path.exists(targetPath) else None import shutil shutil.unpack_archive(sourcePath+os.sep+moduleName+'.zip',targetPath,"zip") if os.path.exists(targetPath+os.sep+moduleName+'.zip'): os.remove(targetPath+os.sep+moduleName+'.zip') print(f'RiskQuantLib module unpack finished: {moduleCategoryName} -> {moduleName}')
[docs]def listItem(hints: str, itemList: list): print(hints) print("".join(['-' for i in range(len(hints))])) [print(index,"->",name) for index,name in enumerate(itemList)]
[docs]def listModule(moduleCategory: str = ''): """ listModule() is a function to show all RiskQuantLib modules from library. Use terminal command 'listRQL' to use this function. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to show. Returns ------- None """ if moduleCategory == '': parser = argparse.ArgumentParser() parser.add_argument("-c", "--category", type=str, help="the sub-category of your library that you want to show") args = parser.parse_args() moduleCategory = args.category if args.category else '' import os _, _, RiskQuantLibDirectoryABS = checkAndCreateLibraryPath() moduleCategoryList = [i for i in os.listdir(RiskQuantLibDirectoryABS) if os.path.isdir(RiskQuantLibDirectoryABS+os.sep+i)] if moduleCategory == '' else [moduleCategory[0].upper() + moduleCategory[1:]] sourcePathList = [(i, RiskQuantLibDirectoryABS + os.sep + i) for i in moduleCategoryList] moduleFileList = [(moduleCategoryName, [i.replace('.zip','') for i in os.listdir(sourcePath)]) for moduleCategoryName, sourcePath in sourcePathList if os.path.isdir(sourcePath)] [listItem(f"\nShow all RiskQuantLib {moduleCategoryName}:", moduleFile) for moduleCategoryName, moduleFile in moduleFileList]
[docs]def deleteModule(moduleCategory: str = '', targetName: str = ''): """ deleteModule() is a function to delete a RiskQuantLib module from library. Use terminal command 'delRQL' to use this function. The terminal command 'delRQL' accept two parameters: 'moduleCategory' and 'targetName'. 'moduleCategory' is the sub-category of your library where you want to delete module from, by default, it can be template, model or tool. 'targetName' is the module name you want to delete. After calling this function, the existing RiskQuantLib module will be removed from library. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to delete module from. targetName : str A terminal command parameter, specify the module name you want to delete from library. Returns ------- None """ if moduleCategory == '' and targetName == '': parser = argparse.ArgumentParser() parser.add_argument("category", type=str, help="the sub-category of your library where you want to delete module from") parser.add_argument("target", type=str, help="the name of module which you will delete") args = parser.parse_args() moduleCategory = args.category targetName = args.target import os moduleCategoryName, sourcePath = checkModuleCategory(moduleCategory) # use index number to locate template if str.isdigit(targetName): moduleNameDict = {idx: i.replace('.zip', '') for idx, i in enumerate(os.listdir(sourcePath))} templateIndex = int(targetName) targetName = moduleNameDict[templateIndex] if templateIndex in moduleNameDict else targetName moduleNameList = [i.replace('.zip','') for i in os.listdir(sourcePath)] if targetName in moduleNameList: os.remove(sourcePath+os.sep+targetName+'.zip') print(f"Delete RiskQuantLib module succeeded: {moduleCategoryName} -> {targetName}") else: print(f"There is no RiskQuantLib module: {moduleCategoryName} -> {targetName}")
[docs]def clearAllModule(): """ clearAllModule() is a function to delete all RiskQuantLib modules from library. Use terminal command 'clearRQL' to use this function. After calling this function, all existing RiskQuantLib modules will be removed. Returns ------- None """ confirm = input("This action can not be canceled, are you sure to move on? (y/n)") if confirm.lower() != 'y': return None else: import os _, _, RiskQuantLibDirectoryABS = checkAndCreateLibraryPath() absPathList = [(i, RiskQuantLibDirectoryABS + os.sep + i) for i in os.listdir(RiskQuantLibDirectoryABS)] sourcePathList = [(n, p) for n, p in absPathList if os.path.isdir(p)] for moduleCategory, sourcePath in sourcePathList: moduleNameList = [i.replace('.zip','') for i in os.listdir(sourcePath)] [os.remove(sourcePath+os.sep+targetName+'.zip') for targetName in moduleNameList] print(f"Delete all RiskQuantLib module finished: {moduleCategory}")
[docs]def setDefaultModule(moduleCategory: str = '', moduleName: str = ''): """ setDefaultModule() is a function to set a module into default initialization one. Use terminal command 'dftRQL' to use this function. The terminal command 'dftRQL' accept two parameters: 'moduleCategory' and 'moduleName'. 'moduleCategory' is the sub-category of your library where you want to set module as default from, by default, it can be model or tool. 'moduleName' is the module name you want to set as default one. After calling this function, the existing RiskQuantLib module will be set as default, which means every new RiskQuantLib project created by 'newRQL' command will use this module. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to set module as default from. moduleName : str A terminal command parameter, specify the module name you want to set as default. Returns ------- None """ if moduleCategory == '' and moduleName == '': parser = argparse.ArgumentParser() parser.add_argument("category", type=str, help="the sub-category of your library where you want to set module as default from") parser.add_argument("module", type=str, help="the name of module which you will set as default") args = parser.parse_args() moduleCategory = args.category moduleName = args.module import os defaultCategory = {'Model', 'Tool'} moduleCategoryName = moduleCategory[0].upper() + moduleCategory[1:] if moduleCategoryName not in defaultCategory: raise ValueError('Given sub-category is not supported to set default value: ', moduleCategory, 'Current supported sub-category of default is: ', [i.lower() for i in defaultCategory]) else: RiskQuantLibDirectory = os.path.abspath(__file__).split('RiskQuantLib' + os.sep + '__init__')[0] targetPath = os.path.abspath(RiskQuantLibDirectory) unpackModule(moduleCategory, moduleName, targetPath)
[docs]def removeDefaultModule(moduleCategory: str = '', moduleName: str = ''): """ setDefaultModule() is a function to remove a module from default initialization. Use terminal command 'udftRQL' to use this function. The terminal command 'udftRQL' accept two parameters: 'moduleCategory' and 'moduleName'. 'moduleCategory' is the sub-category of your library where you want to remove module from, by default, it can be model or tool. 'moduleName' is the module name you want to set as default one. After calling this function, the RiskQuantLib module will be removed from default, which means every new RiskQuantLib project created by 'newRQL' command will not use this module anymore. Parameters ---------- moduleCategory : str A terminal command parameter, specify the library sub-category you want to remove module from. moduleName : str A terminal command parameter, specify the module name you want to remove from default. Returns ------- None """ if moduleCategory == '' and moduleName == '': parser = argparse.ArgumentParser() parser.add_argument("category", type=str, help="the sub-category of your library where you want to remove module from") parser.add_argument("module", type=str, help="the name of module which you will remove from default") args = parser.parse_args() moduleCategory = args.category moduleName = args.module import os, shutil defaultCategory = {'Model', 'Tool'} moduleCategoryName = moduleCategory[0].upper() + moduleCategory[1:] if moduleCategoryName not in defaultCategory: raise ValueError('Given sub-category is not supported to remove default value: ', moduleCategory, 'Current supported sub-category of default is: ', [i.lower() for i in defaultCategory]) else: RiskQuantLibDirectory = os.path.abspath(__file__).split('RiskQuantLib' + os.sep + '__init__')[0] sourcePath = os.path.abspath(RiskQuantLibDirectory) + os.sep + 'RiskQuantLib' + os.sep + moduleCategoryName modulePath = sourcePath + os.sep + moduleName if not os.path.exists(modulePath): moduleList = [i for i in os.listdir(sourcePath)] moduleDict = {os.path.splitext(i)[0]: i for i in moduleList} moduleName = moduleDict[moduleName] if moduleName in moduleDict else moduleName modulePath = sourcePath + os.sep + moduleName saveModule(moduleCategory, modulePath) shutil.rmtree(modulePath) if os.path.isdir(modulePath) else os.remove(modulePath) print(f'RiskQuantLib module removed from default: {moduleCategoryName} -> {moduleName}')
[docs]def initDefaultModule(): """This function is used to initialize default module when installing or upgrading RiskQuantLib.""" initialModule = [ ('model', 'Copula'), ('model', 'KMV'), ('tool', 'excelTool'), ('tool', 'frameTool'), ('tool', 'guiTool'), ('tool', 'mailTool'), ('tool', 'pptTool'), ('tool', 'stringTool'), ('tool', 'threadTool'), ('tool', 'wordTool') ] for moduleCategory, moduleName in initialModule: try: removeDefaultModule(moduleCategory, moduleName) except Exception as e: print(f'RiskQuantLib default module initialization failed: {moduleCategory} -> {moduleName}')
[docs]def addModuleFromGithub(targetGithub:str = ''): """ addModuleFromGithub() is a function to download template from Github to local disk. Use terminal command 'getRQL' to use this function. After this function is called, the target repository will be saved as template project. Parameters ---------- targetGithub : str A terminal command parameter, specify the project name you want to download from Github. Returns ------- None """ if targetGithub == '': parser = argparse.ArgumentParser() parser.add_argument("targetGithub", type=str, help="the name of Github repository or the link of Github repository") args = parser.parse_args() targetGithub = args.targetGithub import os _, _, RiskQuantLibDirectoryABS = checkAndCreateLibraryPath() sourcePath = RiskQuantLibDirectoryABS + os.sep + 'Template' from RiskQuantLib.Tool.githubTool import Github link = Github() link.downloadRepositories(targetGithub,sourcePath)
[docs]def receiveModule(targetPath: str = ''): """ receiveModule() is a function to receive any file or directory from your friend by LOCAL AREA NETWORK (LAN). Use terminal command 'recvRQL' to use this function. You can also specify a path where your want to save the shared file or directory, like 'recvRQL targetPath'. If you do not give a path, the file will be stored in current working directory. After this function is called, you can receive the file shared from your friend, who is also in the same LAN. You can not receive files or project from people outside your local network by this function. If you want to share with friends who is across ocean, maybe you should use Github and getRQL command. Parameters ---------- targetPath : str A terminal command parameter, specify the path where you want to hold the received file. Returns ------- None """ import os if targetPath == '': parser = argparse.ArgumentParser() parser.add_argument("-t","--targetPath", type=str, help="the path where you want to save the received files into, default as current working directory") args = parser.parse_args() targetPath = args.targetPath if args.targetPath else os.getcwd() from RiskQuantLib.Tool.fileTool import fileReceiver receive = fileReceiver(targetPath) receive.run()
[docs]def sendModule(targetPath: str = '') -> None: """ sendModule() is a function to send any file or directory to your friend by LOCAL AREA NETWORK (LAN). Use terminal command 'sendRQL targetProjectPath' or 'sendRQL targetFilePath' to use this function. If you send a directory, it will be packed into a zip file at first, and sent to your friend. After this function is called, you can send to your friend who is also in the same LAN. You can not send files or project to people outside your local network by this function. If you want to share with friends who is across ocean, maybe you should use Github and getRQL command. Parameters ---------- targetPath : str A terminal command parameter, specify the path of file or directory you want to send. Returns ------- None """ if targetPath == '': parser = argparse.ArgumentParser() parser.add_argument("targetPath", type=str, help="the path of directory or file you want to send, if it's a directory, it will be packaged into a zip file first") args = parser.parse_args() targetPath = args.targetPath import os from RiskQuantLib.Tool.fileTool import fileSender if os.path.isdir(targetPath): packModule(targetPath=targetPath) name = targetPath.split(os.sep)[-1] parentModulePath = os.path.dirname(targetPath) filePath = parentModulePath + os.sep + name + ".zip" send = fileSender(filePath) send.run() os.remove(parentModulePath + os.sep + name + '.zip') else: filePath = targetPath send = fileSender(filePath) send.run()
[docs]def buildProject(targetPath:str = '', renderFromPath:str = '', channel:str = '', debug: bool = False, force: bool = False): """ buildProject() is a function to build RiskQuantLib project. Use terminal command 'bldRQL targetProjectPath' to use this function. The project will be built according to the targetProjectPath/config.py in the targetProjectPath. After this function is called, the instrument class file and attribute API will be automatically generated. For old version user of RiskQuantLib, this function is totally the same as command 'python build.py' in terminal with working directory as targetProjectPath. Parameters ---------- targetPath : str A terminal command parameter, specify the RiskQuantLib project path you want to build and render. renderFromPath : str The path of directory of source file used to render target project. channel : str render action in this channel will not delete the result of render in other channel unless it is overwritten by current render. debug : bool If false, the break point in Src will not be effective, only break point within instrument class will effect. If true, the class method defined in Src directory will be dynamically bound to instrument node class. Then the program will take .py file under .Src directory as a module and import it, bind the class method into specified class. This mode is useful when your code is still under development. You will not have to change between ./Src/somecode.py and target instrument class .py file to edit any code error. The break point will stop right under ./Src/somecode.py. force : bool If True, the cached building file buildInfo.pkl will be neglected, a new builder object will be created. This is useful when there are some mistakes in buildInfo.pkl, or error happens when caching buildInfo.pkl. In these cases, old buildInfo.pkl exists but can not be used. The traditional way to solve this problem is manually deleting this file and build whole project again. With this parameter specified as True, users can choose to build project no matter buildInfo.pkl exists or not. However, any information in old building will be deleted. If you use guardian projects, there could be problems, you will have to build all guardian projects again. Returns ------- None """ if targetPath == '' and renderFromPath == '': parser = argparse.ArgumentParser() parser.add_argument("targetPath", type=str, help="the RiskQuantLib project you want to build") parser.add_argument("-r","--renderFromPath", type=str, help="the directory of source code where the template code exists") parser.add_argument("-c", "--channel", type=str, help="if given a channel name, render action in this channel will not delete the result of render in other channel unless it is overwritten by current render") parser.add_argument("-d", "--debug", help="use debug mode, break point in Src will start to effect", action="store_true") parser.add_argument("-f", "--force", help="force to build, cached building information will be neglected", action="store_true") args = parser.parse_args() targetPath = args.targetPath renderFromPath = args.renderFromPath channel = args.channel debug = args.debug force = args.force import os renderFromPath = renderFromPath if renderFromPath else (targetPath + os.sep + "Src") bindType = channel if channel else 'renderedSourceCode' rqlPath, configFilePath, buildCachePath = parseBuildPath(targetPath, checkExist=True) buildProjectFromConfig(targetPath, buildCachePath, configFilePath, renderFromPath, bindType, debug, force)
[docs]def autoBuildProject(targetPath:str = '', renderFromPath:str = '', channel:str = '', debug: bool = False, force: bool = False): """ autoBuildProject() is a function to build RiskQuantLib project. This function keeps running until catch a KeyboardInterrupt Exception. Use terminal command 'autoRQL targetProjectPath' to use this function. The project will be built according to the targetProjectPath/config.py in the targetProjectPath. After this function is called, the instrument class file and attribute API will be automatically generated and updated. Parameters ---------- targetPath : str A terminal command parameter, specify the RiskQuantLib project path you want to build and render. renderFromPath : str The path of directory of source file used to render target project. channel : str render action in this channel will not delete the result of render in other channel unless it is overwritten by current render. debug : bool If false, the break point in Src will not be effective, only break point within instrument class will effect. If true, the class method defined in Src directory will be dynamically bound to instrument node class. Then the program will take .py file under .Src directory as a module and import it, bind the class method into specified class. This mode is useful when your code is still under development. You will not have to change between ./Src/somecode.py and target instrument class .py file to edit any code error. The break point will stop right under ./Src/somecode.py. force : bool If True, the cached building file buildInfo.pkl will be deleted, a new builder object will be created. This is useful when there are some mistakes in buildInfo.pkl, or error happens when caching buildInfo.pkl. In these cases, old buildInfo.pkl exists but can not be used. If this parameter is specified as True, buildInfo.pkl will be deleted before auto-building. However, any information in old building will be deleted. If you use guardian projects, there could be problems, you will have to build all guardian projects again. Another thing to notice is the action of parameter force in autoBuildProject is different with that in buildProject or persistProject. buildInfo.pkl will be neglected in buildProject and persistProject, but will be deleted for a single time before auto-building. Returns ------- None """ if targetPath == '' and renderFromPath == '': parser = argparse.ArgumentParser() parser.add_argument("targetPath", type=str, help="the RiskQuantLib project you want to build automatically") parser.add_argument("-r", "--renderFromPath", type=str, help="the directory of source code where the template code exists") parser.add_argument("-c", "--channel", type=str, help="if given a channel name, render action in this channel will not delete the result of render in other channel unless it is overwritten by current render") parser.add_argument("-d", "--debug", help="use debug mode, break point in Src will start to effect", action="store_true") parser.add_argument("-f", "--force", help="force to build, cached building information will be neglected", action="store_true") args = parser.parse_args() targetPath = args.targetPath renderFromPath = args.renderFromPath channel = args.channel debug = args.debug force = args.force import os renderFromPath = renderFromPath if renderFromPath else (targetPath + os.sep + "Src") bindType = channel if channel else 'renderedSourceCode' rqlPath, configFilePath, buildCachePath = parseBuildPath(targetPath, checkExist=True) # Delete cached build information if force auto-building os.remove(buildCachePath) if force and os.path.isfile(buildCachePath) else None # The call back function must be a single parameter function def build(projectPath=targetPath): try: buildProjectFromConfig(targetPath,buildCachePath,configFilePath,renderFromPath, bindType, debug, force=False) except Exception as e: import time print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "- Build Project Failed: ", e) from RiskQuantLib.Tool.fileTool import systemWatcher watchObj = systemWatcher([configFilePath, renderFromPath], call_back_function_on_any_change=build, withFormat=True, monitorFormat={'.py','.pyt'}) watchObj.start()
[docs]def unBuildProject(targetPath:str = ''): """ unBuildProject() is a function to un-build RiskQuantLib project. Use terminal command 'ubldRQL targetProjectPath' to use this function. The project will be un-built and return to the initial status. After this function is called, the attribute API will be automatically removed, any all registration of instrument will be deleted. But, python source file will not be deleted until you do it by yourself. After a project is un-built, you can not use instrument directly in main.py or create new instrument inherited from those un-registered instrument. The file config.py will not be changed after you call this function. Parameters ---------- targetPath : str A terminal command parameter, specify the RiskQuantLib project path you want to un-build and un-render. Returns ------- None """ if targetPath == '': parser = argparse.ArgumentParser() parser.add_argument("targetPath", type=str, help="the RiskQuantLib project you want to un-build") args = parser.parse_args() targetPath = args.targetPath import os rqlPath, configFilePath, buildCachePath = parseBuildPath(targetPath, checkExist=True) from RiskQuantLib.Build.builder import configBuilder buildObj = configBuilder.loadInfo(buildCachePath) if os.path.isfile(buildCachePath) else configBuilder(targetProjectPath=targetPath) buildObj.clearProject() print("Project un-build finished!")
[docs]def persistProject(targetPath: str = '', renderFromPath: str = '', channel: str = '', force: bool = False): """ persistProject() is a function to persist RiskQuantLib project. Use terminal command 'pstRQL targetProjectPath' to use this function. The project will be changed into a permanent project, where all current attribute APIs and instrument registrations will not be influenced by build.py anymore. This function is like a snapshot of your project, it freezes all effective APIs into permanent ones. Surely, you can still build the persisted project with new config.py file. Just remember, no matter how many times you build it, the persisted API will remain effective and will not be influenced. This command is used when you want to distribute your project to someone else, but you do not want him to change your current API. Or this command is used when you are quite sure your current code is stable and can be settled down so that you can move on to next stage. This command can not be cancelled or un-done, use it carefully. Parameters ---------- targetPath : str A terminal command parameter, specify the RiskQuantLib project path you want to persist. renderFromPath : str The path of directory of source file used to render target project. channel : str render action in this channel will not delete the result of render in other channel unless it is overwritten by current render. force : bool If True, the cached building file buildInfo.pkl will be neglected, a new builder object will be created. This is useful when there are some mistakes in buildInfo.pkl, or error happens when caching buildInfo.pkl. In these cases, old buildInfo.pkl exists but can not be used. The traditional way to solve this problem is manually deleting this file and build whole project again. With this parameter specified as True, users can choose to build project no matter buildInfo.pkl exists or not. However, any information in old building will be deleted. If you use guardian projects, there could be problems, you will have to build all guardian projects again. Returns ------- None """ if targetPath == '' and renderFromPath == '': parser = argparse.ArgumentParser() parser.add_argument("targetPath", type=str, help="the RiskQuantLib project whose code you want to change into permanent") parser.add_argument("-r", "--renderFromPath", type=str, help="the directory of source code where the template code exists") parser.add_argument("-c", "--channel", type=str, help="if given a channel name, render action in this channel will not delete the result of render in other channel unless it is overwritten by current render") parser.add_argument("-f", "--force", help="force to persist, cached building information will be neglected", action="store_true") args = parser.parse_args() targetPath = args.targetPath renderFromPath = args.renderFromPath channel = args.channel force = args.force import os rqlPath, configFilePath, buildCachePath = parseBuildPath(targetPath, checkExist=True) renderFromPath = renderFromPath if renderFromPath else (targetPath + os.sep + "Src") bindType = channel if channel else 'renderedSourceCode' confirm = input("This action can not be Un-Done or Cancelled, do you confirm to continue? (y/n)") if confirm.lower()=='y': from RiskQuantLib.Build.builder import configBuilder buildProject(targetPath,renderFromPath,channel,False,force) if not os.path.isfile(buildCachePath) or force else None buildObj = configBuilder.loadInfo(buildCachePath) buildObj.persistProject(sourceCodeDirPath=renderFromPath,bindType=bindType) configFile = initiateConfigFile() configFile.writeToFile(configFilePath) print("Project persisted!") else: print("Action cancelled, nothing changed!")
[docs]def copyProject(fromProjectPath: str = '', toProjectPath: str = ''): """ copyProject() is a function to copy RiskQuantLib project to another directory. Use terminal command 'copyRQL fromProjectPath toProjectPath' to use this function. The project will be copied into the specified directory, if toProjectPath does not exist, it will be created. This function will copy every file in current project into new directory except those files which are under path fromProjectPath/RiskQuantLib. If files with the same name already exist in toProjectPath, then they will be overwritten, so use it carefully. Parameters ---------- fromProjectPath : str A terminal command parameter, specify the RiskQuantLib project path you want to copy from. toProjectPath : str The path where you want to copy RiskQuantLib project to. Returns ------- None """ if fromProjectPath == '' and toProjectPath == '': parser = argparse.ArgumentParser() parser.add_argument("fromProjectPath", type=str, help="the path of RiskQuantLib project which you want to copy files from") parser.add_argument("toProjectPath", type=str, help="the path of RiskQuantLib project which you want to copy files into") args = parser.parse_args() fromProjectPath = args.fromProjectPath toProjectPath = args.toProjectPath import os fromProjectPath = os.path.abspath(fromProjectPath) toProjectPath = os.path.abspath(toProjectPath) if os.path.isdir(fromProjectPath): import shutil newProject(toProjectPath) dirsAndFiles = [(i, fromProjectPath+os.sep+i, toProjectPath+os.sep+i) for i in os.listdir(fromProjectPath)] [shutil.copy(f, t) for i, f, t in dirsAndFiles if os.path.isfile(f)] [shutil.copytree(f, t, dirs_exist_ok=True) for i, f, t in dirsAndFiles if os.path.isdir(f) and i != 'RiskQuantLib'] print("Project copied from ", fromProjectPath, " to ", toProjectPath) else: print("You must specify a validated path which you want to copy files from")