如何构建回测系统(八)

如何构建回测系统(八)

代码升级问题

回测系统必然涉及策略的反复测试及重新部署

重写ClassLoader

为了将代码存放在不同的位置,我们需要重写ClassLoader用以加载二进制代码,我们选用数据库作为代码的存放处,方便查询

重写Classloader中findClass函数

需要注意关键点是,defineClass对同一个class只能解析一次(JVM限制),所以我们需要解析后将Class用Map缓存起来

如果需要更新同名class,需要将Classloader丢弃掉,并新建一个ClassLoader实例(JVM在认为一个相同的Class是指相同的Classloader+全限名则是相同)

实现JavaCompilerInMemory

为了让前端页面能动态的修改class类,需要将.java的内容通过Java代码进行编译
参考代码:

    public byte[] compile() {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        var diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
        var fileManager = new MyJavaFileObjectManager(compiler.getStandardFileManager(diagnosticCollector, null, StandardCharsets.UTF_8));
        var javaFileObjects = Collections.singletonList(new MyJavaFileObject(getName(), codes, byteArrayOutputStream));
        fileManager.outputStream = byteArrayOutputStream;

        var compilerTask = compiler.getTask(null, fileManager, diagnosticCollector, null, null, javaFileObjects);
        if (compilerTask.call()) {
            return byteArrayOutputStream.toByteArray();
        } else {
            StringBuilder stringBuilder = new StringBuilder();
            diagnosticCollector.getDiagnostics().forEach(diagnostic -> stringBuilder.append(diagnostic.getMessage(Locale.CHINESE)));
            throw new RunException(stringBuilder.toString());
        }
    }

    static class MyJavaFileObjectManager extends ForwardingJavaFileManager<JavaFileManager> {

        Map<String, JavaFileObject> fileObjects = new HashMap<>();
        OutputStream outputStream;

        public MyJavaFileObjectManager(JavaFileManager fileManager) {
            super(fileManager);
        }

        @Override
        public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
            JavaFileObject javaFileObject = fileObjects.get(className);
            if (javaFileObject == null) {
                return super.getJavaFileForInput(location, className, kind);
            }
            return javaFileObject;
        }

        @Override
        public JavaFileObject getJavaFileForOutput(Location location, String qualifiedClassName, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
            JavaFileObject javaFileObject = new MyJavaFileObject(qualifiedClassName, kind, outputStream);
            fileObjects.put(qualifiedClassName, javaFileObject);
            return javaFileObject;
        }
    }

    static class MyJavaFileObject extends SimpleJavaFileObject {
        String source;
        OutputStream outputStream;
        String name;

        public MyJavaFileObject(String name, Kind kind, OutputStream stream) {
            super(URI.create("String:///" + name + kind.extension), kind);
            this.outputStream = stream;
        }

        public MyJavaFileObject(String name, String source) {
            super(URI.create("String:///" + name + Kind.SOURCE.extension), Kind.SOURCE);
            this.source = source;
            this.name = name;
        }

        public MyJavaFileObject(String name, String source, OutputStream outputStream) {
            this(name, source);
            this.outputStream = outputStream;
        }

        public MyJavaFileObject(URI uri, Kind kind) {
            super(uri, kind);
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
            return source;
        }

        @Override
        public OutputStream openOutputStream() throws IOException {
            return outputStream;
        }
    }

最终

通过CURD将上述的ClassLoader和JavaCompilerInMemory通过自己的业务穿起来,就可以实现在网页编写java代码,并动态修改和运行

socket编程日记

socket编程日记

自定义分割包协议

注意到,socket发送出去的包最大只能为tcp的mtu长度减去包头长度,超过长度的包会分批到达

TCP保证了包到达的顺序及正确性

UDP需要自己实现包顺序及异常校验

对端如果没有分包协议会导致一次性读过头或者没读完就进行数据操作

分包协议的两种方式

包头放置数据

  • 特征值
  • 数据长度
  • 数组字段
接收端伪代码形式
header{
    len
}
while(1){
    buf[64]
    socket.recv(buf)
    read(&header,buf)
    data = new data[header.len]
    readLen = readRemainBuf(buf,data)
    socket.recv(data,(header.len-readLen))
}

包尾放置分隔符

  • 数据字段
  • 分隔符
