在上一篇文章中,我们已经对Dresdon所提供的功能进行了简单的介绍。在这篇文章中,我们将介绍如何基于Dresdon进行二次开发。

Dresdon的扩展点

  就像上一篇文章所介绍的那样,Dresdon主要是一个量化引擎。用户可以通过脚本或者Java编程的方式来描述模型的买卖条件,并进一步通过扫描该模型在所有股票中的所有匹配来评估该模型的具体表现。通过这种方式,用户可以很大程度地优化自己的交易系统,从而实现稳定盈利。

  通过脚本来描述股票的买入卖出条件十分简单:

// 当日和前日股价上涨
$isRaisingUp = growth(close()) > 0.01 && growth(close(), 1) > 0.01 // 5日前存在着一个长度至少为30,震荡幅度小于5%的平台
$isPlatform = platform_exists(0.05, 30, 5) // 在平台前存在长度至少为20日,最大上涨幅度为12%的缓慢增长
$isSlowRaiseBeforePlatform = is_slow_raise(0.12, 20, platform_start(0.05, 30, 5))
…… $buyCondition = $isRaisingUp && $isPlatform && $isSlowRaiseBeforePlatform && ……
$sellCondition = close(0) < ma5(0) && ……

  接下里用户就可以通过扫描2006年1月到2020年4月之间所有匹配来统计该模型的表现:

{
"averageBenefit" : 0.049035519980520418, // 平均单笔收益为4.9%左右
"maxBenefit" : 74.86122362293308, // 最高收益为74.9%
"minBenefit" : -4.000000000000014, // 最大止损为4%
"totalCount" : 313, // 2006.01 – 2020.04之间匹配313次
"averageDayCount" : 11.875656742556918, // 平均持股时间为11.9天
"successRatio" : 0.46059544658493873 // 成功率为46%左右
}

  当然,如果用户会Java,那么他还可以将模型写成一个Java类,进而得到编译器的强类型支持:

// 当日和前日股价上涨
BooleanHolder isRaisingUp = and(greaterThan(growth(close()), 0), greaterThan(growth(close(), 1), 0)); // 5日前存在着一个长度至少为30,震荡幅度小于5%的平台
BooleanHolder platformExists = platformExists(0.05, 30, 5) // 在平台前存在长度至少为20日,最大上涨幅度为12%的缓慢增长
IntHolder platformStart = platformStart(0.05, 30, 5);
BooleanHolder isSlowRaiseBeforePlatform = isSlowRaise(0.12, 20, platformStart);
…… BooleanHolder condition = and(isRaisingUp, platformExists, isSlowRaiseBeforePlatform, ……);

  除了添加自定义模型之外,用户还可以添加自定义函数。这些函数可以用来判断某日K线的特征,或者拟合特定K线形态。例如下面就是一个用来计算指定K线震动幅度的函数:

public static Value.Shrink shrink(int index) {
return new Value.Shrink(index);
} @Operation(key = KEY_SHRINK, resultType = DOUBLE, arguments = {
@Arguments(paramTypes = { INT })
})
public static class Shrink extends HolderBase<Double> implements DoubleHolder {
protected IntHolder index; protected Integer indexValue; public Shrink(int index) {
this(new IntValue(index));
} public Shrink(IntHolder index) {
super(KEY_SHRINK); this.index = index;
} @Override
public void preprocess(QuantContext context) {
super.preprocess(context); preprocess(context, index);
} @Override
public boolean needRefresh(QuantContext context) {
return !equals(indexValue, index, context);
} @Override
protected Double recalculate(QuantContext context) {
indexValue = index.getValue(context);
if (indexValue == null) {
return null;
} DailyTrading dailyTrading = context.getTradings().get(indexValue);
double blockSize = Math.abs(dailyTrading.getClose() - dailyTrading.getOpen());
double totalVariation = dailyTrading.getHigh() - dailyTrading.getLow();
this.value = blockSize > SMALL_DOUBLE_VALUE ? totalVariation / blockSize : Double.MAX_VALUE;
return value;
} @Override
public void persist(StringBuilder builder) {
builder.append(getKey());
builder.append(INDEX_START);
getIndex().persist(builder);
builder.append(INDEX_END);
}
}

  在后面的章节中,我们将详细讲解上面模型中各个买入卖出条件的意义。

