在上一篇文章中,我们已经对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. Machine Learning Note

    [Andrew Ng NIPS2016演讲]<Nuts and Bolts of Applying Deep Learning (Andrew Ng) 中文详解:https://mp.weixi ...

  2. Appium 概括与环境安装

    Appium 是什么, 有什么用 Appium 用途和特点 appium 是一个移动 app 自动化工具 手机APP自动化有什么用? 自动化完成一些重要性的任务 比如微信客服机器人 爬虫 就是通过自动 ...

  3. ViewPager2 学习

    ViewPager2 延迟加载数据 ViewPager2 延迟加载数据 ViewPager 实现预加载的方案 ViewPager2 实现预加载的方案 总结 ViewPager 实现预加载的方案 背景 ...

  4. Git配置仓库的用户名邮箱

    Git配置单个仓库的用户名邮箱 $ git config user.name "gitlab's Name" $ git config user.email "gitla ...

  5. Haproxy/LVS负载均衡实现+keepalived实现高可用

    haproxy+keepalived 集群高可用集群转发 环境介绍 #内核版本 Ubuntu 18.04.4 LTS \n \l 107-Ubuntu SMP Thu Jun 4 11:27:52 U ...

  6. [强化学习]Part1:强化学习初印象

    引入 智能 人工智能 强化学习初印象 强化学习的相关资料 经典书籍推荐:<Reinforcement Learning:An Introduction(强化学习导论)>(强化学习教父Ric ...

  7. Hystrix入门教程

    Hystrix入门教程 一·什么是Hystrix?Hystrix有什么作用?使用Hystrix有哪些适用场景 Hystrix是springCloud的组件之一,Hystrix 可以让我们在分布式系统中 ...

  8. 10大HBase常见运维工具整理

    摘要:HBase自带许多运维工具,为用户提供管理.分析.修复和调试功能.本文将列举一些常用HBase工具,开发人员和运维人员可以参考本文内容,利用这些工具对HBase进行日常管理和运维. HBase组 ...

  9. 病毒Virus

    病毒Virus 一本通P1396 病毒Virus 题目简述 给定\(k\)个被病毒感染了的字符串,知道这\(k\)个字符串原本是按字典序从小到大排列,最后给出一个待复原的字符串\(s\),要求根据上面 ...

  10. 五.数据库同步,创建django用户,用户登陆过程

    (1)配置数据库 项目目录/settings.py是一个普通的python模块,每项配置都是一key/value 数据库的配置是以dict的形式存放在这个模块中,key名为:DATABASES DAT ...