while(1){
    buf
    socket.recvAndExpend(buf)
    while not found delimiter:
        socket.recvAndExpend(buf)
        if found delimiter:
            set delimiterIndex
    return buf[0:delimiterIndex]
}

基金的募集与认购

基金的募集与认购

程序

申请

  1. 基金申请报告
  2. 基金合同草案
  3. 基金托管协议草案
  4. 招募说明书草案

申请的注册

  1. 自申请日期六个月内做出决议
  2. 常规产品做简易程序注册,原则上不超过20个工作日
  3. 简易产品为常规股票基金、混合基金、债券基金、指数基金、货币基金、发起式基金、合格境内机构投资者(QDII)基金、理财基金和交易型指数基金(含单市场、跨市场/跨境ETF)及其联接基金

发售

  1. 自收到核准文件六个月内进行发售
  2. 发售日开始计算募集时间不超过三个月

合同生效

  1. 母鸡期满,封闭基金达到核准的80%,持有人数少于200人
  2. 开放基金募集总额不少于两亿份,金额不少于两亿元人民币,持有人不少于200人
  3. 母鸡期满十日内验资,验资结束提交备案申请和验资报告
  4. 证监会自收到备案申请及验资报告三个工作日内书面确认,合同自确认之日起生效,次日发布公告

募集失败

  1. 固有财产承担债务费用
  2. 三十天内返还投资者本金及期间产生的银行同期存款利息

证券投资基金的类型

证券投资基金的类型

意义

  • 有助于投资者选择适合自己的风险偏好的基金
  • 有助于评价基金的业绩
  • 是基金评级的基础
  • 有助于分类监管

按对象分

  • 股票基金:基金资产80%投入股票
  • 债券基金:基金急产80%投入债券
  • 货币市场基金:仅投资货币市场工具
  • 混合基金:除了投资基金外,且比例不满足上述基金属于混合基金
  • 基金中的基金:投资基金的基金

按投资目标分

  • 增长型:追求增值为目标,较少考虑当期收益,风险高,收益大
  • 收入型:追求稳定经常性收入,风险小,收益低
  • 平衡性:以上两种的中间形态

投资理念

  • 主动型:力图超越基准表现
  • 被动性:一般是跟踪或者复制指数

募集方式

  • 公募型:面向不特定人群,强监管
  • 私募型:面向合格投资者

投资基金的法律形式及运作方式

投资基金的法律形式及运作方式

公司型/契约型

契约型

只有两方参与,投资人和基金管理人,没有法人

公司型

组建法人,成立股东会,按公司运营

区别

法律地位不同:公司型投资人是以股东形式存在,并可以行使股东权利,对投资可以有较大的干预权限,契约型的则是委托代为投资,没有太多对投资行为干预的权限

封闭式/开放式

封闭式基金

  1. 有固定存续期
  2. 份额在封闭期内是固定的
  3. 直接与基金管理人之间完成交易
  4. 交易价格会受到二级市场影响
  5. 不会因为标的价格波动而面临直接压力
  6. 比较适合中长期投资

开放式

  1. 无固定存续期
  2. 份额随时增减
  3. 上市之后仅能在二级市场买卖
  4. 价格仅与净值挂钩
  5. 会直接面临赎回压力
  6. 要保持资产的流动性

证券投资基金的概览

证券投资基金的概览

定义

证券投资基金(特指公募基金)是将众多不特定的投资者资金汇集形成独立财产,委托基金管理人(发产品的人)投资管理,基金托管人进行财产托管

参与主体

  • 份额持有人,也就是投资者类似你我这样普通的买基金的人
  • 基金管理人,是负责基金运作的角色,是基金产品设计的人士
  • 基金托管人,是负责监管资金运作和实际资金指令执行的机构,并需要与基金管理人完全独立

基金市场服务机构

  • 销售(可以销售)
  • 销售支付(只能参与结算)
  • 份额登记
  • 估值核算
  • 投资顾问
  • 评级
  • 基金信息技术系统服务
  • 法律支持

监管机构和自律组织

  • 基金监管机构(中国证券监督管理委员会、中国人民银行、证券交易所、证券业协会)
  • 自律组织:证券交易所,中国证券投资基金业协会

金融、资产管理、投资基金

金融、资产管理、投资基金

