股票学习网

每日牛股推荐,股票每日推荐,每日股票推荐,股票预测 - - 股票预测网!

德马克指标源码(德马克指标公式源码)

2023-04-23 03:28分类:创业板 阅读:

 

分享一个好用的通达信量能指标,指标你一看就懂,特别直观明了!复制源码,自己导入。

源码:

VOLUME:VOL,VOLSTICK;

|:COUNT(CLOSE>REF(CLOSE,1),2)=2 AND COUNT(VOL<REF(VOL,1),2)=2;

STICKLINE(|,0,V,1,0),COLORGREEN;

DRAWTEXT(|,VOL+3000,'降'),COLORGREEN;

T:=DYNAINFO(8)>0;

VO:VOL=LLV(VOL,100) AND T;

地量柱:T AND VO,COLORBLUE;

STICKLINE(地量柱,0,V,1,0),COLORBLUE;

DRAWTEXT(地量柱,VOL+3000,'地'),COLORBLUE;

BLZ:=VOL>REF(V,1)*1.9;

BLZ1:=VOL<REF(LLV(VOL,13),1);

BLZ2:=VOL<REF(LLV(VOL,55),1);

STICKLINE(BLZ,0,V,1,0),COLORYELLOW;

倍量柱:BLZ,COLORYELLOW;

DRAWTEXT(倍量柱,VOL+3000,'倍'),COLORYELLOW;

HJZ:=(3*C+H+L+O)/6;

HJZ2:=(8*HJZ+7*REF(HJZ,1)+6*REF(HJZ,2)+5*REF(HJZ,3)+4*REF(HJZ,4)+3*REF(HJZ,5)+2*REF(HJZ,6)+REF(HJZ,8))/36;

HJZ3:=VOL=HHV(VOL,10) AND VOL>2*REF(VOL,1) AND CLOSE>HJZ2 AND C>O;

黄金柱:FILTER(HJZ3,5),STICK,COLORRED,LINETHICK4;

{好股票网-股票软件公式网站 WWW.GOODGUPIAO.COM}

STICKLINE(黄金柱,0,V,1,0),COLORRED;

DRAWTEXT(黄金柱,VOL+10000,'黄'),COLORRED;

价涨量缩:|,COLORGREEN;

FLZ:=V>REF(V,1)*0.98 AND V<REF(V,1)*1.02,LINETHICK0;

STICKLINE(FLZ,0,V,1,0),COLORMAGENTA;

DRAWTEXT(FLZ,VOL+3000,'平'),COLORMAGENTA;

平量柱:FLZ,COLORMAGENTA;

主力启动线:MA(VOL,5);

主力洗盘线:MA(VOL,40);

资金异动线:MA(VOL,135);

DRAWTEXT(CROSS(主力启动线,主力洗盘线)AND V>主力洗盘线 AND V>资金异动线 AND 主力洗盘线<资金异动线,V,'穿三线');

梯量:COUNT(V>REF(V,1),3)=3 AND COUNT(C>O,3)=3,NODRAW,COLOR824173;

STICKLINE(梯量,0,V,1,0),COLOR824173;

低量:V<=REF(LLV(V,15),1),NODRAW,COLORGREEN;

STICKLINE(低量,0,V,1,0),COLORGREEN;

倍缩:V<=REF(V,1)*0.5,NODRAW,COLORRED;

STICKLINE(倍缩,0,V,1,0),COLORRED;

{有庄控盘}

VAW1:=EMA(EMA(CLOSE,13),13);

控盘:=(VAW1-REF(VAW1,1))/REF(VAW1,1)*1000;

无庄控盘:=控盘<0;

有庄控盘:=控盘>REF(控盘,1) AND 控盘>0;

{分时价格原码}

JJ:=DYNAINFO(11);

{波段买卖原码}

P:=21;S:=8;M1:=3;

财:=(EMA(CLOSE,S)-EMA(CLOSE,P))*50;

神:=EMA(财,M1);

{股价与均线偏离买卖条件}

BTJ1:=(C-JJ)/JJ<-0.03;

BTJ11:=(C-JJ)/JJ<-0.03;

BTJ12:=LAST(JJ>=REF(JJ,1),5,1) AND (C-JJ)/JJ<0.005;

STJ1:=(C-JJ)/JJ>0.005;

{控盘买卖条件}

STJ01:=控盘<REF(控盘,1) AND 控盘>0.5;

STJ02:=控盘>0;

BTJ2:=控盘>REF(控盘,1) AND 控盘<-0.2;

BTJ22:=控盘>REF(控盘,1) AND 控盘<0 ;

{波段买卖条件}

BTJ3:=CROSS(财,神) AND 财<-0.3;

BTJ32:=CROSS(财,神) AND 财<-0.1;

STJ31:=CROSS(神,财);

STJ32:=CROSS(神,财) AND 财>1.618;

{以下为信号};

BTJ81:=FILTER(BTJ1 AND BTJ2 AND BTJ3,13);

BTJ811:=FILTER(BTJ11 AND BTJ2 AND BTJ3,13);

BTJ82:=FILTER(BTJ12 AND BTJ22 AND BTJ32,13);

STJ81:=FILTER(STJ1 AND STJ01 AND STJ31,13);

STJ82:=FILTER(STJ02 AND STJ32 ,13);

STJ83:=STJ81 AND STJ82;

DRAWTEXT(STJ83,H*1.002,'↓庄出'),COLORGREEN;

DRAWICON(STJ83,H+0.003,24);

庄:= BTJ11 AND BTJ2 AND BTJ3;

有庄:=FILTER(庄,30);

DRAWTEXT(有庄,L-0.0011,'↖庄进'),COLORYELLOW;

DRAWICON(有庄,L*0.999,23);

AQ1:=REF(V,1);AQ2:=DVOL;AQ3:=AQ2/AQ1;

LNX:=AQ3-REF(AQ3,1);

E1:=REF(C,1);E2:=DCLOSE;E3:=(E2-E1)/E1*100;

QMX:=E3-REF(E3,1);

XG:=CROSS(LNX,500) AND CROSS(QMX,10);

DRAWTEXT(XG=1,L*0.995,'--钻石'),COLORFFCC88;

DRAWICON(XG=1,L*0.993,25);

{STYLE:抄底钻石王--可以用于选股预警,也可以放到附图}

YY:=(REF(H,4)-REF(L,1))/C*100>10;{4天前的最高到昨天最低跌幅为10个点}

YY1:=(REF(L,1)-L)/C*100>=0;{昨天的最低价跟今天最低价相平或收下影线}

YY2:=REF(C<O,1) AND C>O;{昨天收阴,今天收阳线}

YY3:= CLOSE>REF(CLOSE,1)*1.03 ;{股价3个点预警}

YY4:=(H-C)/C*100<1.5;{上影线少于1.5个点}

YY5:=V<=LLV(V,5) OR V>=HHV(V,5);{成交量是五天最低或五天最高}

DRAWICON(YY AND YY1 AND YY2 AND YY3 AND YY4 AND YY5,L*0.988,26);{满足所有条件};

DRAWTEXT(YY AND YY1 AND YY2 AND YY3 AND YY4 AND YY5,L*0.988,'--钻石王'),COLORRED;

VAR5:=(CLOSE-LLV(LOW,528))/(HHV(HIGH,528)-LLV(LOW,528))*100;

VAR6:=(CLOSE-LLV(CLOSE,530))/(HHV(CLOSE,530)-LLV(CLOSE,530))*100;

VAR7:=0;

VAR8:=SMA(MAX(CLOSE-REF(CLOSE,1),0),34,1)/SMA(ABS(CLOSE-REF(CLOSE,1)),7,1)*100;

VAR9:=SMA(MAX(CLOSE-REF(CLOSE,1),0),13,1)/SMA(ABS(CLOSE-REF(CLOSE,1)),13,1)*100;

VAR10:=IF(TROUGHBARS(3,16,1)=0 AND HIGH>LOW+0.04,4,0);

VAR11:=ZIG(3,6)>REF(ZIG(3,6),1) AND REF(ZIG(3,6),1)<=REF(ZIG(3,6),2) AND REF(ZIG(3,6),2)<=REF(ZIG(3,6),3);

VAR12:=ZIG(3,6)<REF(ZIG(3,6),1) AND REF(ZIG(3,6),1)>=REF(ZIG(3,6),2) AND REF(ZIG(3,6),2)>=REF(ZIG(3,6),3);

VAR13:=ZIG(3,22)>REF(ZIG(3,22),1) AND REF(ZIG(3,22),1)<=REF(ZIG(3,22),2) AND REF(ZIG(3,22),2)<=REF(ZIG(3,22),3);

VAR14:=ZIG(3,22)<REF(ZIG(3,22),1) AND REF(ZIG(3,22),1)>=REF(ZIG(3,22),2) AND REF(ZIG(3,22),2)>=REF(ZIG(3,22),3);