添加自定义模型

  下面就让我们从添加自定义模型开始。抽取一个模型常常需要经过以下一系列步骤:

  1. 确定模型形态。用户首先需要确定需要匹配的模型的大致形态有哪些,如起涨阶段的线形是什么样子的,整理期是以什么形态呈现的,甚至之前筹码是如何收集的等等。

  2. 初筛并收集目标匹配。用户需要为该模型定义一个大致的匹配条件,然后运行引擎。此时得到的结果可能存在着大量的噪音,因此统计数据常常并不好看。但其中也会包含大量的具有较高准确度的匹配。而这些匹配常常是模型的目标匹配。

  3. 细化模型。添加其它条件逐渐祛除噪音,以提高模型正确匹配的比率。

  4. 细化卖出条件。添加其它卖出提交,以提高模型的收益率及成功率。

  当然,凡事都有一个从陌生到熟悉的过程。在添加了几个模型之后,用户可能就能摸到其中的诀窍,进而大大提高模型抽取的效率。在这里给大家列出来我在抽取模型过程中最常使用的一系列经验型策略,避免大家重走我之前的弯路。

  首先,模型的买入特征线型要明显,近端的辅助判断逻辑要严格,而远端的辅助判断逻辑要具有较高的容错性。可以说,所谓的股票拉升实际上就是股票价格的异动,而该异动的阻力则很大程度上决定了股票行情到底能走多远。因此起涨阶段线形的略微不同都可能导致量化结果产生非常大的差异。比如都是上涨5%,一个有长上影的K线就远不如没有长上影的K线。反之离当前交易日越远的交易,其对当前股价的影响越小,因此远端的辅助判断逻辑不宜非常严格。

  其次,要对常见线形所代表的意义有正确的理解。同样的K线在不同的位置其意义常常并不相同。例如一般来说,低位揉搓线常常是一个好的K线组合,而高位揉搓线,尤其是放量揉搓线则很可能代表一段行情将要终结。

  最后,筛选条件常常是可以通用的。就像第一条所说的那样,我们要将买入的特征线形严格地区分。比如拉升是通过一根阳线完成的,和拉升是通过三根K线形成的组合K线完成的效果类似。但是它们的筛选逻辑则常常有一个为2的索引差:一根阳线完成的拉升,我们要从前一天的K线检查,而三根K线组成的拉升,则需要从三天前的交易开始检查。只不过这些检查的参数有些不太相同而已。

 

添加自定义函数

  在编写一段时间的模型之后,用户可能就会感觉到引擎内建的各个表达式很难表现一些特定的限制条件。例如他可能常常需要通过如下表达式来限制K线的波动情况:

$noBigShrink = abs(close(0) – open(0)) * 5 < high(0) – low(0)

  甚至用Java编写出来的表达式的可读性更差:

BooleanHolder noBigShrink = lessThan(multiply(abs(minus(close(0), open(0))), 5), minus(high(0), low(0)));

  而这部分的逻辑仅仅是在判断当日K线的实体是否过小,进而呈现十字星或锤头线等形态。此时用户就可以在Plugin里面添加自定义的表达式:

public static Value.Shrink shrink(int index) {
return new Value.Shrink(index);
} @Operation(key = KEY_SHRINK, resultType = DOUBLE, arguments = {
@Arguments(paramTypes = { INT })
})
public static class Shrink extends HolderBase<Double> implements DoubleHolder {
protected IntHolder index; protected Integer indexValue; public Shrink(int index) {
this(new IntValue(index));
} public Shrink(IntHolder index) {
super(KEY_SHRINK); this.index = index;
} @Override
public void preprocess(QuantContext context) {
super.preprocess(context); preprocess(context, index);
} @Override
public boolean needRefresh(QuantContext context) {
return !equals(indexValue, index, context);
} @Override
protected Double recalculate(QuantContext context) {
indexValue = index.getValue(context);
if (indexValue == null) {
return null;
} DailyTrading dailyTrading = context.getTradings().get(indexValue);
double blockSize = Math.abs(dailyTrading.getClose() - dailyTrading.getOpen());
double totalVariation = dailyTrading.getHigh() - dailyTrading.getLow();
this.value = blockSize > SMALL_DOUBLE_VALUE ? totalVariation / blockSize : Double.MAX_VALUE;
return value;
} @Override
public void persist(StringBuilder builder) {
builder.append(getKey());
builder.append(INDEX_START);
getIndex().persist(builder);
builder.append(INDEX_END);
}
}

  下面就让我们一行行地讲解这些代码的含义。首先是一个静态函数:

public static Value.Shrink shrink(int index) {
return new Value.Shrink(index);
}

  通过该静态函数,用户可以更直观地描述模型逻辑,属于一种语法糖:

new lessThan(new Shrink(0), 5) vs. lessThan(shrink(0), 5)

  接下来我们则通过@Operation来标明当前类中包含的逻辑是一个引擎操作的定义。该操作的key为KEY_SHRINK,带有一个Integer类型的参数,返回值的类型为Double:

@Operation(key = KEY_SHRINK, resultType = DOUBLE, arguments = {
@Arguments(paramTypes = { INT })
})
public static class Shrink extends HolderBase<Double> implements DoubleHolder {

  这里有一个概念,那就是Holder。马上您就会看到,Shrink类实例上并没有记录和交易相关的数据,它仅仅用来承载计算逻辑。也就是说,它相当于一个占位符。实际上,Dresdon支持的所有运算符都是一个Holder,内部只记录算法,不记录任何数据。

  那么交易相关的数据都记录在哪里呢?答案是Context。用户可以通过各个holder的getValue()函数来得到各个holder的当前值。现在就让我们看看Shrink类的recalculate()函数的是如何使用它的:

@Override
protected Double recalculate(QuantContext context) {
indexValue = index.getValue(context);
if (indexValue == null) {
return null;
} DailyTrading dailyTrading = context.getTradings().get(indexValue);
double blockSize = Math.abs(dailyTrading.getClose() - dailyTrading.getOpen());
double totalVariation = dailyTrading.getHigh() - dailyTrading.getLow();
this.value = blockSize > SMALL_DOUBLE_VALUE ? totalVariation / blockSize : Double.MAX_VALUE;
return value;
}

  可以看到,recalculate()函数传入了一个QuantContext类型的实例。接下来,该函数的实现通过调用index的getValue()函数得到了index的实际值。接下来,我们就从context中取得了目标交易数据(dailyTrading),并依次通过计算目标K线的实体大小(blockSize),当日最高价和最低价之差(totalVariation)来计算当日的波动情况。这里需要注意的是,计算结果将被首先记录在value这个域中,然后才被该函数返回。

  为了提高计算的性能,我们引入了两个机制:refresh和preprocess。前者通过判断参数的值是否变化来确定是否需要运行recalculate()函数。毕竟该函数所包含的计算逻辑可能相当复杂。在其它属性没有发生变化的时候,我们可以通过直接返回value这个缓存域中记录的值来提高运行性能:

@Override
public boolean needRefresh(QuantContext context) {
return !equals(indexValue, index, context);
}

  另一种情况则是对预处理的支持。其主要用来提高拟合功能的性能。让我们以一只股票在多年的交易中存在着一系列盘整平台的情况为例。如果我们针对不同的日期都计算一次拟合逻辑,那么引擎的性能将变得很差。毕竟在平台内部的各个交易日对应的是同一个平台。为了解决这个问题,我们添加了预处理步骤。该步骤允许引擎对所有交易日进行一次扫描,并将其扫描结果存储在Context中。在需要的时候从Context中取出相应的预处理结果即可:

@Override
public void preprocess(QuantContext context) {
……
PlatformExtractor extractor = new PlatformExtractor(symbol, ma5s, rangeValue, minLengthValue);
List<PlatformInfo> platforms = extractor.extractPlatforms();
context.getVariableMap().setVariable(key, new ObjectWrapper(symbol, platforms));
} protected PlatformInfo getCurrentPlatform(QuantContext context) {
……
ValueHolder<?> variable = context.getVariableMap().getVariable(key);
List<PlatformInfo> platforms = (List<PlatformInfo>)(((ObjectWrapper)variable).getValue());
return platforms.stream().filter(platform -> platform.getStartDate().compareTo(seedDate) < 0
&& platform.getEndDate().compareTo(seedDate) > 0).findFirst().orElse(null);
}

  通过这种方法,用户就可以自行创建更高级的函数,进而使得自己的模型变得更为简洁。

转载请注明原文地址并标明转载:https://www.cnblogs.com/loveis715/p/13324937.html

商业转载请事先与我联系:silverfox715@sina.com

公众号一定帮忙别标成原创,因为协调起来太麻烦了。。。

Dresdon二次开发的更多相关文章

  1. Navisworks API 简单二次开发 (自定义工具条)

    在Navisworks软件运行的时候界面右侧有个工具条.比较方便.但是在二次开发的时候我不知道在Api那里调用.如果有网友知道请告诉我.谢谢. 我用就自己设置一个工具.界面比较丑!没有美工. 代码: ...

  2. [连载]《C#通讯(串口和网络)框架的设计与实现》- 12.二次开发及应用

    目       录 第十二章     二次开发及应用... 2 12.1        项目配制... 3 12.2        引用相关组件... 4 12.3        构建主程序... 5 ...