金融市场与资产管理行业

  1. 什么是金融资产
    金融资产就是代表未来收益或资产合法权的凭证
    标识了明确的价值和交易双方的关系
  2. 金融与居民理财
    金融就是货币资金的融通
    货币资金来源于居民通过生产劳动产生
    理财是通过对货币资金/资本进行管理以其保值或增值
  3. 金融市场是资金货币融通的市场
    市场分类:
    交易工具期限分类:货币市场、资本市场
    标的物分类:证券、票据、衍生物、外汇、黄金
    交易交割期限分类:期货、现货
    市场构成:
    市场参与者:政府、中央银行、企业或个人居民、金融机构
    金融工具:证明债权债务关系并据此进行货币资金交易的合法凭证
    交易组织方式:固定场所集中式、柜台双方面议、电信网络交易

  4. 资产管理与资产管理行业
    资产管理:受投资人委托,为达成投资人特定目标代理投资人对投资人资本进行管理,并以此收取服务费用
    资产管理行业:基金管理公司、私募机构、信托公司、证券公司、期货公司、保险公司、商业银行

投资基金

  1. 是资产管理的主要方式
  2. 是组合投资、专业管理、利益共享、风险共担的投资方式
  3. 有证券投资、私募股权、分线投资、对冲基金、其他

为图片添加水印


import numpy from PIL import Image from PIL import ImageDraw from PIL import ImageFont from numpy import fft i1 = Image.open("d:/abc.jpg") weigh = i1.size[0] heigh = i1.size[1] i2 = Image.new('RGB', (weigh, heigh), (255, 255, 255)) font = ImageFont.truetype("arial.ttf", 36) font.size = 36 draw = ImageDraw.Draw(i2) draw.text((0, 0), "Mark By Michaelssss", font=font, fill=(0, 0, 0)) i2 = i2.transpose(Image.ROTATE_180) draw = ImageDraw.Draw(i2) draw.text((0, 0), "Mark By Michaelssss", font=font, fill=(0, 0, 0)) arr1 = numpy.array(i1) arr2 = numpy.array(i2) final = numpy.uint8(fft.ifft2(arr2 * 40 + fft.fft2(arr1))) Image.fromarray(final).show() # Image.fromarray(numpy.uint8(arr2)).show() Image.fromarray(numpy.uint8(fft.fft2(numpy.array(final)) - fft.fft2(arr1))).show()

这样就能添加一个盲水印,感谢开源让我不用学习如何手撕FFT哈哈哈哈哈哈

如何构建回测系统(七)

如何构建回测系统(七)

清洗数据

内存不足问题

  1. 单个对象粗略估算(刨除所有的对象辅助数据):438byte
  2. 约有5*60*60*3000条数据
  3. 为了使得在8GB左右的系统中也能顺利运行需要做改进

数据不连续问题

  1. ETF中成分股出现没有交易无法从逐笔还原

改进

  1. 切割导入时间,实践是每三十分钟一个段落导入
  2. 所有需要前置数据可以预留在内存
  3. 做到每个段落指执行读1次数据库,写一次数据库
  4. 将导入任务抽离,并且每进行一次导入保存当前进度以方便过程恢复
  5. 往前搜索直到股票第一次开市价格
  6. 需要保存股票的天数据
  7. 使用现金替代来代替无交易数据

结果

  1. 在8GB系统中,成功完成4GB数据导入
  2. iopv均有数据,但无法验证,下一步想办法验证

如何构建回测系统(六)

如何构建回测系统(六)

数据量问题

  1. 粗略估算,从2000年到目前,若完全采用逐笔构建高精度交易数据,至少需要31TB的存储空间
  2. 内存不可能满足
  3. 采用文件存储搜索不方便,时间复杂度过高

解决方向

  1. 抛弃原有的完全将数据放置于内存中的想法
    1. 引入数据库
    2. 分表存储和读取
    3. 应用层做聚合
  2. 将原来由回测系统每次构建高精度数据,变更为ETL系统做数据的计算和存储,回测系统仅读取,分离其功能

难点

  1. 为提升开发效率使用的是jpa/hibernate,但是jpa的层级过高,不方便做分表分库聚合,需要使用其底层的接口针对需要分表的对象重新构建应用层数据库接口
  2. 剥离了项目之后的依赖关系,及接口关系梳理