VAR15:=ZIG(3,51)>REF(ZIG(3,51),1) AND REF(ZIG(3,51),1)<=REF(ZIG(3,51),2) AND REF(ZIG(3,51),2)<=REF(ZIG(3,51),3);

VAR16:=ZIG(3,51)<REF(ZIG(3,51),1) AND REF(ZIG(3,51),1)>=REF(ZIG(3,51),2) AND REF(ZIG(3,51),2)>=REF(ZIG(3,51),3);

VAR17:=ZIG(3,72)>REF(ZIG(3,72),1) AND REF(ZIG(3,72),1)<=REF(ZIG(3,72),2) AND REF(ZIG(3,72),2)<=REF(ZIG(3,72),3);

VAR18:=ZIG(3,72)<REF(ZIG(3,72),1) AND REF(ZIG(3,72),1)>=REF(ZIG(3,72),2) AND REF(ZIG(3,72),2)>=REF(ZIG(3,72),3);

VAR19:=EMA(CLOSE,2)-EMA(CLOSE,150);

买1:=VAR10;

买2:=(VAR11+VAR13+VAR15+VAR17);

卖1:=(VAR12+VAR14+VAR16+VAR18);

DRAWTEXT(买1>0 OR 买2>0,LOW*0.99,'买入'),COLORRED;

DRAWTEXT(卖1>0,HIGH*1.02,'卖出'),COLORGREEN;

 

节点选择器:NodeSelectorSlot

NodeSelectorSlot 负责为资源的首次访问创建 DefaultNode,以及维护 Context.curNode 和调用树。NodeSelectorSlot 被放在 ProcessorSlotChain 链表的第一个位置,这是因为后续的 ProcessorSlot 都需要依赖这个 ProcessorSlot。NodeSelectorSlot 源码如下:

public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> { // Context 的 name -> 资源的 DefaultNode private volatile Map<String, DefaultNode> map = new HashMap<>(10); // 入口方法 @Override public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable { // 使用 Context 的名称作为 key 缓存资源的 DefaultNode DefaultNode node = map.get(context.getName()); if (node == null) { synchronized (this) { node = map.get(context.getName()); if (node == null) { // 为资源创建 DefaultNode node = new DefaultNode(resourceWrapper, null); // 替换 map HashMap<String, DefaultNode> cacheMap = new HashMap<>(map.size()); cacheMap.putAll(map); cacheMap.put(context.getName(), node); map = cacheMap; // 绑定调用树 ((DefaultNode) context.getLastNode()).addChild(node); } } } // 替换 Context 的 curNode 为当前 DefaultNode context.setCurNode(node); fireEntry(context, resourceWrapper, node, count, prioritized, args); } // 出口方法什么也不做 @Override public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { fireExit(context, resourceWrapper, count, args); } }

如源码所示,map 字段是一个非静态字段,意味着每个 NodeSelectorSlot 都有一个 map。由于一个资源对应一个 ProcessorSlotChain,而一个 ProcessorSlotChain 只创建一个 NodeSelectorSlot,并且 map 缓存 DefaultNode 使用的 key 并非资源 ID,而是 Context.name,所以 map 的作用是缓存针对同一资源为不同调用链路入口创建的 DefaultNode。

在 entry 方法中,首先根据 Context.name 从 map 获取当前调用链路入口的资源 DefaultNode,如果资源第一次被访问,也就是资源的 ProcessorSlotChain 第一次被创建,那么这个 map 是空的,就会加锁为资源创建 DefaultNode,如果资源不是首次被访问,但却首次作为当前调用链路(Context)的入口资源,也需要加锁为资源创建一个 DefaultNode。可见,Sentinel 会为同一资源 ID 创建多少个 DefaultNode 取决于有多少个调用链使用其作为入口资源,直白点就是同一资源存在多少个 DefaultNode 取决于 Context.name 有多少种不同取值,这就是为什么说一个资源可能有多个 DefaultNode 的原因。

为什么这么设计呢?

举个例子,对同一支付接口,我们需要使用 Spring MVC 暴露给前端访问,同时也可能会使用 Dubbo 暴露给其它内部服务调用。Sentinel 的 Web MVC 适配器在调用链路入口创建名为“
sentinel_spring_web_context”的 Context,与 Sentinel 的 Dubbo 适配器调用 ContextUtil#enter 方法创建的 Context 名称不同。针对这种情况,我们可以实现只限制 Spring MVC 进来的流量,也就是限制前端发起接口调用的 QPS、并行占用的线程数等。

NodeSelectorSlot#entry 方法最难以理解的就是实现绑定调用树这行代码:

((DefaultNode) context.getLastNode()).addChild(node);

这行代码分两种情况分析更容易理解,我们就以 Sentinel 提供的 Demo 为例进行分析。

一般情况

Sentinel 的 sentinel-demo 模块下提供了多种使用场景的 Demo,我们选择
sentinel-demo-spring-webmvc 这个 Demo 为例,该 Demo 下有一个 hello 接口,其代码如下。

@RestController public class WebMvcTestController { @GetMapping("/hello") public String apiHello() throws BlockException { doBusiness(); return "Hello!"; } }

我们不需要添加任何规则,只是为了调试 Sentinel 的源码。将 demo 启动起来后,在浏览器访问“/hello”接口,在 NodeSelectorSlot#entry 方法的绑定调用树这一行代码下断点,观察此时 Context 的字段信息。正常情况下我们可以看到如下图所示的结果。

从上图中可以看出,此时的 Context.entranceNode 的子节点为空(childList 的大小为 0),并且当前 CtEntry 父、子节点都是 Null(curEntry 字段)。当绑定调用树这一行代码执行完成后,Context 的字段信息如下图所示:

从上图可以看出,NodeSelectorSlot 为当前资源创建的 DefaultNode 被添加到了 Context.entranceNode 的子节点。entranceNode 类型为 EntranceNode,在调用 ContextUtil#enter 方法时创建,在第一次创建名为“
sentinel_spring_web_context”的 Context 时创建,相同名称的 Context 都使用同一个 EntranceNode。并且该 EntranceNode 在创建时会被添加到 Constant.ROOT。

此时,Constant.ROOT、Context.entranceNode、当前访问资源的 DefaultNode 构造成的调用树如下:

ROOT (machine-root) / EntranceNode (context name: sentinel_spring_web_context) / DefaultNoderesource name: GET:/hello

如果我们现在再访问 Demo 的其他接口,例如访问“/err”接口,那么生成的调用树就会变成如下:

ROOT (machine-root) / EntranceNode (context name: sentinel_spring_web_context) / \ DefaultNoderesource name: GET:/helloDefaultNoderesource name: GET:/err

Context.entranceNode 将会存储 Web 项目的所有资源(接口)的 DefaultNode。

存在多次 SphU#entry 的情况

比如我们在一个服务中添加了 Sentinel 的 Web MVC 适配模块的依赖,也添加了 Sentinel 的 OpenFeign 适配模块的依赖,并且我们使用 OpenFeign 调用内部其他服务的接口,那么就会存在一次调用链路上出现多次调用 SphU#entry 方法的情况。

首先 webmvc 适配器在接收客户端请求时会调用一次 SphU#entry,在处理客户端请求时可能需要使用 OpenFeign 调用其它服务的接口,那么在发起接口调用时,Sentinel 的 OpenFeign 适配器也会调用一次 SphU#entry。

现在我们将 Demo 的 hello 接口修改一下,将 hello 接口调用的 doBusiness 方法也作为资源使用 Sentinel 保护起来,改造后的 hello 接口代码如下:

@RestController public class WebMvcTestController { @GetMapping("/hello") public String apiHello() throws BlockException { ContextUtil.enter("my_context"); Entry entry = null; try { entry = SphU.entry("POST:http://wujiuye.com/hello2", EntryType.OUT); // ==== 这里是被包装的代码 ===== doBusiness(); return "Hello!"; // ==== end =============== } catch (Exception e) { if (!(e instanceof BlockException)) { Tracer.trace(e); } throw e; } finally { if (entry != null) { entry.exit(1); } ContextUtil.exit(); } } }

我们可将 doBusiness 方法看成是远程调用,例如调用第三方的接口,接口名称为“http://wujiuye.com/hello2”,使用 POST 方式调用,那么我们可以使用“
POST:http://wujiuye.com/hello2”作为资源名称,并将流量类型设置为 OUT 类型。上下文名称取名为"my_context"。

现在启动 demo,使用浏览器访问“/hello”接口。当代码执行到 apiHello 方法时,在 NodeSelectorSlot#entry 方法的绑定调用树这一行代码下断点。当绑定调用树这行代码执行完成后,Context 的字段信息如下图所示。

如图所示,Sentinel 并没有创建名称为 my_context 的 Context,还是使用应用接收到请求时创建名为“
sentinel_spring_web_context”的 Context,所以处理浏览器发送过来的请求的“GET:/hello”资源是本次调用链路的入口资源,Sentinel 在调用链路入口处创建 Context 之后不再创建新的 Context。

由于之前并没有为名称为“
POST:http://wujiuye.com/hello2”的资源创建 ProcessorSlotChain,所以 SphU#entry 会为该资源创建一个 ProcessorSlotChain,也就会为该 ProcessorSlotChain 创建一个 NodeSelectorSlot。在执行到 NodeSelectorSlot#entry 方法时,就会为该资源创建一个 DefaultNode,而将该资源的 DefaultNode 绑定到节点树后,该资源的 DefaultNode 就会成为“GET:/hello”资源的 DefaultNode 的子节点,调用树如下。

ROOT (machine-root) / EntranceNode (name: sentinel_spring_web_context) / \ DefaultNodeGET:/hello) ......... / DefaultNode (POST:/hello2)

此时,当前调用链路上也已经存在两个 CtEntry,这两个 CtEntry 构造一个双向链表,如下图所示。

虽然存在两个 CtEntry,但此时 Context.curEntry 指向第二个 CtEntry,第二个 CtEntry 在 apiHello 方法中调用 SphU#entry 方法时创建,当执行完 doBusiness 方法后,调用当前 CtEntry#exit 方法,由该 CtEntry 将 Context.curEntry 还原为该 CtEntry 的父 CtEntry。这有点像入栈和出栈操作,例如栈帧在 Java 虚拟机栈的入栈和出栈,调用方法时方法的栈帧入栈,方法执行完成栈帧出栈。

NodeSelectorSlot#entry 方法我们还有一行代码没有分析,就是将当前创建的 DefaultNode 设置为 Context 的当前节点,代码如下:

// 替换 Context.curNode 为当前 DefaultNode context.setCurNode(node);

替换 Context.curNode 为当前资源 DefaultNode 这行代码就是将当前创建的 DefaultNode 赋值给当前 CtEntry.curNode。对着上图理解就是,将资源“GET:/hello”的 DefaultNode 赋值给第一个 CtEntry.curNode,将资源“
POST:http://wujiuye.com/hello2”的 DefaultNode 赋值给第二个 CtEntry.curNode。

要理解 Sentinel 构造 CtEntry 双向链表的目的,首先我们需要了解调用 Context#getCurNode 方法获取当前资源的 DefaultNode 可以做什么。

Tracer#tracer 方法用于记录异常。以异常指标数据统计为例,在发生非 Block 异常时,Tracer#tracer 需要从 Context 获取当前资源的 DefaultNode,通知 DefaultNode 记录异常,同时 DefaultNode 也会通知 ClusterNode 记录记录,如下代码所示。

public class DefaultNode extends StatisticNode { ...... @Override public void increaseExceptionQps(int count) { super.increaseExceptionQps(count); this.clusterNode.increaseExceptionQps(count); } }

这个例子虽然简单,但也足以说明 Sentinel 构造 CtEntry 双向链表的目的。

ClusterNode 构造器:ClusterBuilderSlot

ClusterNode 出现的背景

在一个资源的 ProcessorSlotChain 中,NodeSelectorSlot 负责为资源创建 DefaultNode,这个 DefaultNode 仅限同名的 Context 使用。所以一个资源可能会存在多个 DefaultNode,那么想要获取一个资源的总的 QPS 就必须要遍历这些 DefaultNode。为了性能考虑,Sentinel 会为每个资源创建一个全局唯一的 ClusterNode,用于统计资源的全局并行占用线程数、QPS、异常总数等指标数据。

ClusterBuilderSlot

与 NodeSelectorSlot 的职责相似,ClusterBuilderSlot 的职责是为资源创建全局唯一的 ClusterNode,仅在资源第一次被访问时创建。ClusterBuilderSlot 还会将 ClusterNode 赋值给 DefaultNode.clusterNode,由 DefaultNode 持有 ClusterNode,负责管理 ClusterNode 的指标数据统计。这点也是 ClusterBuilderSlot 在 ProcessorSlotChain 链表中必须排在 NodeSelectorSlot 之后的原因,即必须先有 DefaultNode,才能将 ClusterNode 交给 DefaultNode 管理。

ClusterBuilderSlot 的源码比较多,本篇只分析其实现 ProcessorSlot 接口的 entry 和 exit 方法。ClusterBuilderSlot 删减后的源码如下。

public class ClusterBuilderSlot extends AbstractLinkedProcessorSlot<DefaultNode> { // 资源 -> ClusterNode private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>(); private static final Object lock = new Object(); // 非静态,一个资源对应一个 ProcessorSlotChain,所以一个资源共用一个 ClusterNode private volatile ClusterNode clusterNode = null; @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { if (clusterNode == null) { synchronized (lock) { if (clusterNode == null) { // 创建 ClusterNode clusterNode = new ClusterNode(resourceWrapper.getName(), resourceWrapper.getResourceType()); // 添加到缓存 HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16)); newMap.putAll(clusterNodeMap); newMap.put(node.getId(), clusterNode); clusterNodeMap = newMap; } } } // node 为 NodeSelectorSlot 传递过来的 DefaultNode node.setClusterNode(clusterNode); // 如果 origin 不为空,则为远程创建一个 StatisticNode if (!"".equals(context.getOrigin())) { Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin()); context.getCurEntry().setOriginNode(originNode); } fireEntry(context, resourceWrapper, node, count, prioritized, args); } @Override public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { fireExit(context, resourceWrapper, count, args); } }

ClusterBuilderSlot 使用一个 Map 缓存资源的 ClusterNode,并且用一个非静态的字段维护当前资源的 ClusterNode。因为一个资源只会创建一个 ProcessorSlotChain,意味着 ClusterBuilderSlot 也只会创建一个,那么让 ClusterBuilderSlot 持有该资源的 ClusterNode 就可以省去每次都从 Map 中获取的步骤,这当然也是 Sentinel 为性能做出的努力。

ClusterBuilderSlot#entry 方法的 node 参数由前一个 ProcessorSlot 传递过来,也就是 NodeSelectorSlot 传递过来的 DefaultNode。ClusterBuilderSlot 将 ClusterNode 赋值给 DefaultNode.clusterNode,那么后续的 ProcessorSlot 就能从 node 参数中取得 ClusterNode。DefaultNode 与 ClusterNode 的关系如下图所示。

ClusterNode 有一个 Map 类型的字段用来缓存 origin 与 StatisticNode 的映射,代码如下:

public class ClusterNode extends StatisticNode { private final String name; private final int resourceType; private Map<String, StatisticNode> originCountMap = new HashMap<>(); }

如果上游服务在调用当前服务的接口传递 origin 字段过来,例如可在 http 请求头添加“S-user”参数,或者 Dubbo rpc 调用在请求参数列表加上“application”参数,那么 ClusterBuilderSlot 就会为 ClusterNode 创建一个 StatisticNode,用来统计当前资源被远程服务调用的指标数据。

例如,当 origin 表示来源应用的名称时,对应的 StatisticNode 统计的就是针对该调用来源的指标数据,可用来查看哪个服务访问这个接口最频繁,由此可实现按调用来源限流。

ClusterNode#getOrCreateOriginNode 方法源码如下:

public Node getOrCreateOriginNode(String origin) { StatisticNode statisticNode = originCountMap.get(origin); if (statisticNode == null) { try { lock.lock(); statisticNode = originCountMap.get(origin); if (statisticNode == null) { statisticNode = new StatisticNode(); // 这几行代码在 Sentinel 中随处可见 HashMap<String, StatisticNode> newMap = new HashMap<>(originCountMap.size() + 1); newMap.putAll(originCountMap); newMap.put(origin, statisticNode); originCountMap = newMap; } } finally { lock.unlock(); } } return statisticNode; }

为了便于使用,ClusterBuilderSlot 会将调用来源(origin)的 StatisticNode 赋值给
Context.curEntry.originNode,后续的 ProcessorSlot 可调用 Context#getCurEntry#getOriginNode 方法获取该 StatisticNode。这里我们可以得出一个结论,如果我们自定义的 ProcessorSlot 需要用到调用来源的 StatisticNode,那么在构建 ProcessorSlotChain 时,我们必须要将这个自定义 ProcessorSlot 放在 ClusterBuilderSlot 之后。

资源指标数据统计:StatisticSlot

StatisticSlot 才是实现资源各项指标数据统计的 ProcessorSlot,它与 NodeSelectorSlot、ClusterBuilderSlot 组成了资源指标数据统计流水线,分工明确。

首先 NodeSelectorSlot 为资源创建 DefaultNode,将 DefaultNode 向下传递,ClusterBuilderSlot 负责给资源的 DefaultNode 加工,添加 ClusterNode 这个零部件,再将 DefaultNode 向下传递给 StatisticSlot,如下图所示:

StatisticSlot 在统计指标数据之前会先调用后续的 ProcessorSlot,根据后续 ProcessorSlot 判断是否需要拒绝该请求的结果决定记录哪些指标数据,这也是为什么 Sentinel 设计的责任链需要由前一个 ProcessorSlot 在 entry 或者 exit 方法中调用 fireEntry 或者 fireExit 完成调用下一个 ProcessorSlot 的 entry 或 exit 方法,而不是使用 for 循环遍历调用 ProcessorSlot 的原因。每个 ProcessorSlot 都有权决定是先等后续的 ProcessorSlot 执行完成再做自己的事情,还是先完成自己的事情再让后续 ProcessorSlot 执行,与流水线有所区别。

StatisticSlot 源码框架如下:

public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> { @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { try { // Do some checking. fireEntry(context, resourceWrapper, node, count, prioritized, args); // ..... } catch (PriorityWaitException ex) { // ..... } catch (BlockException e) { // .... throw e; } catch (Throwable e) { // ..... throw e; } } @Override public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { DefaultNode node = (DefaultNode)context.getCurNode(); // .... fireExit(context, resourceWrapper, count); } }

  • entry:先调用 fireEntry 方法完成调用后续的 ProcessorSlot#entry 方法,根据后续的 ProcessorSlot 是否抛出 BlockException 决定记录哪些指标数据,并将资源并行占用的线程数加 1。
  • exit:若无任何异常,则记录响应成功、请求执行耗时,将资源并行占用的线程数减 1。

entry 方法

第一种情况:当后续的 ProcessorSlot 未抛出任何异常时,表示不需要拒绝该请求,放行当前请求。

当请求可正常通过时,需要将当前资源并行占用的线程数增加 1、当前时间窗口被放行的请求总数加 1,代码如下:

// Request passed, add thread count and pass count. node.increaseThreadNum(); node.addPassRequest(count);

如果调用来源不为空,也将调用来源的 StatisticNode 的当前并行占用线程数加 1、当前时间窗口被放行的请求数加 1,代码如下:

if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); context.getCurEntry().getOriginNode().addPassRequest(count); }

如果流量类型为 IN,则将资源全局唯一的 ClusterNode 的并行占用线程数、当前时间窗口被放行的请求数都增加 1,代码如下:

if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); Constants.ENTRY_NODE.addPassRequest(count); }

回调所有
ProcessorSlotEntryCallback#onPass 方法,代码如下:

// Handle pass event with registered entry callback handlers. for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); }

可调用
StatisticSlotCallbackRegistry#addEntryCallback 静态方法注册
ProcessorSlotEntryCallback,
ProcessorSlotEntryCallback 接口的定义如下:

public interface ProcessorSlotEntryCallback<T> { void onPass(Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args) throws Exception; void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args); }

  • onPass:该方法在请求被放行时被回调执行。
  • onBlocked:该方法在请求被拒绝时被回调执行。

第二种情况:捕获到类型为 PriorityWaitException 的异常。

这是特殊情况,在需要对请求限流时,只有使用默认流量效果控制器才可能会抛出 PriorityWaitException 异常,这部分内容将在分析 FlowSlot 的实现源码时再作分析。

当捕获到 PriorityWaitException 异常时,说明当前请求已经被休眠了一会了,但请求还是允许通过的,只是不需要为 DefaultNode 记录这个请求的指标数据了,只自增当前资源并行占用的线程数,同时,DefaultNode 也会为 ClusterNode 自增并行占用的线程数。最后也会回调所有
ProcessorSlotEntryCallback#onPass 方法。这部分源码如下。

node.increaseThreadNum(); if (context.getCurEntry().getOriginNode() != null) { // Add count for origin node. context.getCurEntry().getOriginNode().increaseThreadNum(); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseThreadNum(); } // Handle pass event with registered entry callback handlers. for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onPass(context, resourceWrapper, node, count, args); }

第三种情况:捕获到 BlockException 异常,BlockException 异常只在需要拒绝请求时抛出。

当捕获到 BlockException 异常时,将异常记录到调用链路上下文的当前 Entry(StatisticSlot 的 exit 方法会用到),然后调用 DefaultNode#increaseBlockQps 方法记录当前请求被拒绝,将当前时间窗口的 block qps 这项指标数据的值加 1。如果调用来源不为空,让调用来源的 StatisticsNode 也记录当前请求被拒绝;如果流量类型为 IN,则让用于统计所有资源指标数据的 ClusterNode 也记录当前请求被拒绝。这部分的源码如下:

// Blocked, set block exception to current entry. context.getCurEntry().setError(e); // Add block count. node.increaseBlockQps(count); if (context.getCurEntry().getOriginNode() != null) { context.getCurEntry().getOriginNode().increaseBlockQps(count); } if (resourceWrapper.getEntryType() == EntryType.IN) { // Add count for global inbound entry node for global statistics. Constants.ENTRY_NODE.increaseBlockQps(count); } // Handle block event with registered entry callback handlers. for (ProcessorSlotEntryCallback<DefaultNode> handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) { handler.onBlocked(e, context, resourceWrapper, node, count, args); } throw e;

StatisticSlot 捕获 BlockException 异常只是为了收集被拒绝的请求,BlockException 异常还是会往上抛出。抛出异常的目的是为了拦住请求,让入口处能够执行到 catch 代码块完成请求被拒绝后的服务降级处理。

第四种情况:捕获到其它异常。

其它异常并非指业务异常,因为此时业务代码还未执行,而业务代码抛出的异常是通过调用 Tracer#trace 方法记录的。

当捕获到非 BlockException 异常时,除 PriorityWaitException 异常外,其它类型的异常都同样处理。让 DefaultNode 记录当前请求异常,将当前时间窗口的 exception qps 这项指标数据的值加 1。调用来源的 StatisticsNode、用于统计所有资源指标数据的 ClusterNode 也记录下这个异常。这部分源码如下:

// Unexpected error, set error to current entry. context.getCurEntry().setError(e); // This should not happen. node.increaseExceptionQps(count); if (context.getCurEntry().getOriginNode() != null) { context.getCurEntry().getOriginNode().increaseExceptionQps(count); } if (resourceWrapper.getEntryType() == EntryType.IN) { Constants.ENTRY_NODE.increaseExceptionQps(count); } throw e;

exit 方法

exit 方法被调用时,要么请求被拒绝,要么请求被放行并且已经执行完成,所以 exit 方法需要知道当前请求是否正常执行完成,这正是 StatisticSlot 在捕获异常时将异常记录到当前 Entry 的原因,exit 方法中通过 Context 可获取到当前 CtEntry,从当前 CtEntry 可获取 entry 方法中写入的异常。

exit 方法源码如下(有删减):

@Override public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { DefaultNode node = (DefaultNode)context.getCurNode(); if (context.getCurEntry().getError() == null) { // 计算耗时 long rt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime(); // 记录执行耗时与成功总数 node.addRtAndSuccess(rt, count); if (context.getCurEntry().getOriginNode() != null) { context.getCurEntry().getOriginNode().addRtAndSuccess(rt, count); } // 自减当前资源占用的线程数 node.decreaseThreadNum(); // origin 不为空 if (context.getCurEntry().getOriginNode() != null) { context.getCurEntry().getOriginNode().decreaseThreadNum(); } // 流量类型为 in 时 if (resourceWrapper.getEntryType() == EntryType.IN) { Constants.ENTRY_NODE.addRtAndSuccess(rt, count); Constants.ENTRY_NODE.decreaseThreadNum(); } } // Handle exit event with registered exit callback handlers. Collection<ProcessorSlotExitCallback> exitCallbacks = StatisticSlotCallbackRegistry.getExitCallbacks(); for (ProcessorSlotExitCallback handler : exitCallbacks) { handler.onExit(context, resourceWrapper, count, args); } fireExit(context, resourceWrapper, count); }

exit 方法中通过 Context 可获取当前资源的 DefaultNode,如果 entry 方法中未出现异常,那么说明请求是正常完成的,在请求正常完成情况下需要记录请求的执行耗时以及响应是否成功,可将当前时间减去调用链路上当前 Entry 的创建时间作为请求的执行耗时。

资源指标数据的记录过程

ClusterNode 才是一个资源全局的指标数据统计节点,但我们并未在 StatisticSlot#entry 方法与 exit 方法中看到其被使用。因为 ClusterNode 被 ClusterBuilderSlot 交给了 DefaultNode 掌管,在 DefaultNode 的相关指标数据收集方法被调用时,ClusterNode 的对应方法也会被调用,如下代码所示:

public class DefaultNode extends StatisticNode { ...... private ClusterNode clusterNode; @Override public void addPassRequest(int count) { super.addPassRequest(count); this.clusterNode.addPassRequest(count); } }

记录某项指标数据指的是:针对当前请求,记录当前请求的某项指标数据,例如请求被放行、请求被拒绝、请求的执行耗时等。

假设当前请求被成功处理,StatisticSlot 会调用 DefaultNode#addRtAndSuccess 方法记录请求处理成功、并且记录处理请求的耗时,DefaultNode 先调用父类的 addRtAndSuccess 方法,然后 DefaultNode 会调用 ClusterNode#addRtAndSuccess 方法。ClusterNode 与 DefaultNode 都是 StatisticNode 的子类,StatisticNode#addRtAndSuccess 方法源码如下:

@Override public void addRtAndSuccess(long rt, int successCount) { // 秒级滑动窗口 rollingCounterInSecond.addSuccess(successCount); rollingCounterInSecond.addRT(rt); // 分钟级的滑动窗口 rollingCounterInMinute.addSuccess(successCount); rollingCounterInMinute.addRT(rt); }

rollingCounterInSecond 是一个秒级的滑动窗口,rollingCounterInMinute 是一个分钟级的滑动窗口,类型为 ArrayMetric。分钟级的滑动窗口一共有 60 个 MetricBucket,每个 MetricBucket 都被 WindowWrap 包装,每个 MetricBucket 统计一秒钟内的各项指标数据,如下图所示:

当调用 rollingCounterInMinute#addSuccess 方法时,由 ArrayMetric 根据当前时间戳获取当前时间窗口的 MetricBucket,再调用 MetricBucket#addSuccess 方法将 success 这项指标的值加上方法参数传递进来的值(一般是 1)。MetricBucket 使用 LongAdder 记录各项指标数据的值。

Sentinel 在 MetricEvent 枚举类中定义了 Sentinel 会收集哪些指标数据,MetricEvent 枚举类的源码如下:

public enum MetricEvent { PASS, BLOCK, EXCEPTION, SUCCESS, RT, OCCUPIED_PASS }

  • pass 指标:请求被放行的总数
  • block:请求被拒绝的总数
  • exception:请求处理异常的总数
  • success:请求被处理成功的总数
  • rt:被处理成功的请求的总耗时
  • occupied_pass:预通过总数(前一个时间窗口使用了当前时间窗口的 passQps)

其它的指标数据都可通过以上这些指标数据计算得出,例如,平均耗时可根据总耗时除以成功总数计算得出。

资源指标数据统计总结

  • 一个调用链路上只会创建一个 Context,在调用链路的入口创建(一个调用链路上第一个被 Sentinel 保护的资源)。
  • 一个 Context 名称只创建一个 EntranceNode,也是在调用链路的入口创建,调用 Context#enter 方法时创建。
  • 与方法调用的入栈出栈一样,一个线程上调用多少次 SphU#entry 方法就会创建多少个 CtEntry,前一个 CtEntry 作为当前 CtEntry 的父节点,当前 CtEntry 作为前一个 CtEntry 的子节点,构成一个双向链表。Context.curEntry 保存的是当前的 CtEntry,在调用当前的 CtEntry#exit 方法时,由当前 CtEntry 将 Context.curEntry 还原为当前 CtEntry 的父节点 CtEntry。
  • 一个调用链路上,如果多次调用 SphU#entry 方法传入的资源名称都相同,那么只会创建一个 DefaultNode,如果资源名称不同,会为每个资源名称创建一个 DefaultNode,当前 DefaultNode 会作为调用链路上的前一个 DefaultNode 的子节点。
  • 一个资源有且只有一个 ProcessorSlotChain,一个资源有且只有一个 ClusterNode。
  • 一个 ClusterNode 负责统计一个资源的全局指标数据。
  • StatisticSlot 负责记录请求是否被放行、请求是否被拒绝、请求是否处理异常、处理请求的耗时等指标数据,在 StatisticSlot 调用 DefaultNode 用于记录某项指标数据的方法时,DefaultNode 也会调用 ClusterNode 的相对应方法,完成两份指标数据的收集。
  • DefaultNode 统计当前资源的各项指标数据的维度是同一个 Context(名称相同),而 ClusterNode 统计当前资源各项指标数据的维度是全局。

 

市场有自己的时间观念。股票投资就如同任何人类互动一样,耐心是一项美德。——罗伯特·D·爱德华《股市趋势技术分析》

引言

TA-Lib,全称“Technical Analysis Library”, 即技术分析库,是Python金融量化的高级库,涵盖了150多种股票、期货交易软件中常用的技术分析指标,如MACD、RSI、KDJ、动量指标、布林带等等。TA-Lib可分为10个子板块:Overlap Studies(重叠指标),Momentum Indicators(动量指标),Volume Indicators(交易量指标),Cycle Indicators(周期指标),Price Transform(价格变换),Volatility Indicators(波动率指标),Pattern Recognition(模式识别),Statistic Functions(统计函数),Math Transform(数学变换)和Math Operators(数学运算),见下图。本公众号将以系列的形式详细介绍talib技术指标的实现和应用,而本文作为开篇,主要介绍Overlap Studies内容。

安装与使用

安装:在cmd上使用“pip install talib”命令一般会报错,正确安装方法是,进入
https://www.lfd.uci.edu/~gohlke/pythonlibs/,下拉选择
TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl(win系统64位,python3.7版本,根据自己系统和python版本选择相应的安装包),将下载包放在当前工作路径中,然后在Anaconda Prompt(或windows的cmd)里面输入命令:pip install
TA_Lib-0.4.17-cp27-cp27m-win_amd64.whl。

使用:import talib as ta

Overlap Studies Functions重叠指标

01 移动平均线系列

移动平均线是技术分析理论中应用最普遍的指标之一,主要用于确认、跟踪和判断趋势,提示买入和卖出信号,在单边市场行情中可以较好的把握市场机会和规避风险。但是,移动平均线一般要与其他的技术指标或基本面相结合来使用,特别是当市场处于盘整行情时,其买入卖出信号会频繁出现,容易失真。

通用函数名:MA

代码:ta.MA(close,timeperiod=30,matype=0)

移动平均线系列指标包括:SMA简单移动平均线、EMA指数移动平均线、WMA加权移动平均线、DEMA双移动平均线、TEMA三重指数移动平均线、TRIMA三角移动平均线、KAMA考夫曼自适应移动平均线、MAMA为MESA自适应移动平均线、T3三重指数移动平均线。

其中,close为收盘价,时间序列,timeperiod为时间短,默认30天,指标类型matype分别对应:0=SMA, 1=EMA, 2=WMA, 3=DEMA, 4=TEMA, 5=TRIMA, 6=KAMA, 7=MAMA, 8=T3 (Default=SMA)

不同类型的移动均线也有各自相应的调用函数:

#先引入后面可能用到的包(package) import pandas as pd import numpy as np import matplotlib.pyplot as plt %matplotlib inline #正常显示画图时出现的中文和负号 from pylab import mpl mpl.rcParams['font.sans-serif']=['SimHei'] mpl.rcParams['axes.unicode_minus']=False #引入TA-Lib库 import talib as ta #查看包含的技术指标和数学运算函数 #print(ta.get_functions()) #print(ta.get_function_groups()) ta_fun=ta.get_function_groups() ta_fun.keys() #使用tushare获取上证指数数据作为示例 import tushare as ts df=ts.get_k_data('sh',start='2000-01-01') df.index=pd.to_datetime(df.date) df=df.sort_index() types=['SMA','EMA','WMA','DEMA','TEMA', 'TRIMA','KAMA','MAMA','T3'] df_ma=pd.DataFrame(df.close) for i in range(len(types)): df_ma[types[i]]=ta.MA(df.close,timeperiod=5,matype=i) df_ma.tail()

df_ma.loc['2018-08-01':].plot(figsize=(16,6)) ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') plt.title('上证指数各种类型移动平均线',fontsize=15) plt.xlabel('') plt.show()

 

数据来源:tushare

 

#画5、30、120、250指数移动平均线 N=[5,30,120,250] for i in N: df['ma_'+str(i)]=ta.EMA(df.close,timeperiod=i) df.tail() df.loc['2014-01-01':,['close','ma_5','ma_30','ma_120','ma_250']].plot(figsize=(16,6)) ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') plt.title('上证指数走势',fontsize=15) plt.xlabel('') plt.show()

 

数据来源:tushare

 

02 布林带

布林带(Bollinger Band),由压力线、支撑线价格平均线组成,一般情况价格线在压力线和支撑线组成的上下区间中游走,区间位置会随着价格的变化而自动调整。布林线的理论使用原则是:当股价穿越最外面的压力线(支撑线)时,表示卖点(买点)出现。当股价延着压力线(支撑线)上升(下降)运行,虽然股价并未穿越,但若回头突破第二条线即是卖点或买点。在实际应用中,布林线有其滞后性,相对于其他技术指标在判断行情反转时参考价值较低,但在判断盘整行情终结节点上成功率较高。

计算方法:首先计出过去 N 日收巿价的标准差 SD(Standard Deviation) ,通常再乘 2 得出 2 倍标准差, Up 线为 N日平均线加 2 倍标准差, Down 线则为 N日平均线减 2 倍标准差。

代码:ta.BBANDS(close, timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)

H_line,M_line,L_line=ta.BBANDS(df.close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) df1=pd.DataFrame(df.close,index=df.index,columns=['close']) df1['H_line']=H_line df1['M_line']=M_line df1['L_line']=L_line df1.tail()

df1.loc['2013-01-01':'2014-12-30'].plot(figsize=(16,6)) ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') plt.title('上证指数布林线',fontsize=15) plt.xlabel('') plt.show()

 

数据来源:tushare

 

03 其他指标

df2=pd.DataFrame(df.close) df2['HT']=ta.HT_TRENDLINE(df.close) periods =np.array([3]*len(df), dtype=float) df2['MAVP']=ta. MAVP(df.close,periods) df2['MIDPOINT']=ta.MIDPOINT(df.close) df2['MIDPRICE']=ta.MIDPRICE(df.high,df.low) df2['SAR']=ta.SAR(df.high,df.low) df2['SAREXT']=ta.SAREXT(df.high,df.low) df2.tail()

df2.loc['2018-01-01':'2019-02-21',['close','HT','MAVP','MIDPOINT','MIDPRICE','SAR']].plot(figsize=(16,6)) ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') plt.title('上证指数的其他趋势指标线',fontsize=15) plt.xlabel('') plt.show()

 

数据来源:tushare

 

df2.loc['2018-01-01':'2019-02-21','SAREXT'].plot(figsize=(16,6)) ax = plt.gca() ax.spines['right'].set_color('none') ax.spines['top'].set_color('none') plt.title('上证指数的抛物线扩展走势',fontsize=15) plt.xlabel('') plt.show()

 

数据来源:tushare

 

 

均线策略回测

双均线策略:分别选择L天和S天的移动平均线(L>S),如L=20,S=5,当短周期S均线(5日均线)向上突破长周期L均线(20日均线)时,为买入点;反之,当S均线向下击穿L均线时为卖出点。

标的:中国平安(601318),期间:2014.1-2019.1,回测结果:

 

数据来源:万矿

 

布林线策略回测

标的:黄金期货

策略:下穿布林线上边界做空,上穿布林线下边界做多

(1)timeperiod=10,回测期间:2017.01.01-2019.02.21

 

数据来源:万矿

 

 

(2)timeperiod=10,回测期间:2016.01.01-2017.02.21

 

数据来源:万矿

 

关于Python金融量化

专注于分享Python在金融领域的实际应用,包括金融数据分析、金融建模与量化投资等。加入知识星球,免费获取量化投资学习视频、公众号文章Python源代码,一起学习,共同进步!

 

一些常用的vba代码合集,方便检索引用

模块1:生成workbook下的目录

Attribute VB_Name = "Basic" Option Explicit Sub Generate_Content_General() Application.ScreenUpdating = False '第一部分:声明基础变量 Dim sht As Worksheet Dim sht_content As Worksheet Dim wk As Workbook Set wk = ThisWorkbook Set sht_content = wk.Sheets("目录") With sht_content.Cells(2, 2) .Value = "目录" .Offset(0, 1) = "超链接" End With '第二部分:超链接 Dim i, j, k Dim zstr, ystr, xstr j = 2 i = 2 Do While i < wk.Sheets.Count Set sht = wk.Sheets(i) If sht.Name <> "目录" And sht.Visible = -1 Then With sht_content.Cells(j + 1, 2) .Value = sht.Name sht_content.Hyperlinks.Add .Offset(0, 1), Address:="", SubAddress:="'" & sht.Name & "'!a1", TextToDisplay:="点击链接表" '逆向链接过程 j = j + 1 End With End If i = i + 1 Loop With sht_content.Range("b:c") .Columns.AutoFit .Font.Size = 12 End With Application.ScreenUpdating = True End Sub

模块2:移动目录到第一个位置

Sub move_sheet_index() Dim wb As Workbook Dim sht As Worksheet Dim dht As Worksheet Dim i Dim sheet_name Dim index Set wb = ThisWorkbook Set sht = wb.Sheets("目录") For i = 2 To 38 sheet_name = sht.Cells(i, 2) index = sht.Cells(i, 7) wb.Sheets(sheet_name).Move After:=Sheets(i - 1) Next End Sub

模块3:更新目录

Sub Update_Content() Application.ScreenUpdating = False Dim wk As Workbook Dim sht_content As Worksheet Set wk = ThisWorkbook Set sht_content = wk.Sheets("目录") sht_content.Range("b:c").ClearContents Call Generate_Content_General Application.ScreenUpdating = True End Sub

模块4:取消隐藏单元格

Sub Cancel_Hidden() Dim sht As Worksheet For Each sht In Sheets sht.Visible = xlSheetVisible Next End Sub

模块5:删除workbook下的代码模块

Sub 删除代码() '这个程序要在标准的Moudle模块中 Dim i, icon Dim vbc As Object Dim wk As Workbook Dim sht As Worksheet Dim arr Set wk = ThisWorkbook Set sht = wk.Sheets("Draft") icon = wk.VBProject.VBComponents.Count ReDim arr(1 To icon, 2) For i = 1 To icon If i > icon Then Exit For Set vbc = wk.VBProject.VBComponents(i) ' arr(i, 0) = i ' arr(i, 1) = vbc.Name ' arr(i, 2) = vbc.Type If vbc.Type = 1 And vbc.Name <> "Delete_Model" And vbc.Name <> "Func" Then With Application.VBE.ActiveVBProject.VBComponents .Remove .Item(vbc.Name) '删除模块、类模块、窗体 End With i = i - 1 icon = icon - 1 End If Next 'sht.[a1].Resize(UBound(arr, 1), UBound(arr, 2) + 1) = arr End Sub

模块6:vba中用sql模块

Function exe_sql(ds, sql As String) Dim conn As Object Dim spath$ Dim i As Integer, j, k%, t As Integer, Trow%, Tcolumn% Dim columns, data Dim rst As Object Set conn = CreateObject("adodb.connection") Set rst = CreateObject("adodb.recordset") conn.Open "provider=microsoft.ace.oledb.12.0;extended properties='excel 12.0;imex=1';data source= " & ds If sql = "" Then MsgBox "请输入SQL语句" Exit Function Else rst.Open sql, conn, 3 i = rst.Fields.Count ReDim columns(1 To i) ' 记录获取的列名 For k = 1 To i columns(k) = rst.Fields(k - 1).Name Next If rst.RecordCount > 0 Then j = rst.RecordCount ReDim data(1 To j, 1 To i) t = 1 Do While rst.EOF = False For k = 1 To i If Not IsNull(rst.Fields(k - 1)) Then data(t, k) = rst.Fields(k - 1).Value End If Next rst.movenext t = t + 1 Loop End If exe_sql = Array(columns, data) End Function

模块7:通用的一些function

Function Extract(sql As String, f As String) '#@@ 拽数,并返回数组 Dim cnn As Object, rst As Object Dim r_arr, arr Dim i, j '#@@@@# 大前提 On Error GoTo Err_Handle If sql = "" Then Extract = 0: Exit Function '#@@@@# 正常执行 Set cnn = CreateObject("adodb.connection") Set rst = CreateObject("adodb.recordset") ' cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties='excel 12.0;HDR=YES';data source=" & f cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties=excel 12.0;data source=" & f ' cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties='excel 12.0;imex=1';data source= " & f '# imex=1 数据导入模式 'rst = cnn.Execute(sql) | rng.copyfromrecordset rst | rst.fields.count | rst.recordcount rst.Open sql, cnn, 3 i = rst.RecordCount If i <> "" And i >= 1 Then arr = rst.getrows(): rst.movefirst If Not IsArray(arr) Then Extract = Array("无记录"): Exit Function ReDim r_arr(UBound(arr, 2) + 1, UBound(arr, 1)) i = rst.Fields.Count '#@@@@# 这里属于标题部分 For j = 1 To i r_arr(0, j - 1) = rst.Fields(j - 1).Name Next rst.movefirst rst.Close: cnn.Close Set rst = Nothing: Set cnn = Nothing '#@@@@# 二维转换 For j = 0 To UBound(arr, 2) For i = 0 To UBound(arr) r_arr(j + 1, i) = arr(i, j) Next Next Extract = r_arr 'Debug.Print "Over" Exit Function '#@@@@# 错误提醒,on error resume next,on error goto err_handle,on error goto line,on error goto 0 Err_Handle: Extract = Err.Description End Function Function Extract_Origin(sql As String, f As String) '#@@ 拽数,并返回数组 Dim cnn As Object, rst As Object Dim r_arr, arr Dim i, j '#@@@@# 大前提 On Error GoTo Err_Handle If sql = "" Then Extract_Origin = 0: Exit Function '#@@@@# 正常执行 Set cnn = CreateObject("adodb.connection") Set rst = CreateObject("adodb.recordset") ' cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties='excel 12.0;HDR=YES';data source=" & f cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties=excel 12.0;data source=" & f ' cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties='excel 12.0;imex=1';data source= " & f '# imex=1 数据导入模式 'rst = cnn.Execute(sql) | rng.copyfromrecordset rst | rst.fields.count | rst.recordcount rst.Open sql, cnn, 3 If rst.RecordCount > 0 Then arr = rst.getrows ReDim r_arr(UBound(arr, 2), UBound(arr, 1)) For j = 0 To UBound(arr, 2) For i = 0 To UBound(arr) r_arr(j, i) = arr(i, j) Next Next Else r_arr = 0 End If Extract_Origin = r_arr rst.Close cnn.Close Set rst = Nothing Set cnn = Nothing 'Debug.Print "Over" Exit Function '#@@@@# 错误提醒,on error resume next,on error goto err_handle,on error goto line,on error goto 0 Err_Handle: Extract_Origin = Err.Description End Function Function CheckWkOpen(ByVal f) Dim tk As Workbook Dim status status = 0 For Each tk In Workbooks If StrComp(f, "book1.xls", 1) = 0 Then MsgBox f & " is open" Application.Windows(f).Visible = True Workbooks(f).Close False status = 1 End If Next End Function Function CheckFile(spath) Dim fso As Object Set fso = CreateObject("scripting.filesystemobject") CheckExists = fso.fileexists(spath) End Function Function CheckTable(wk As Workbook, zstr As String) Dim sht As Worksheet Dim status For Each sht In wk.Sheets If sht.Name = zstr Then status = 1 Exit For Else status = 0 End If Next CheckTable = status End Function Sub tt() ActiveWorkbook.RemovePersonalInformation = False End Sub Function 拽数(sql As String, f As String) '@@拽数,并返回数组 Dim cnn As Object, rst As Object Dim r_arr, arr Dim i, j Set cnn = CreateObject("adodb.connection") Set rst = CreateObject("adodb.recordset") cnn.Open "provider=microsoft.ace.oledb.12.0;extended properties=excel 12.0;data source= " & f On Error GoTo Err_Handle rst.Open sql, cnn, 3 i = rst.RecordCount If i <> "" And i >= 1 Then arr = rst.getrows(): rst.movefirst ReDim r_arr(UBound(arr, 2) + 1, UBound(arr, 1)) i = rst.Fields.Count For j = 1 To i r_arr(0, j - 1) = rst.Fields(j - 1).Name Next rst.movefirst rst.Close cnn.Close Set rst = Nothing Set cnn = Nothing For j = 0 To UBound(arr, 2) For i = 0 To UBound(arr) r_arr(j + 1, i) = arr(i, j) Next Next 拽数 = r_arr Set rst = Nothing Set cnn = Nothing Exit Function Err_Handle: Debug.Print Err.Description End Function

模块8:vba自动生成图表

Attribute VB_Name = "Generate_Chart" Option Explicit '=======================================下面为VBA自动生成部分======================================= Sub Chart_Initial(C_row As Integer, C_column As Integer, ChartName As String, C_width As Integer, C_height) 'C_row,C_Column 存放行列位置,ChartName 存放表,C_width C_height 存放大小 Dim XTitle, YTitle Dim Crng As Range, Xrng As Range, rng As Range Dim sht As Worksheet, wb1 As Workbook Dim MyChart As ChartObject Dim R1, C, zstr Set wb1 = ThisWorkbook Set sht = wb1.Sheets("ChartData") R1 = sht.ChartObjects.Count If R1 > 0 Then For Each C In sht.ChartObjects zstr = C.Name If zstr = ChartName Then C.Delete Next End If '第一部分:创建一个新的图表Object事件 Set rng = sht.Cells(C_row, C_column) Set MyChart = sht.ChartObjects.Add(rng.Left, rng.Offset(1, 0).Top, rng.Width * C_width, rng.Height * C_height) With MyChart .Name = ChartName End With '第二部分:设置图表区格式 With MyChart.chart.ChartArea .Font.Name = "宋体" .Font.Size = 8 .Font.ColorIndex = xlAutomatic .Border.LineStyle = 0 .Interior.ColorIndex = xlAutomatic '图表区填充 End With '第三部分:设置绘图区格式 With MyChart.chart.PlotArea .Border.ColorIndex = 15 .Border.Weight = xlThin ' .Border.LineStyle = xlDot .Border.LineStyle = xlDot .Interior.ColorIndex = xlNone '绘图区填充 End With '第五部分:设置图表标题 MyChart.chart.HasTitle = True With MyChart.chart.ChartTitle .Text = "<p>string</p>" .Font.Name = "宋体" .Font.Bold = True .Font.Size = 9 .Top = 0 End With End Sub Sub Chart_FillData(MyChart As ChartObject, SerieName As String, Xrng As Range, Yrng As Range) With MyChart.chart Dim ns Set ns = .SeriesCollection.NewSeries ns.Values = Xrng If Not Yrng Is Nothing Then ns.XValues = Yrng ns.Name = SerieName End With End Sub Sub Chart_FinalStyle(MyChart As ChartObject) With MyChart.chart ' .ChartTitle.Left = (myChart.Chart.ChartArea.Width / 2) - (myChart.Chart.ChartTitle.Width / 2) End With End Sub Sub Chart_Axes(MyChart As ChartObject) MyChart.chart.Axes(xlValue).HasMajorGridlines = True With MyChart.chart.Axes(xlValue).MajorGridlines.Border .ColorIndex = 15 .Weight = xlHairline .LineStyle = xlDot End With End Sub Sub Chart_SeriesPoint(MyChart As ChartObject, S1) Dim ms As SeriesCollection MyChart.Activate ActiveChart.SeriesCollection(1).Points(S1).Select With Selection.Format.Fill .Visible = msoTrue .ForeColor.ObjectThemeColor = msoThemeColorAccent2 .ForeColor.TintAndShade = 0 ' .ForeColor.Brightness = 0 '透明度设置 0.400000006=40% .Transparency = 0 .Solid End With End Sub Sub Chart_Transmit(ChartName As String, Gsht As Worksheet) Dim C As ChartObject Set C = Gsht.ChartObjects(ChartName) With Gsht.Shapes(ChartName) .Fill.ForeColor.RGB = RGB(63, 74, 92) ' .TextFrame2.TextRange.Font.Fill.ForeColor.RGB = RGB(255, 255, 255) ' .Line.ForeColor.RGB = RGB(255, 0, 0) ' .Line.ForeColor.ObjectThemeColor = msoThemeColorBackground1 End With With C.chart.ChartArea .Font.ColorIndex = 2 .Border.ColorIndex = 2 End With C.CopyPicture Appearance:=xlPrinter, Format:=xlPicture ' C.Chart.Export C.Name & ".JPG" '导出到文件路径文件夹 End Sub Sub ChartToPicture(ChartName As String, Gsht As Worksheet, Grng As Range) Dim C As ChartObject Gsht.Select Set C = Gsht.ChartObjects(ChartName) C.Copy Grng.Select Gsht.PasteSpecial Format:="图片(JPEG)" Call ShapeCheck("P" & ChartName, Gsht) Selection.Name = "P" & ChartName C.Delete End Sub Sub ChartCheck(ChartName As String, Gsht As Worksheet) Dim R1, zstr Dim C As ChartObject R1 = Gsht.ChartObjects.Count If R1 > 0 Then For Each C In Gsht.ChartObjects zstr = C.Name If zstr = ChartName Then C.Delete Next End If End Sub Sub ShapeCheck(ShapeName As String, Gsht As Worksheet) Dim R1, zstr Dim s As Shape R1 = Gsht.Shapes.Count If R1 > 0 Then For Each s In Gsht.Shapes zstr = s.Name If zstr = ShapeName Then s.Delete Next End If End Sub 'Sub Chart_XY_Axes() '第六部分:设置X\Y轴 'myChart.Chart.Axes(xlCategory, xlPrimary).HasTitle = True 'XlCategory是X轴 'mychart.Chart.Axes(xlCategory, xlPrimary).AxisTitle.Text = "X轴标题" 'With myChart.Chart.Axes(xlCategory, xlPrimary) ' .CrossesAt = 0 ' .TickLabelSpacing = 1 ' .TickMarkSpacing = 1 ' .AxisBetweenCategories = True ' .ReversePlotOrder = False 'End With 'myChart.Chart.Axes(xlValue, xlPrimary).HasTitle = True 'xlValue是Y轴 'myChart.Chart.Axes(xlValue, xlPrimary).AxisTitle.Text = "项目数" ' 'myChart.Chart.SetElement (msoElementPrimaryValueAxisTitleHorizontal) 'With myChart.Chart.Axes(xlValue, xlPrimary) ' .MinimumScale = 0 '最小值 ' .MaximumScale = 10 '最大值 ' .MajorUnit = 2 '主要间距 ' .MinorUnit = xlAutomatic '次要间距 ' .CrossesAt = 0 '坐标轴的交叉点 ' .ReversePlotOrder = False ' .ScaleType = xlLinear 'End With '第八部分:调整对比point的颜色 'Dim ms As SeriesCollection 'Set ms = myChart.Chart.SeriesCollection(1).points(1) 'End Sub

模块9:实现自动分级分组

Option Explicit Sub group_by() Application.ScreenUpdating = False Dim sh_0 As Worksheet Dim sh_1 As Worksheet Call loading_data Set sh_0 = ThisWorkbook.Sheets("res") Set sh_1 = ThisWorkbook.Sheets("structure") With sh_1 With .Cells .Clear .Font.Size = 9 .VerticalAlignment = xlCenter .RowHeight = 16.25 End With .Select With .Rows(1) .Font.Bold = True .RowHeight = 22.75 End With sh_0.Range("a:e").Copy .Range("a1").PasteSpecial (xlPasteValues) End With Call melt Call group Application.ScreenUpdating = True End Sub Sub loading_data() Dim sql$ Dim spath$ Dim arr Dim sht As Worksheet Set sht = ThisWorkbook.Sheets("res") spath = ThisWorkbook.FullName sql = "select tb_sort,表名,业务,按业务分类,指标数 from(" sql = sql + "Select tb_sort,表名,业务,按业务分类,count(1) as 指标数 ,b_sort,bc_sort from [indicator $] " sql = sql + "group by tb_sort,表名,业务,按业务分类,b_sort,bc_sort " sql = sql + "order by tb_sort ,b_sort,bc_sort) " arr = Extract(sql, spath) With sht .Cells.Clear .Range("A1").Resize(UBound(arr, 1) + 1, UBound(arr, 2) + 1) = arr End With End Sub Sub melt() Dim nr, nc Dim sh As Worksheet Set sh = ThisWorkbook.Sheets("structure") nc = sh.UsedRange.Columns.Count sh.Cells.ClearOutline sh.Range("a1:e1").Interior.Color = RGB(255, 217, 102) Dim i, j, k Dim ini_str, tmp_str Dim tmp_c, tmp_end Dim tmp_array tmp_array = Array(1, 3) ' tmp_array = Array(4) j = LBound(tmp_array) Do While j <= UBound(tmp_array) tmp_c = tmp_array(j) i = 2 Select Case tmp_c Case Is < 3: nr = sh.UsedRange.Rows.Count Do While i <= nr If i = 2 Then ini_str = sh.Cells(i, tmp_c) With sh.Rows(i + 1) .Insert Shift:=xlDown sh.Cells(i + 1, tmp_c + 2) = sh.Cells(i, tmp_c + 2) sh.Cells(i + 1, tmp_c + 3) = sh.Cells(i, tmp_c + 3) sh.Cells(i + 1, tmp_c + 4) = sh.Cells(i, tmp_c + 4) sh.Range(Cells(i, tmp_c + 2), Cells(i, tmp_c + 4)).Clear End With nr = nr + 1 i = i + 1 Else tmp_str = sh.Cells(i, tmp_c) If tmp_str = ini_str Then sh.Range(Cells(i, tmp_c), Cells(i, tmp_c + 1)).Clear Else ini_str = tmp_str With sh.Rows(i + 1) .Insert Shift:=xlDown sh.Cells(i + 1, tmp_c + 2) = sh.Cells(i, tmp_c + 2) sh.Cells(i + 1, tmp_c + 3) = sh.Cells(i, tmp_c + 3) sh.Cells(i + 1, tmp_c + 4) = sh.Cells(i, tmp_c + 4) sh.Range(Cells(i, tmp_c + 2), Cells(i, tmp_c + 4)).Clear End With nr = nr + 1 i = i + 1 End If End If i = i + 1 Loop Case Else: nr = sh.UsedRange.Rows.Count For k = 2 To nr If sh.Cells(k, tmp_c - 1) <> "" Then i = k + 1 With sh.Cells(i, tmp_c) ini_str = .Value If .Offset(1, 0) = "" Then tmp_end = i Else tmp_end = .End(xlDown).Row End If End With Do While i <= tmp_end tmp_str = sh.Cells(i, tmp_c) If tmp_str = ini_str And i = k + 1 Then With sh.Rows(i + 1) .Insert Shift:=xlDown sh.Cells(i + 1, tmp_c + 1) = sh.Cells(i, tmp_c + 1) sh.Cells(i + 1, tmp_c + 2) = sh.Cells(i, tmp_c + 2) sh.Range(Cells(i, tmp_c + 1), Cells(i, tmp_c + 2)).Clear End With i = i + 1 nr = nr + 1 tmp_end = tmp_end + 1 Else If tmp_str = ini_str Then sh.Cells(i, tmp_c).Clear Else If tmp_str <> "" Then ini_str = tmp_str With sh.Rows(i + 1) .Insert Shift:=xlDown sh.Cells(i + 1, tmp_c + 1) = sh.Cells(i, tmp_c + 1) sh.Cells(i + 1, tmp_c + 2) = sh.Cells(i, tmp_c + 2) sh.Range(Cells(i, tmp_c + 1), Cells(i, tmp_c + 2)).Clear End With nr = nr + 1 i = i + 1 tmp_end = tmp_end + 1 End If End If End If i = i + 1 Loop k = i - 1 End If Next End Select j = j + 1 Loop End Sub Sub group() Dim sht As Worksheet Dim row_start%, row_end% Dim target_column Set sht = Sheets("structure") row_start = 2 target_column = "D" ' row_end = sht.Cells(1048576, target_column).End(xlUp).Row + 1 row_end = sht.UsedRange.Rows.Count sht.Cells.ClearOutline Dim i Dim refer_row% i = row_start refer_row = row_start Do While i <= row_end If Cells(i, 1) <> "" Then With Range(Cells(i, 1), Cells(i, 5)) .Interior.Color = RGB(208, 206, 206) .Font.Color = RGB(0, 0, 0) .Font.Bold = True With .Borders(xlEdgeTop) .LineStyle = xlDash .Color = RGB(166, 166, 166) End With With .Borders(xlEdgeBottom) .LineStyle = xlDash .Color = RGB(166, 166, 166) End With End With End If If Cells(i, 3) <> "" Then With Range(Cells(i, 3), Cells(i, 5)) .Interior.Color = RGB(255, 242, 204) .Font.Color = RGB(0, 0, 0) .Font.Bold = True With .Borders(xlEdgeTop) .LineStyle = xlDash .Color = RGB(191, 191, 191) End With With .Borders(xlEdgeBottom) .LineStyle = xlDash .Color = RGB(191, 191, 191) End With End With End If If Cells(i, 4) <> "" Then With Range(Cells(i, 4), Cells(i, 5)) .Interior.Color = RGB(255, 242, 204) .Font.Color = RGB(0, 0, 0) .Font.Bold = True With .Borders(xlEdgeTop) .LineStyle = xlDash .Color = RGB(191, 191, 191) End With With .Borders(xlEdgeBottom) .LineStyle = xlDash .Color = RGB(191, 191, 191) End With End With End If If Cells(i, 5) <> "" Then With Range(Cells(i, 5), Cells(i, 5)) With .Borders(xlEdgeTop) .LineStyle = xlDash .Color = RGB(128, 128, 128) End With With .Borders(xlEdgeBottom) .LineStyle = xlDash .Color = RGB(128, 128, 128) End With End With End If If Cells(i, 1) = "" Then Rows(i).group i = i + 1 Loop For i = row_start To row_end If Cells(i, 2) = "" And Cells(i, 3) = "" Then Rows(i).group End If Next ' For i = row_start To row_end ' If Cells(i, 3) = "" And Cells(i, 4) = "" Then ' Rows(i).group ' End If ' Next End Sub

https://www.shideke.com

上一篇:现货交易操作技巧(现货交易技巧教学)

下一篇:贵金属现货交易入门(炒黄金现货入门)

相关推荐

返回顶部