Dresdon二次开发
在上一篇文章中,我们已经对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二次开发的更多相关文章
- Navisworks API 简单二次开发 (自定义工具条)
在Navisworks软件运行的时候界面右侧有个工具条.比较方便.但是在二次开发的时候我不知道在Api那里调用.如果有网友知道请告诉我.谢谢. 我用就自己设置一个工具.界面比较丑!没有美工. 代码: ...
- [连载]《C#通讯(串口和网络)框架的设计与实现》- 12.二次开发及应用
目 录 第十二章 二次开发及应用... 2 12.1 项目配制... 3 12.2 引用相关组件... 4 12.3 构建主程序... 5 ...
- OBS-Studio二次开发记录
OBS-Studio 是一款跨平台的,开源的视频直播客户端软件. 公司需要对他进行二次开发,开发的目的是使用它的录屏功能. 开发的要求是:定制全新的界面,所见即所得,window系统兼容要好. 开发步 ...
- 小猪cms微信二次开发之怎样分页
$db=D('Classify'); $zid=$db->where(array('id'=>$this->_GET('fid'),'token'=>$this->tok ...
- 承接 AutoCAD 二次开发 项目
本人有多年的CAD开发经验,独立完成多个CAD二次开发项目.熟悉.net及Asp.net开发技术,和Lisp开发技术. 现在成立了工作室,独立承接CAD二次开发项目.结项后提供源码及开发文档,有需要的 ...
- 【工业串口和网络软件通讯平台(SuperIO)教程】七.二次开发服务驱动
SuperIO相关资料下载:http://pan.baidu.com/s/1pJ7lZWf 1.1 服务接口的作用 围绕着设备驱动模块采集的数据,根据需求提供多种应用服务,例如:数据上传服务.数 ...
- 【工业串口和网络软件通讯平台(SuperIO)教程】三.二次开发流程
1.1 二次开发流程图 1.2 引用相关组件 找到“开发包”,引用里边的相关组件.如下图: 1.3 开发设备驱动模块 1.3.1 开发发送协议驱动 继承SuperIO.Devi ...
- Civil 3D API二次开发学习指南
Civil 3D构建于AutoCAD 和 Map 3D之上,在学习Civil 3D API二次开发之前,您至少需要了解AutoCAD API的二次开发,你可以参考AutoCAD .NET API二次开 ...
- visio二次开发——图纸解析之线段
多写博客,其实还是蛮好的习惯的,当初大学的时候导师就叫我写,但是就是懒,大学的时候,谁不是魔兽或者LOL呢,是吧,哈哈哈. 好了,接着上一篇visio二次开发——图纸解析,我继续写. 摘要: (转发请 ...
随机推荐
- 项目实战:Qt手机模拟器拉伸旋转框架
若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...
- WeChair项目Beta冲刺(10/10)
团队项目进行情况 1.昨日进展 Beta冲刺第十天 昨日进展: 项目完工 2.今日安排 对小程序进行测试,同时对项目进行总结,并整理博客材料等 3.燃尽图 4.展示Git当日代码记录 详情 ...
- mongodb 数据库 增删改查
mongodb 数据库 增删改查 增: // 引入express 模块 var express = require('express'); // 路由var router = expr ...
- Centos安装redis详解
redis官方网站对redis的安装已有介绍,但没有redis环境的安装,redis自动启动服务安装等等: redis官网:https://redis.io 本文以centos6为基础安装redis ...
- java基础 内部类详解
什么是内部类? 1.内部类也是一个类: 2.内部类位于其他类声明内部. 内部类的常见类型 1.成员内部类 2.局部内部类 3.匿名内部类 4.静态内部类 简单示例 /** * 外部类 * */ pub ...
- 阿里druid数据源属性配置表
https://cloud.tencent.com/developer/article/1368903 DRUID 属性说明表 属性(Parameter) 默认值(Default) 描述(Descri ...
- Spring事务深入剖析--spring事务失效的原因
之前我们讲的分布式事务的调用都是在一个service中的事务方法,去调用另外一个service中的业务方法, 如果在一个sevice中存在两个分布式事务方法,在一个seivice中两个事务方法相互嵌套 ...
- 2、尚硅谷_SSM高级整合_创建Maven项目.avi
第一步我们新建立一个web工程 这里首先要勾选上enable的第一个复选框 这里要勾选上add maven support 我们在pom.xml中添加sevlet的依赖 创建java web项目之后, ...
- redis高级命令3哨兵模式
redis的哨兵模式 现在我们在从服务器1.222上让该从服务器作为哨兵 首先将redis安装包文件下的sentinel.conf文件复制到/usr/local/redis/etc目录下 然后修改se ...
- Spring系列.SpEL表达式
Spring表达式语言 SpEL语言是一种强大的表达式语言,支持在运行时查询和操作对象.SpEL表达式不一定要创建IOC容器后才能使用.用户完全可以单独调用SpEL的API来独立的使用时SpEL表达式 ...