  3. OBS-Studio二次开发记录

    OBS-Studio 是一款跨平台的,开源的视频直播客户端软件. 公司需要对他进行二次开发,开发的目的是使用它的录屏功能. 开发的要求是:定制全新的界面,所见即所得,window系统兼容要好. 开发步 ...

  4. 小猪cms微信二次开发之怎样分页

    $db=D('Classify'); $zid=$db->where(array('id'=>$this->_GET('fid'),'token'=>$this->tok ...

  5. 承接 AutoCAD 二次开发 项目

    本人有多年的CAD开发经验,独立完成多个CAD二次开发项目.熟悉.net及Asp.net开发技术,和Lisp开发技术. 现在成立了工作室,独立承接CAD二次开发项目.结项后提供源码及开发文档,有需要的 ...

  6. 【工业串口和网络软件通讯平台(SuperIO)教程】七.二次开发服务驱动

    SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1    服务接口的作用 围绕着设备驱动模块采集的数据,根据需求提供多种应用服务,例如:数据上传服务.数 ...

  7. 【工业串口和网络软件通讯平台(SuperIO)教程】三.二次开发流程

    1.1    二次开发流程图 1.2    引用相关组件 找到“开发包”,引用里边的相关组件.如下图: 1.3    开发设备驱动模块 1.3.1    开发发送协议驱动 继承SuperIO.Devi ...

  8. Civil 3D API二次开发学习指南

    Civil 3D构建于AutoCAD 和 Map 3D之上,在学习Civil 3D API二次开发之前,您至少需要了解AutoCAD API的二次开发,你可以参考AutoCAD .NET API二次开 ...

  9. visio二次开发——图纸解析之线段

    多写博客,其实还是蛮好的习惯的,当初大学的时候导师就叫我写,但是就是懒,大学的时候,谁不是魔兽或者LOL呢,是吧,哈哈哈. 好了,接着上一篇visio二次开发——图纸解析,我继续写. 摘要: (转发请 ...

随机推荐

  1. CRC16冗余循环检测计算器-好用。modbus RTU

    开始使用 http://cht.nahua.com.tw/index.php?url=http://cht.nahua.com.tw/software/crc16/&key=Modbus,%2 ...

  2. Win10搭建VM12.0.1虚拟机,虚拟机网络同宿主机ping不通的解决办法

    准备系统学习Linux系统,在电脑搭建了一个CentOS虚拟机,希望能从宿主机连接至虚拟机. 尝试了很多办法,碰到各种坑,最后这个方法成功了! 分享给大家,希望有所帮助. 一.环境 1.宿主机:Win ...

  3. 初识Java Java基础知识

    今天给大家带来的是初级Java基础部分的知识:包括初识Java.变量.常量.数据类型.运算符.各种选择结构.循环结构.数组等Java的基础语法部分!!!内容.步骤超详细,附有各种案例的源代码(可以直接 ...

  4. springboot 集成mybatis时日志输出

    application.properties(yml)中配置的两种方式: 这两种方式的效果是一样的,但是下面一种可以指定某个包下的SQL打印出来,上面这个会全部的都会打印出来.

  5. linux 系统文件目录颜色及特殊权限对应的颜色

    什么决定文件目录的颜色和背景?  颜色  说明  栗子  权限 白色 表示普通文件   蓝色 表示目录  绿色 表示可执行文件 浅蓝色 链接文件 黄色 表示设备文件 红色   表示压缩文件 红色闪烁 ...

  6. 07.Easymock的实际应用

    第一步下载对应的java包添加到工程中 并静态导入所需要的j类 import static org.easymock.EasyMock.*; 这里有的注意点 package com.fjnu.serv ...

  7. xutils工具上传日志文件--后台服务器的搭建

    在上一篇文章中使用xutils将手机上保存的日志上传到后台服务器中,现在我们来讲后台服务器是如何搭建的 后台服务器采用jsp+sevlet+mysql的框架 首先讲mysql数据库的表的建立 在fil ...

  8. DOM-BOM-EVENT(5)

    5.宽.高.位置相关 5.1.clientX/clientY clientX和clientY表示鼠标在浏览器可视区的坐标位置 <script> document.onclick = fun ...

  9. 三.cmdb

    一.服务器管理: https://github.com/rfjer/autoAdmin/tree/master/apps/servers 一服务器信息收集方式: 1.物理服务器 跑脚本传(bash/a ...

  10. My97DatePicker 4.8

    https://jeesite.gitee.io/front/my97/demo/index.htm